use std::borrow::Cow; use std::time::UNIX_EPOCH; use chrono::Duration; use shell_escape::escape; use ssh_key::Certificate; /// Generates an command to renew the given certs pub fn renew_command(cert: &Certificate, ca_path: &str, file_name: Option<&str>) -> String { let validity = cert .valid_before_time() .duration_since(cert.valid_after_time()) .unwrap_or(Duration::zero().to_std().unwrap()); let expiry = cert.valid_before_time().checked_add(validity).unwrap(); let expiry_date = expiry.duration_since(UNIX_EPOCH).unwrap(); let host_key = if cert.cert_type().is_host() { " -h" } else { "" }; let opts = cert .critical_options() .iter() .map(|(opt, val)| { if val.is_empty() { opt.clone() } else { format!("{opt}={val}") } }) .map(|arg| format!("-O {}", escape(arg.into()))) .collect::>() .join(" "); let opts = opts.trim(); let renew_command = format!( "ssh-keygen -s {ca_path} {host_key} -I {} -n {} -z {} -V {}:{} {opts} {}", escape(cert.key_id().into()), escape(cert.valid_principals().join(",").into()), cert.serial() + 1, cert.valid_after(), expiry_date.as_secs(), escape( file_name .map(|name| name.trim_end_matches("-cert.pub")).map(Cow::Borrowed) .unwrap_or_else(|| escape(format!("{}.pub", cert.key_id()).into())) ) ); renew_command }