nixfleet_cli/commands/
mint_operator_cert.rs1use std::path::PathBuf;
5
6use anyhow::{Context, Result, bail};
7use clap::Args as ClapArgs;
9
10use crate::{MintOperatorCertArgs, mint_operator_cert};
11
12#[derive(ClapArgs, Debug)]
13#[command(
14 about = "Mint an mTLS client cert for an operator workstation, signed by the offline fleet root CA."
15)]
16pub struct Args {
17 #[arg(long)]
21 root_cert: Option<PathBuf>,
22
23 #[arg(long)]
27 root_key: Option<PathBuf>,
28
29 #[arg(long)]
31 cn: Option<String>,
32
33 #[arg(long)]
35 output_cert: Option<PathBuf>,
36
37 #[arg(long)]
39 output_key: Option<PathBuf>,
40
41 #[arg(long, default_value_t = 365)]
43 days: u32,
44
45 #[arg(long)]
47 force: bool,
48}
49
50pub fn run(args: Args) -> Result<()> {
51 let cfg_dir = crate::config::default_config_path()
52 .parent()
53 .map(|p| p.to_path_buf())
54 .context("resolve ~/.config/nixfleet directory")?;
55
56 let root_cert = args
57 .root_cert
58 .or_else(|| std::env::var_os("NIXFLEET_OPERATOR_FLEET_ROOT_CERT_FILE").map(PathBuf::from))
59 .unwrap_or_else(|| cfg_dir.join("fleet-root.cert.pem"));
60 let root_key = args
61 .root_key
62 .or_else(|| std::env::var_os("NIXFLEET_OPERATOR_FLEET_ROOT_KEY_FILE").map(PathBuf::from))
63 .unwrap_or_else(|| cfg_dir.join("fleet-root.key.pem"));
64 let output_cert = args
65 .output_cert
66 .unwrap_or_else(|| cfg_dir.join("operator.pem"));
67 let output_key = args
68 .output_key
69 .unwrap_or_else(|| cfg_dir.join("operator.key"));
70
71 let cn = match args.cn {
72 Some(c) => c,
73 None => {
74 let user = std::env::var("USER").unwrap_or_default();
75 let host = whoami::fallible::hostname().unwrap_or_default();
76 if user.is_empty() || host.is_empty() {
77 bail!("operator CN is empty (USER={user:?}, HOSTNAME={host:?}); pass --cn");
78 }
79 format!("operator-{user}@{host}")
80 }
81 };
82
83 let outcome = mint_operator_cert(MintOperatorCertArgs {
84 root_cert_path: root_cert,
85 root_key_path: root_key,
86 cn,
87 output_cert_path: output_cert,
88 output_key_path: output_key,
89 validity_days: args.days,
90 overwrite: args.force,
91 })?;
92
93 eprintln!(
94 "minted operator cert
95 cn: {}
96 valid until: {} ({} days)
97 cert: {}
98 key: {}
99
100next: nixfleet config init --client-cert {} --client-key {}",
101 outcome.cn,
102 outcome.not_after.to_rfc3339(),
103 args.days,
104 outcome.cert_path.display(),
105 outcome.key_path.display(),
106 outcome.cert_path.display(),
107 outcome.key_path.display(),
108 );
109 Ok(())
110}