summaryrefslogtreecommitdiff
path: root/src/cloudinit.rs
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-03-07 09:01:23 +0200
committerLars Wirzenius <liw@liw.fi>2021-03-07 12:25:19 +0200
commit4621b07522564f6a3c1c2ad0484fb88cf0e2ce49 (patch)
tree8f8c33437771322c2c5c2c40d79151320beb2beb /src/cloudinit.rs
parenta6f802fda57fc7e951c0374a268de2274718cd9d (diff)
downloadvmadm-4621b07522564f6a3c1c2ad0484fb88cf0e2ce49.tar.gz
feat: generate SSH key pairs, create host certificates
Diffstat (limited to 'src/cloudinit.rs')
-rw-r--r--src/cloudinit.rs37
1 files changed, 31 insertions, 6 deletions
diff --git a/src/cloudinit.rs b/src/cloudinit.rs
index 6e62cc5..842baa7 100644
--- a/src/cloudinit.rs
+++ b/src/cloudinit.rs
@@ -1,7 +1,9 @@
use crate::spec::{Specification, SpecificationError};
+use crate::sshkeys::{CaKey, KeyError, KeyKind, KeyPair};
use log::debug;
use serde::{Deserialize, Serialize};
use shell_words::quote;
+use std::default::Default;
use std::fs::write;
// use std::os::unix::fs::PermissionsExt;
use std::path::Path;
@@ -92,6 +94,9 @@ logfile.close()
#[derive(Debug, thiserror::Error)]
pub enum CloudInitError {
+ #[error("Host certificate requested, but no CA key specified")]
+ NoCAKey,
+
#[error("failed to create ISO image with genisoimage: {0}")]
IsoFailed(String),
@@ -99,6 +104,9 @@ pub enum CloudInitError {
SpecificationError(#[from] SpecificationError),
#[error(transparent)]
+ KeyError(#[from] KeyError),
+
+ #[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
@@ -148,7 +156,7 @@ impl Userdata {
fn from(spec: &Specification) -> Result<Self, CloudInitError> {
Ok(Self {
ssh_authorized_keys: spec.ssh_keys.clone(),
- ssh_keys: Hostkeys::from(spec),
+ ssh_keys: Hostkeys::from(spec)?,
runcmd: vec![
format!("python3 -c {}", quote(SCRIPT)),
"systemctl reload ssh".to_string(),
@@ -165,7 +173,7 @@ impl Userdata {
}
}
-#[derive(Clone, Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, Default)]
struct Hostkeys {
#[serde(skip_serializing_if = "Option::is_none")]
rsa_private: Option<String>,
@@ -193,7 +201,7 @@ struct Hostkeys {
}
impl Hostkeys {
- fn from(spec: &Specification) -> Option<Self> {
+ fn from(spec: &Specification) -> Result<Option<Self>, CloudInitError> {
let rsa = spec.rsa_host_key.clone();
let rsa_cert = spec.rsa_host_cert.clone();
@@ -207,7 +215,7 @@ impl Hostkeys {
let ed25519_cert = spec.ed25519_host_cert.clone();
if rsa.is_some() || dsa.is_some() || ecdsa.is_some() || ed25519.is_some() {
- Some(Self {
+ Ok(Some(Self {
rsa_private: rsa,
rsa_certificate: rsa_cert,
dsa_private: dsa,
@@ -216,9 +224,26 @@ impl Hostkeys {
ecdsa_certificate: ecdsa_cert,
ed25519_private: ed25519,
ed25519_certificate: ed25519_cert,
- })
+ }))
+ } else if spec.generate_host_certificate {
+ if spec.ca_key.is_none() {
+ return Err(CloudInitError::NoCAKey);
+ }
+ if let Some(filename) = &spec.ca_key {
+ let ca = CaKey::from_file(&filename)?;
+ let pair = KeyPair::generate(KeyKind::Ed25519)?;
+ let cert = ca.certify_host(&pair, &spec.name)?;
+ debug!("generated Ed25519 host certificate {:?}", cert);
+ Ok(Some(Self {
+ ed25519_private: Some(pair.public().to_string()),
+ ed25519_certificate: Some(cert.to_string()),
+ ..Self::default()
+ }))
+ } else {
+ Ok(None)
+ }
} else {
- None
+ Ok(None)
}
}
}