~nickbp/originz

ref: d94f181c5fbfdf0f62b40ea46d3f301f4ec681e5 originz/src/main.rs -rw-r--r-- 3.3 KiB
d94f181cNick Parker Backport current benchmark to older code 1 year, 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#![deny(warnings)]

use std::env;
use std::ffi::CString;

use anyhow::{bail, Context, Result};
use git_testament::{git_testament, render_testament};
use libc;
use nix::unistd;
use tokio::runtime::Runtime;
use tracing::{self, debug, info};

use kapiti::{config, logging};
use kapiti::runner::Runner;

git_testament!(TESTAMENT);

fn print_syntax() {
    println!(
        "Kapiti DNS ({})

Syntax: {} </path/to/config.toml>

Environment variables:
  LOG_LEVEL=error/warn/info/debug/trace/off",
        render_testament!(TESTAMENT),
        env::args().nth(0).unwrap()
    );
}

fn main() -> Result<()> {
    logging::init_logging();

    if env::args().len() <= 1 {
        print_syntax();
        bail!("Missing required TOML config path argument");
    }

    let config_path = env::args().nth(1).unwrap();
    if config_path.starts_with('-') {
        // Probably a commandline argument like '-h'/'--help', avoid parsing as a path
        print_syntax();
        bail!("Unrecognized TOML config path argument: {}", config_path);
    }

    // Got past logging/args, print version message before getting into config parsing
    info!("Kapiti DNS version {}", render_testament!(TESTAMENT));

    let config = config::parse_config_file(&config_path)
        .with_context(|| format!("Failed to parse TOML config: {}", config_path))?;
    debug!("config: {:?}", config);

    // Get downgrade user before passing ownership of config
    let downgrade_user_orig = config.user.clone();
    let downgrade_user = downgrade_user_orig.trim();

    let mut runtime = Runtime::new()?;

    // Needs to run async since it sets up the sockets internally
    let mut runner = runtime.block_on(Runner::new(config_path, config))?;

    // If currently root, downgrade to specified non-root user after Runner has set up any sockets
    if !downgrade_user.is_empty() && unistd::geteuid().is_root() {
        chuser(downgrade_user)
            .with_context(|| format!("Failed to change to user: {}", downgrade_user))?;
        info!("Changed to user: {}", downgrade_user);
    }

    // Manually start the service within a runtime.
    runtime.block_on(runner.run())
}

/// Downgrades the user running the process to the provided username
/// This allows binding to port 53 as root, then running the daemon as a user
fn chuser(username: &str) -> Result<()> {
    let mut result = std::ptr::null_mut::<libc::passwd>();
    let ret = unsafe {
        let mut pwd = std::mem::zeroed::<libc::passwd>();
        let mut buf = vec![0; 4096];
        libc::getpwnam_r(
            CString::new(username.as_bytes())?.as_ptr(),
            &mut pwd,
            buf.as_mut_ptr(),
            buf.len(),
            &mut result,
        )
    };
    if ret != 0 || result.is_null() {
        bail!("User not found: {:?}", username);
    }

    let uid: u32;
    let gid: u32;
    unsafe {
        uid = (*result).pw_uid;
        gid = (*result).pw_gid;
    }

    if unsafe { libc::setgroups(1, &gid) } != 0 {
        bail!("Unable to revoke supplementary groups");
    }
    unistd::setgid(unistd::Gid::from_raw(gid))
        .with_context(|| format!("Unable to set process gid to {}/{}", username, gid))?;
    unistd::setuid(unistd::Uid::from_raw(uid))
        .with_context(|| format!("Unable to set process uid to {}/{}", username, uid))?;

    Ok(())
}