--- a/cmds/fleet/src/cmds/secrets/mod.rs +++ b/cmds/fleet/src/cmds/secrets/mod.rs @@ -70,17 +70,21 @@ let mut input = vec![]; io::stdin().read_to_end(&mut input)?; - let mut encrypted = vec![]; - let recipients = recipients - .iter() - .cloned() - .map(|r| Box::new(r) as Box) - .collect(); - let mut encryptor = - age::Encryptor::with_recipients(recipients).wrap_output(&mut encrypted)?; - io::copy(&mut Cursor::new(input), &mut encryptor)?; - encryptor.finish()?; - encrypted + if input.is_empty() { + input + } else { + let mut encrypted = vec![]; + let recipients = recipients + .iter() + .cloned() + .map(|r| Box::new(r) as Box) + .collect(); + let mut encryptor = age::Encryptor::with_recipients(recipients) + .wrap_output(&mut encrypted)?; + io::copy(&mut Cursor::new(input), &mut encryptor)?; + encryptor.finish()?; + encrypted + } }; let mut data = config.data_mut(); --- a/cmds/fleet/src/fleetdata.rs +++ b/cmds/fleet/src/fleetdata.rs @@ -39,7 +39,12 @@ pub expire_at: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub public: Option, - #[serde(serialize_with = "as_z85", deserialize_with = "from_z85")] + #[serde( + default, + skip_serializing_if = "Vec::is_empty", + serialize_with = "as_z85", + deserialize_with = "from_z85" + )] pub secret: Vec, } --- a/cmds/install-secrets/src/main.rs +++ b/cmds/install-secrets/src/main.rs @@ -29,16 +29,21 @@ mode: String, owner: String, #[serde(deserialize_with = "from_z85")] - secret: Vec, + secret: Option>, } -fn from_z85<'de, D>(deserializer: D) -> Result, D::Error> +fn from_z85<'de, D>(deserializer: D) -> Result>, D::Error> where D: Deserializer<'de>, { use serde::de::Error; - String::deserialize(deserializer) - .and_then(|string| z85::decode(&string).map_err(|err| Error::custom(err.to_string()))) + if let Some(v) = >::deserialize(deserializer)? { + Ok(Some( + z85::decode(&v).map_err(|err| Error::custom(err.to_string()))?, + )) + } else { + Ok(None) + } } type Data = HashMap; @@ -49,6 +54,11 @@ name: &str, value: DataItem, ) -> Result<()> { + if value.secret.is_none() { + return Ok(()); + } + let secret = value.secret.as_ref().unwrap(); + let mut path = dir.to_path_buf(); path.push(name); if path.strip_prefix(&dir).is_err() { @@ -88,7 +98,7 @@ // File is owned by root, and only root can modify it let decrypted = { - let mut input = Cursor::new(&value.secret); + let mut input = Cursor::new(&secret); let decryptor = Decryptor::new(&mut input).context("failed to init decryptor")?; let decryptor = match decryptor { Decryptor::Recipients(r) => r, --- a/modules/nixos/secrets.nix +++ b/modules/nixos/secrets.nix @@ -3,7 +3,9 @@ sysConfig = config; secretType = types.submodule ({ config, ... }: { config = { - path = mkOptionDefault "/run/secrets/${config._module.args.name}"; + path = mkOptionDefault (if config.secret == null then (error "secret is not set") else "/run/secrets/${config._module.args.name}"); + publicPath = mkOptionDefault (pkgs.writeText "pub-${config._module.args.name}" config.public); + secret = mkIf (config.public != null) ""; }; options = { public = mkOption { @@ -12,7 +14,7 @@ default = null; }; secret = mkOption { - type = types.str; + type = types.nullOr types.str; description = "Encrypted secret data"; }; mode = mkOption { @@ -36,6 +38,11 @@ readOnly = true; description = "Path to the decrypted secret"; }; + publicPath = mkOption { + type = types.package; + readOnly = true; + description = "Path to the public part of secret"; + }; }; }); secretsFile = pkgs.writeTextFile {