diff options
author | Lars Wirzenius <liw@liw.fi> | 2023-10-14 10:00:10 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2023-10-14 10:42:28 +0300 |
commit | 7a8569d83c336d173a04a137716289e0bcde9323 (patch) | |
tree | 45fe3df14de53a55b7ff9e09fe5d6f435103c36a | |
parent | dc54c84f8aab5b45722f1e75a5d6607cba8aabae (diff) | |
download | sshca-7a8569d83c336d173a04a137716289e0bcde9323.tar.gz |
feat: certify all users at once
Sponsored-by: author
-rw-r--r-- | src/cmd/user.rs | 57 | ||||
-rw-r--r-- | src/error.rs | 3 | ||||
-rw-r--r-- | sshca.md | 28 |
3 files changed, 77 insertions, 11 deletions
diff --git a/src/cmd/user.rs b/src/cmd/user.rs index 3bd0050..e1eb4bf 100644 --- a/src/cmd/user.rs +++ b/src/cmd/user.rs @@ -131,7 +131,7 @@ impl Runnable for New { /// Certify a user. #[derive(Debug, Parser)] pub struct Certify { - /// Write output to this file. + /// Write output to this file. If --all is used, to a file in this directory. #[clap(long)] output: Option<PathBuf>, @@ -142,24 +142,35 @@ pub struct Certify { #[clap(long, default_value = DEFAULT_VALIDITY)] expires_in: String, + /// CA to use to certify. #[clap(long)] ca: String, - username: String, + /// User to certify, unless --all is used. + username: Option<String>, + + /// Certify all users? + #[clap(long)] + all: bool, } -impl Runnable for Certify { - fn run(&mut self, _config: &Config, store: &mut KeyStore) -> Result<(), CAError> { +impl Certify { + fn certify( + &self, + username: &str, + store: &KeyStore, + output: &Option<PathBuf>, + ) -> Result<(), CAError> { let ca = if let Some(pair) = store.get_user_ca(&self.ca) { pair } else { return Err(CAError::NoSuchUserCA(self.ca.clone())); }; - let user = if let Some(user) = store.get_user(&self.username) { + let user = if let Some(user) = store.get_user(username) { user } else { - return Err(CAError::NoSuchUser(self.username.clone())); + return Err(CAError::NoSuchUser(username.into())); }; let key = user.public(); @@ -167,12 +178,36 @@ impl Runnable for Certify { let valid_for = parse_validity(&self.expires_in)?; let cert = ca.certify(key, &valid_for, user.principals())?; - if let Some(output) = &self.output { - let mut file = File::create(output).map_err(|e| CAError::Create(output.into(), e))?; - write!(file, "{}", cert).map_err(|e| CAError::Write(output.into(), e))?; + if let Some(filename) = output { + let mut file = + File::create(filename).map_err(|e| CAError::Create(filename.into(), e))?; + write!(file, "{}", cert).map_err(|e| CAError::Write(filename.into(), e))?; } else { - print!("{}", cert); - } + print!("{}", cert) + }; + + Ok(()) + } +} + +impl Runnable for Certify { + fn run(&mut self, _config: &Config, store: &mut KeyStore) -> Result<(), CAError> { + if self.all { + let dirname = if let Some(output) = &self.output { + output.clone() + } else { + PathBuf::from(".") + }; + for user in store.users() { + println!("Certifying user {}", user.name()); + let filename = dirname.join(format!("{}-cert.pub", user.name())); + self.certify(user.name(), store, &Some(filename))?; + } + } else if let Some(user) = self.username.as_ref() { + self.certify(user, store, &self.output)?; + } else { + return Err(CAError::UserOrAll); + }; Ok(()) } diff --git a/src/error.rs b/src/error.rs index 5ef6d9d..65b422e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -94,4 +94,7 @@ pub enum CAError { #[error("host key for {0} has expired, only valid until {1}")] ExpiredHostKey(String, String), + + #[error("must give name of user to certify, or --all")] + UserOrAll, } @@ -1006,6 +1006,34 @@ when I run ssh-keygen -L -f my.cert then stdout matches regex Valid: from \d+-\d+-\d+T\d+:\d+:\d+ to \d+-\d+-\d+T\d+:\d+:\d+ ~~~ +### Certify all users + +_Requirement: we must be able to create all users certificates at once._ + +Justification: This is convenient if there are many users. + +~~~scenario +given an installed sshca +given file .config/sshca/config.yaml from config.yaml + +when I run sshca ca new user CAv1 + +when I run ssh-keygen -t ed25519 -N '' -f granny +when I run sshca user new granny granny.pub + +when I run ssh-keygen -t ed25519 -N '' -f gytha +when I run sshca user new gytha gytha.pub + +when I run sshca user certify --ca CAv1 --all +then file granny-cert.pub matches regex /^ssh-ed25519-cert-v01@openssh.com/ +then file gytha-cert.pub matches regex /^ssh-ed25519-cert-v01@openssh.com/ + +given a directory certs +when I run sshca user certify --ca CAv1 --all --output=certs +then file certs/granny-cert.pub matches regex /^ssh-ed25519-cert-v01@openssh.com/ +then file certs/gytha-cert.pub matches regex /^ssh-ed25519-cert-v01@openssh.com/ +~~~ + ### User CA can't certify a host _Requirement: we can't certify a host with a user CA._ |