use std::env; use regex::Regex; use std::io::prelude::*; use std::process::{Command, Stdio, Output}; use std::fs; use users::get_current_username; use std::ffi::OsString; fn exit(error: String) { eprintln!("Error: {}", error); std::process::exit(0); } fn main() { let superuser = OsString::from("root"); match get_current_username() { Some(user) => match user { _ if user == superuser => println!("root: right user"), _ => exit("must be run as root".to_string()), } None => exit("the current user does not exist".to_string()), } let args: Vec = env::args().collect(); match args[1].as_ref() { "setup-user" => setup_user(args[2].to_string(), args[3].to_string()), "reload-nginx" => reload_nginx(), "reload-tor" => reload_tor(), "restart-gmnisrv" => restart_gmnisrv(), "le-install" => le_install(args[2].to_string()), "export-tor" => export_tor(args[2].to_string(), args[3].to_string()), _ => println!("ERROR: You must specify a subcommand"), } } 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/niver/".to_owned(); src_path += &dir.to_string(); src_path += &"/hostname".to_string().to_owned(); let mut dest_path: String = "/srv/hyper/".to_owned(); dest_path += &username.to_string(); dest_path += &"/hyper/".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!("Erreur lors d'une copie de fichier (fs::copy) : {}", why), Ok(process) => process, }; let output = Command::new("/usr/bin/chown") .arg("www-data:www-data") .arg(dest_path) .output() .expect("failed to execute process"); print_output(output); } else { println!("ERROR: The dirname must be composed only of lowercase letters"); } } else { println!("ERROR: The username must be composed only of lowercase letters"); } } fn le_install(domain: String) { let output = Command::new("/usr/bin/certbot") .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 reload_nginx() { let output = Command::new("/usr/bin/systemctl") .arg("reload") .arg("nginx") .output() .expect("Error while reloading Nginx config"); print_output(output); } fn reload_tor() { let output = Command::new("/usr/bin/systemctl") .arg("reload") .arg("tor@default") .output() .expect("Error while reloading Tor config"); print_output(output); } fn restart_gmnisrv() { let output = Command::new("/usr/bin/systemctl") .arg("restart") .arg("gmnisrv") .output() .expect("Error while restarting Gmnisrv"); 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()) { let username1 = &username; let username2 = &username; let username3 = &username; let username4 = &username; newser(username1.to_string()); pwd(username2.to_string(), password); chroot(username3.to_string()); quota(username4.to_string()); } else { println!("ERROR: The dirname must be composed only of lowercase letters"); } } else { println!("ERROR: The password must be shorter than 1024 characters"); } } else { println!("ERROR: The username must be shorter than 32 characters"); } } // 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("testfract") .arg(&username) .output() .expect("failed to execute process"); print_output(output); } // Chown /srv/hyper/username to root:root fn chroot(username: String) { if username.chars().count() < 32 { let mut path = "/srv/hyper/".to_string(); path += &username; let output = Command::new("/usr/bin/chown") .arg("root:root") .arg(&path) .output() .expect("Failed to chown /srv/hyper/ to root:root"); print_output(output); let output = Command::new("/usr/bin/chmod") .arg("755") .arg(path) .output() .expect("Failed to chmod /srv/hyper/ to 755"); print_output(output); } else { println!("Erreur : l'username doit faire moins de 32 caractères"); } } // Creates a new user in the group 'hyper', which is available only over SFTP fn newser(username: String) { let output = Command::new("/usr/sbin/useradd") .arg(&username) .arg("--create-home") .arg("--base-dir") .arg("/srv/hyper") .arg("--gid") .arg("hyper") .arg("--shell") .arg("/usr/sbin/nologin") .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(); 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"), } 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), } } 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; } } 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()); }