servnest
/
maniver
Archived
2
0
Fork 0

spaces > tabs

This commit is contained in:
Miraty 2022-04-29 13:49:36 +02:00
parent 0879fd76bf
commit f19fa68d31
1 changed files with 228 additions and 228 deletions

View File

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