spaces > tabs
This commit is contained in:
parent
0879fd76bf
commit
f19fa68d31
1 changed files with 228 additions and 228 deletions
456
src/main.rs
456
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<String> = env::args().collect();
|
||||
let args: Vec<String> = 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/<username> 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/<username> to root:root");
|
||||
print_output(output);
|
||||
|
||||
let output = Command::new("/usr/bin/chmod")
|
||||
.arg("755")
|
||||
.arg(path)
|
||||
.output()
|
||||
.expect("Failed to chmod /srv/ht/<username> to 755");
|
||||
print_output(output);
|
||||
let output = Command::new("/usr/bin/chmod")
|
||||
.arg("755")
|
||||
.arg(path)
|
||||
.output()
|
||||
.expect("Failed to chmod /srv/ht/<username> 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());
|
||||
}
|
||||
|
|
Reference in a new issue