From 550e0d8870d3d7aecc1e79ea2349ec885ab496ac Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Sun, 22 Dec 2019 17:35:06 +0100 Subject: [PATCH] src/bin/proxmox-backup-manager.rs: add cli to generate proxy certificate Without calling external openssl binary. --- src/api2/node.rs | 2 +- src/api2/node/dns.rs | 2 +- src/bin/proxmox-backup-manager.rs | 157 ++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 2 deletions(-) diff --git a/src/api2/node.rs b/src/api2/node.rs index ff90d284..b8bcd6cd 100644 --- a/src/api2/node.rs +++ b/src/api2/node.rs @@ -4,7 +4,7 @@ use proxmox::api::list_subdirs_api_method; mod tasks; mod time; mod network; -mod dns; +pub mod dns; mod syslog; mod journal; mod services; diff --git a/src/api2/node/dns.rs b/src/api2/node/dns.rs index 065af411..923e24ad 100644 --- a/src/api2/node/dns.rs +++ b/src/api2/node/dns.rs @@ -16,7 +16,7 @@ use crate::api2::types::*; static RESOLV_CONF_FN: &str = "/etc/resolv.conf"; -fn read_etc_resolv_conf() -> Result { +pub fn read_etc_resolv_conf() -> Result { let mut result = json!({}); diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs index c88a262f..0f64cf03 100644 --- a/src/bin/proxmox-backup-manager.rs +++ b/src/bin/proxmox-backup-manager.rs @@ -1,10 +1,15 @@ use failure::*; use serde_json::{json, Value}; +use std::path::PathBuf; +use nix::sys::stat::Mode; +use proxmox::tools::fs::{CreateOptions, replace_file}; use proxmox::api::{api, cli::*}; +use proxmox_backup::configdir; use proxmox_backup::tools; use proxmox_backup::config; +use proxmox_backup::backup::*; use proxmox_backup::api2::types::*; use proxmox_backup::client::*; use proxmox_backup::tools::ticket::*; @@ -260,11 +265,163 @@ fn task_mgmt_cli() -> CommandLineInterface { cmd_def.into() } +#[api( + input: { + properties: { + force: { + description: "Force generation of new SSL certifate.", + type: Boolean, + optional:true, + }, + } + }, +)] +/// Update node certificates and generate all needed files/directories. +fn update_certs(force: Option) -> Result<(), Error> { + + let backup_user = backup_user()?; + + config::create_configdir()?; + + if let Err(err) = generate_auth_key() { + bail!("unable to generate auth key - {}", err); + } + + if let Err(err) = generate_csrf_key() { + bail!("unable to generate csrf key - {}", err); + } + + //openssl req -x509 -newkey rsa:4096 -keyout /etc/proxmox-backup/proxy.key -out /etc/proxmox-backup/proxy.pem -nodes + let key_path = PathBuf::from(configdir!("/proxy.key")); + let cert_path = PathBuf::from(configdir!("/proxy.pem")); + + if key_path.exists() && cert_path.exists() && !force.unwrap_or(false) { return Ok(()); } + + use openssl::rsa::{Rsa}; + use openssl::x509::{X509Builder}; + use openssl::pkey::PKey; + + let rsa = Rsa::generate(4096).unwrap(); + + let priv_pem = rsa.private_key_to_pem()?; + + replace_file( + &key_path, + &priv_pem, + CreateOptions::new() + .perm(Mode::from_bits_truncate(0o0640)) + .owner(nix::unistd::ROOT) + .group(backup_user.gid), + )?; + + let mut x509 = X509Builder::new()?; + + x509.set_version(2)?; + + let today = openssl::asn1::Asn1Time::days_from_now(0)?; + x509.set_not_before(&today)?; + let expire = openssl::asn1::Asn1Time::days_from_now(365*1000)?; + x509.set_not_after(&expire)?; + + let nodename = proxmox::tools::nodename(); + let mut fqdn = nodename.to_owned(); + + let resolv_conf = proxmox_backup::api2::node::dns::read_etc_resolv_conf()?; + if let Some(search) = resolv_conf["search"].as_str() { + fqdn.push('.'); + fqdn.push_str(search); + } + + // we try to generate an unique 'subject' to avoid browser problems + //(reused serial numbers, ..) + let uuid = proxmox::tools::uuid::Uuid::generate(); + + let mut subject_name = openssl::x509::X509NameBuilder::new()?; + subject_name.append_entry_by_text("O", "Proxmox Backup Server")?; + subject_name.append_entry_by_text("OU", &format!("{:X}", uuid))?; + subject_name.append_entry_by_text("CN", &fqdn)?; + let subject_name = subject_name.build(); + + x509.set_subject_name(&subject_name)?; + x509.set_issuer_name(&subject_name)?; + + let bc = openssl::x509::extension::BasicConstraints::new(); // CA = false + let bc = bc.build()?; + x509.append_extension(bc)?; + + let usage = openssl::x509::extension::ExtendedKeyUsage::new() + .server_auth() + .build()?; + x509.append_extension(usage)?; + + let context = x509.x509v3_context(None, None); + + let mut alt_names = openssl::x509::extension::SubjectAlternativeName::new(); + + alt_names.ip("127.0.0.1"); + alt_names.ip("::1"); + + // fixme: add local node IPs + + alt_names.dns("localhost"); + + if nodename != "localhost" { alt_names.dns(nodename); } + if nodename != fqdn { alt_names.dns(&fqdn); } + + let alt_names = alt_names.build(&context)?; + + x509.append_extension(alt_names)?; + + let pub_pem = rsa.public_key_to_pem()?; + let pubkey = PKey::public_key_from_pem(&pub_pem)?; + + x509.set_pubkey(&pubkey)?; + + let context = x509.x509v3_context(None, None); + let ext = openssl::x509::extension::SubjectKeyIdentifier::new().build(&context)?; + x509.append_extension(ext)?; + + let context = x509.x509v3_context(None, None); + let ext = openssl::x509::extension::AuthorityKeyIdentifier::new() + .keyid(true) + .build(&context)?; + x509.append_extension(ext)?; + + let privkey = PKey::from_rsa(rsa)?; + + x509.sign(&privkey, openssl::hash::MessageDigest::sha256())?; + + let x509 = x509.build(); + let cert_pem = x509.to_pem()?; + + replace_file( + &cert_path, + &cert_pem, + CreateOptions::new() + .perm(Mode::from_bits_truncate(0o0640)) + .owner(nix::unistd::ROOT) + .group(backup_user.gid), + )?; + + Ok(()) +} + +fn cert_mgmt_cli() -> CommandLineInterface { + + let update_cert_cmd_def = CliCommand::new(&API_METHOD_UPDATE_CERTS); + + let cmd_def = CliCommandMap::new() + .insert("update", update_cert_cmd_def); + + cmd_def.into() +} + fn main() { let cmd_def = CliCommandMap::new() .insert("datastore", datastore_commands()) .insert("garbage-collection", garbage_collection_commands()) + .insert("cert", cert_mgmt_cli()) .insert("task", task_mgmt_cli()); run_cli_command(cmd_def);