diff --git a/src/main.rs b/src/main.rs index 1f0fc52..b8adc29 100755 --- a/src/main.rs +++ b/src/main.rs @@ -7,315 +7,315 @@ use regex::Regex; use users::get_current_username; fn exit(error: String) { - eprintln!("error: {}", error); - std::process::exit(0); + eprintln!("error: {}", error); + std::process::exit(0); } fn main() { - let superuser = OsString::from("root"); + let superuser = OsString::from("root"); - match get_current_username() { - Some(user) => match user { - _ if user == superuser => parse_command(), - _ => exit("Must be run as root.".to_string()), - } - None => exit("The current user does not exist.".to_string()), - } + match get_current_username() { + Some(user) => match user { + _ if user == superuser => parse_command(), + _ => exit("Must be run as root.".to_string()), + } + None => exit("The current user does not exist.".to_string()), + } } fn parse_command() { - let args: Vec = env::args().collect(); + let args: Vec = env::args().collect(); - match args.get(1) { - Some(p) => match p { - _ if p == "setup-user" => setup_user(args[2].to_string(), args[3].to_string()), - _ if p == "reload-nginx" => reload_nginx(), - _ if p == "reload-tor" => reload_tor(), - _ if p == "reload-twins" => reload_twins(), - _ if p == "gemini-new-certificate" => gemini_new_certificate(args[2].to_string()), - _ if p == "le-install" => le_install(args[2].to_string()), - _ if p == "export-tor" => export_tor(args[2].to_string(), args[3].to_string()), - _ => exit("This subcommand doesn't exists.".to_string()), - } - None => exit("You must specify a subcommand.".to_string()), - } + match args.get(1) { + Some(p) => match p { + _ if p == "setup-user" => setup_user(args[2].to_string(), args[3].to_string()), + _ if p == "reload-nginx" => reload_nginx(), + _ if p == "reload-tor" => reload_tor(), + _ if p == "reload-twins" => reload_twins(), + _ if p == "gemini-new-certificate" => gemini_new_certificate(args[2].to_string()), + _ if p == "le-install" => le_install(args[2].to_string()), + _ if p == "export-tor" => export_tor(args[2].to_string(), args[3].to_string()), + _ => exit("This subcommand doesn't exists.".to_string()), + } + None => exit("You must specify a subcommand.".to_string()), + } } fn gemini_new_certificate(domain: String) { - let mut common_name: String = "/CN=".to_owned(); - common_name += &domain.to_string(); + let mut common_name: String = "/CN=".to_owned(); + common_name += &domain.to_string(); - let mut key_file: String = "/var/local/twins/tls/".to_owned(); - key_file += &domain.to_string(); - key_file += &".key".to_string().to_owned(); + let mut key_file: String = "/var/local/twins/tls/".to_owned(); + key_file += &domain.to_string(); + key_file += &".key".to_string().to_owned(); - let mut cert_file: String = "/var/local/twins/tls/".to_owned(); - cert_file += &domain.to_string(); - cert_file += &".crt".to_string().to_owned(); + let mut cert_file: String = "/var/local/twins/tls/".to_owned(); + cert_file += &domain.to_string(); + cert_file += &".crt".to_string().to_owned(); - let output = Command::new("/usr/bin/openssl") - .arg("req") - .arg("-subj") - .arg(common_name) - .arg("-new") - .arg("-newkey") - .arg("ED25519") - .arg("-days") - .arg("3650") - .arg("-nodes") - .arg("-x509") - .arg("-keyout") - .arg(&key_file) - .arg("-out") - .arg(&cert_file) - .output() - .expect("failed to execute process"); - print_output(output); + let output = Command::new("/usr/bin/openssl") + .arg("req") + .arg("-subj") + .arg(common_name) + .arg("-new") + .arg("-newkey") + .arg("ED25519") + .arg("-days") + .arg("3650") + .arg("-nodes") + .arg("-x509") + .arg("-keyout") + .arg(&key_file) + .arg("-out") + .arg(&cert_file) + .output() + .expect("failed to execute process"); + print_output(output); - let output = Command::new("/usr/bin/chmod") - .arg("400") - .arg(&key_file) - .output() - .expect("Failed to change key file mode to 400"); - print_output(output); + let output = Command::new("/usr/bin/chmod") + .arg("400") + .arg(&key_file) + .output() + .expect("Failed to change key file mode to 400"); + print_output(output); - let output = Command::new("/usr/bin/chown") - .arg("twins:twins") - .arg(key_file) - .output() - .expect("Failed to chown key file to twins:twins"); - print_output(output); + let output = Command::new("/usr/bin/chown") + .arg("twins:twins") + .arg(key_file) + .output() + .expect("Failed to chown key file to twins:twins"); + print_output(output); - let output = Command::new("/usr/bin/chmod") - .arg("400") - .arg(&cert_file) - .output() - .expect("Failed to change key file mode to 400"); - print_output(output); + let output = Command::new("/usr/bin/chmod") + .arg("400") + .arg(&cert_file) + .output() + .expect("Failed to change key file mode to 400"); + print_output(output); - let output = Command::new("/usr/bin/chown") - .arg("twins:twins") - .arg(cert_file) - .output() - .expect("Failed to chown key file to twins:twins"); - print_output(output); + let output = Command::new("/usr/bin/chown") + .arg("twins:twins") + .arg(cert_file) + .output() + .expect("Failed to chown key file to twins:twins"); + print_output(output); } fn le_install(domain: String) { - let output = Command::new("/usr/bin/certbot") - .arg("certonly") - .arg("--nginx") - // Using ECDSA - //.arg("--key-type") - //.arg("ecdsa") - //.arg("--elliptic-curve") - //.arg("secp384r1") - // Using RSA - .arg("--key-type") - .arg("rsa") - .arg("--rsa-key-size") - .arg("3072") - .arg("-d") - .arg(&domain) - .output() - .expect("failed to execute process"); - print_output(output); + let output = Command::new("/usr/bin/certbot") + .arg("certonly") + .arg("--nginx") + // Using ECDSA + //.arg("--key-type") + //.arg("ecdsa") + //.arg("--elliptic-curve") + //.arg("secp384r1") + // Using RSA + .arg("--key-type") + .arg("rsa") + .arg("--rsa-key-size") + .arg("3072") + .arg("-d") + .arg(&domain) + .output() + .expect("failed to execute process"); + print_output(output); } fn export_tor(username: String, dir: String) { - if is_string_lowercase(username.to_string()) { - if is_string_lowercase(dir.to_string()) { - let mut src_path: String = "/var/lib/tor-instances/niver/keys/".to_owned(); - src_path += &dir.to_string(); - src_path += &"/hostname".to_string().to_owned(); + if is_string_lowercase(username.to_string()) { + if is_string_lowercase(dir.to_string()) { + let mut src_path: String = "/var/lib/tor-instances/niver/keys/".to_owned(); + src_path += &dir.to_string(); + src_path += &"/hostname".to_string().to_owned(); - let mut dest_path: String = "/srv/ht/".to_owned(); - dest_path += &username.to_string(); - dest_path += &"/ht/".to_string().to_owned(); - dest_path += &dir.to_string(); - dest_path += &"/hostname".to_string().to_owned(); + let mut dest_path: String = "/srv/ht/".to_owned(); + dest_path += &username.to_string(); + dest_path += &"/ht/".to_string().to_owned(); + dest_path += &dir.to_string(); + dest_path += &"/hostname".to_string().to_owned(); - match fs::copy(src_path, &dest_path) { - Err(why) => panic!("Error while copying file (fs::copy) : {}", why), - Ok(process) => process, - }; + match fs::copy(src_path, &dest_path) { + Err(why) => panic!("Error while copying file (fs::copy) : {}", why), + Ok(process) => process, + }; - let output = Command::new("/usr/bin/chown") - .arg("php-niver:ht") - .arg(&dest_path) - .output() - .expect("failed to execute process"); + let output = Command::new("/usr/bin/chown") + .arg("php-niver:ht") + .arg(&dest_path) + .output() + .expect("failed to execute process"); - print_output(output); + print_output(output); - let output = Command::new("/usr/bin/chmod") - .arg("440") - .arg(dest_path) - .output() - .expect("failed to execute process"); + let output = Command::new("/usr/bin/chmod") + .arg("440") + .arg(dest_path) + .output() + .expect("failed to execute process"); - print_output(output); - } else { - exit("The dirname must be composed only of lowercase letters.".to_string()); - } - } else { - exit("The username must be composed only of lowercase letters.".to_string()); - } + print_output(output); + } else { + exit("The dirname must be composed only of lowercase letters.".to_string()); + } + } else { + exit("The username must be composed only of lowercase letters.".to_string()); + } } fn reload_tor() { - let output = Command::new("/usr/bin/systemctl") - .arg("reload") - .arg("tor@niver") - .output() - .expect("Error while reloading Tor config"); - print_output(output); + let output = Command::new("/usr/bin/systemctl") + .arg("reload") + .arg("tor@niver") + .output() + .expect("Error while reloading Tor config"); + print_output(output); } fn reload_nginx() { - let output = Command::new("/usr/bin/systemctl") - .arg("reload") - .arg("nginx") - .output() - .expect("Error while reloading Nginx config"); - print_output(output); + let output = Command::new("/usr/bin/systemctl") + .arg("reload") + .arg("nginx") + .output() + .expect("Error while reloading Nginx config"); + print_output(output); } fn reload_twins() { - let output = Command::new("/usr/bin/systemctl") - .arg("reload") - .arg("twins") - .output() - .expect("Error while reloading Twins"); - print_output(output); + let output = Command::new("/usr/bin/systemctl") + .arg("reload") + .arg("twins") + .output() + .expect("Error while reloading Twins"); + print_output(output); } fn setup_user(username: String, password: String) { - if username.chars().count() < 32 { - if password.chars().count() < 1024 { - if is_string_lowercase(username.to_string()) { + if username.chars().count() < 32 { + if password.chars().count() < 1024 { + if is_string_lowercase(username.to_string()) { - let username1 = &username; - let username2 = &username; - let username3 = &username; - let username4 = &username; - newser(username1.to_string()); - pwd(username2.to_string(), password); - chown_root(username3.to_string()); - quota(username4.to_string()); + let username1 = &username; + let username2 = &username; + let username3 = &username; + let username4 = &username; + newser(username1.to_string()); + pwd(username2.to_string(), password); + chown_root(username3.to_string()); + quota(username4.to_string()); - } else { - exit("The username must be composed only of lowercase letters.".to_string()); - } - } else { - exit("The password must be shorter than 1024 characters.".to_string()); - } - } else { - exit("The username must be shorter than 32 characters.".to_string()); - } + } else { + exit("The username must be composed only of lowercase letters.".to_string()); + } + } else { + exit("The password must be shorter than 1024 characters.".to_string()); + } + } else { + exit("The username must be shorter than 32 characters.".to_string()); + } } // Creates a new user in the group 'ht', which is available only over SFTP fn newser(username: String) { - let output = Command::new("/usr/sbin/useradd") - .arg(&username) - .arg("--create-home") - .arg("--skel") - .arg("/usr/local/share/niver/skel") - .arg("--base-dir") - .arg("/srv/ht") - .arg("--gid") - .arg("ht") - .arg("--shell") - .arg("/usr/sbin/nologin") + let output = Command::new("/usr/sbin/useradd") + .arg(&username) + .arg("--create-home") + .arg("--skel") + .arg("/usr/local/share/niver/skel") + .arg("--base-dir") + .arg("/srv/ht") + .arg("--gid") + .arg("ht") + .arg("--shell") + .arg("/usr/sbin/nologin") - .output() - .expect("failed to execute process"); - print_output(output); + .output() + .expect("failed to execute process"); + print_output(output); } // Changes password of the newly created user fn pwd(username: String, password: String) { - // line must be in the form username:password - let mut line: String = username.to_string(); - line += &":".to_string().to_owned(); - line += &password.to_owned(); + // line must be in the form username:password + let mut line: String = username.to_string(); + line += &":".to_string().to_owned(); + line += &password.to_owned(); - let process = match Command::new("/usr/sbin/chpasswd") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() { - Err(why) => panic!("couldn't spawn chpasswd: {}", why), - Ok(process) => process, - }; + let process = match Command::new("/usr/sbin/chpasswd") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() { + Err(why) => panic!("couldn't spawn chpasswd: {}", why), + Ok(process) => process, + }; - match process.stdin.unwrap().write_all(line.as_bytes()) { - Err(why) => panic!("couldn't write to chpasswd stdin: {}", why), - Ok(_) => println!("sent username:password to chpasswd"), - } + match process.stdin.unwrap().write_all(line.as_bytes()) { + Err(why) => panic!("couldn't write to chpasswd stdin: {}", why), + Ok(_) => println!("sent username:password to chpasswd"), + } - let mut s = String::new(); - match process.stdout.unwrap().read_to_string(&mut s) { - Err(why) => panic!("couldn't read chpasswd stdout: {}", why), - Ok(_) => print!("chpasswd responded with:\n{}", s), - } + let mut s = String::new(); + match process.stdout.unwrap().read_to_string(&mut s) { + Err(why) => panic!("couldn't read chpasswd stdout: {}", why), + Ok(_) => print!("chpasswd responded with:\n{}", s), + } } // Chown /srv/ht/username to root:root fn chown_root(username: String) { - let mut path = "/srv/ht/".to_string(); + let mut path = "/srv/ht/".to_string(); - path += &username; + path += &username; - let output = Command::new("/usr/bin/chown") - .arg("root:root") - .arg(&path) - .output() - .expect("Failed to chown /srv/ht/ to root:root"); - print_output(output); + let output = Command::new("/usr/bin/chown") + .arg("root:root") + .arg(&path) + .output() + .expect("Failed to chown /srv/ht/ to root:root"); + print_output(output); - let output = Command::new("/usr/bin/chmod") - .arg("755") - .arg(path) - .output() - .expect("Failed to chmod /srv/ht/ to 755"); - print_output(output); + let output = Command::new("/usr/bin/chmod") + .arg("755") + .arg(path) + .output() + .expect("Failed to chmod /srv/ht/ to 755"); + print_output(output); } // Set disk usage limit to the user by copying another user quota fn quota(username: String) { - let output = Command::new("/usr/sbin/edquota") - .arg("-p") - .arg("niver-quota") - .arg(&username) - .output() - .expect("failed to execute process"); - print_output(output); + let output = Command::new("/usr/sbin/edquota") + .arg("-p") + .arg("niver-quota") + .arg(&username) + .output() + .expect("failed to execute process"); + print_output(output); } fn is_string_lowercase(stri: String) -> bool { - let re = Regex::new("^[[:lower:]]+$").unwrap(); - let matching = re.is_match(&stri); - if matching { - return true; - } else { - return false; - } + let re = Regex::new("^[[:lower:]]+$").unwrap(); + let matching = re.is_match(&stri); + if matching { + return true; + } else { + return false; + } } fn print_output(output: Output) { - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - assert!(output.status.success()); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + assert!(output.status.success()); }