--- a/Cargo.lock +++ b/Cargo.lock @@ -13,34 +13,34 @@ [[package]] name = "aes" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.3", "cpufeatures", - "ctr", - "opaque-debug", ] [[package]] name = "age" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23100453ca2a1bbda9bfc6deac1bebb828d7e66ba481ebccfedfddf29321b6b9" +checksum = "f066ce1514d24201eab31e0831e9333d2e9b06d698b25f705ef0697fee8256a2" dependencies = [ "aes", "age-core", "base64", "bcrypt-pbkdf", "bech32", - "block-modes", + "cbc", "chacha20poly1305", + "cipher 0.4.3", "cookie-factory", + "ctr", "curve25519-dalek", "hkdf", - "hmac 0.11.0", + "hmac", "i18n-embed", "i18n-embed-fl", "lazy_static", @@ -52,6 +52,7 @@ "rsa", "rust-embed", "scrypt", + "sha2 0.10.3", "sha2 0.9.9", "subtle", "x25519-dalek", @@ -60,18 +61,19 @@ [[package]] name = "age-core" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70afa630ef12a4fc666277713efbe6da2bc87bb3f3af0f1149415b701362c615" +checksum = "00a5c8d8a33abc74ad393896a6305351dd159d0e184788f4729e3c80e397fa45" dependencies = [ "base64", "chacha20poly1305", "cookie-factory", "hkdf", + "io_tee", "nom", "rand 0.8.5", "secrecy", - "sha2 0.9.9", + "sha2 0.10.3", ] [[package]] @@ -149,14 +151,13 @@ [[package]] name = "bcrypt-pbkdf" -version = "0.7.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bde65b3c84000288c0abe8aa601a4b7c40b0dbbb7d144dd6c712ed9796e1fd5" +checksum = "f4ef233ffa9cb9c7820b2b0e9efd0821ed180e866c9120ec9f45518659742074" dependencies = [ "blowfish", - "hex-literal", "pbkdf2", - "sha2 0.10.1", + "sha2 0.10.3", ] [[package]] @@ -190,30 +191,22 @@ ] [[package]] -name = "block-modes" -version = "0.8.1" +name = "block-padding" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" +checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78" dependencies = [ - "block-padding", - "cipher", + "generic-array", ] [[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] name = "blowfish" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" dependencies = [ "byteorder", - "cipher", - "opaque-debug", + "cipher 0.4.3", ] [[package]] @@ -235,10 +228,13 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] -name = "cc" -version = "1.0.73" +name = "cbc" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher 0.4.3", +] [[package]] name = "cfg-if" @@ -253,7 +249,7 @@ checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91" dependencies = [ "cfg-if", - "cipher", + "cipher 0.3.0", "cpufeatures", "zeroize", ] @@ -266,7 +262,7 @@ dependencies = [ "aead", "chacha20", - "cipher", + "cipher 0.3.0", "poly1305", "zeroize", ] @@ -295,6 +291,16 @@ ] [[package]] +name = "cipher" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] name = "clap" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -366,25 +372,15 @@ dependencies = [ "generic-array", "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", ] [[package]] name = "ctr" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "0d14f329cfbaf5d0e06b5e87fff7e265d2673c5ea7d2c27691a2c107db1442a0" dependencies = [ - "cipher", + "cipher 0.4.3", ] [[package]] @@ -713,38 +709,21 @@ dependencies = [ "libc", ] - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" [[package]] name = "hkdf" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" -dependencies = [ - "digest 0.9.0", - "hmac 0.11.0", -] - -[[package]] -name = "hmac" -version = "0.11.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" dependencies = [ - "crypto-mac", - "digest 0.9.0", + "hmac", ] [[package]] name = "hmac" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ "digest 0.10.3", ] @@ -846,6 +825,16 @@ ] [[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -875,6 +864,12 @@ ] [[package]] +name = "io_tee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" + +[[package]] name = "itoa" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -891,9 +886,9 @@ [[package]] name = "libc" -version = "0.2.118" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "libm" @@ -985,15 +980,16 @@ [[package]] name = "nix" -version = "0.23.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" dependencies = [ + "autocfg 1.1.0", "bitflags", - "cc", "cfg-if", "libc", "memoffset", + "pin-utils", ] [[package]] @@ -1141,9 +1137,9 @@ [[package]] name = "pbkdf2" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4628cc3cf953b82edcd3c1388c5715401420ce5524fedbab426bd5aba017434" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" dependencies = [ "digest 0.10.3", ] @@ -1482,11 +1478,11 @@ [[package]] name = "salsa20" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.3", ] [[package]] @@ -1506,14 +1502,14 @@ [[package]] name = "scrypt" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e73d6d7c6311ebdbd9184ad6c4447b2f36337e327bda107d3ba9e3c374f9d325" +checksum = "ba0aaf3911fff0d942c10a49779de7754699810fc7dbe3df515613b2ecc8195a" dependencies = [ - "hmac 0.12.0", + "hmac", "pbkdf2", "salsa20", - "sha2 0.10.1", + "sha2 0.10.3", ] [[package]] @@ -1577,9 +1573,9 @@ [[package]] name = "sha2" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" +checksum = "899bf02746a2c92bf1053d9327dadb252b01af1f81f90cdb902411f518bc7215" dependencies = [ "cfg-if", "cpufeatures", --- a/cmds/fleet/Cargo.toml +++ b/cmds/fleet/Cargo.toml @@ -13,14 +13,19 @@ tempfile = "3.2" once_cell = "1.5" hostname = "0.3.1" -age-core = "0.7.0" +age-core = "0.8.0" peg = "0.8.0" -nixlike = {path = "../../crates/nixlike"} -age = { version = "0.7.0", features = ["ssh", "armor"] } +nixlike = { path = "../../crates/nixlike" } +age = { version = "0.8.1", features = ["ssh", "armor"] } base64 = "0.13.0" chrono = { version = "0.4.19", features = ["serde"] } z85 = "3.0.3" -clap = { version = "3.1.0", features = ["derive", "env", "wrap_help", "unicode"] } +clap = { version = "3.1.0", features = [ + "derive", + "env", + "wrap_help", + "unicode", +] } tokio = { version = "1.14.0", features = ["full"] } tracing = "0.1.29" tracing-subscriber = { version = "0.3.3", features = ["fmt", "env-filter"] } --- a/cmds/install-secrets/Cargo.toml +++ b/cmds/install-secrets/Cargo.toml @@ -4,13 +4,18 @@ edition = "2021" [dependencies] -age = { version = "0.7.1", features = ["ssh"] } +age = { version = "0.8.1", features = ["ssh"] } anyhow = "1.0.44" env_logger = "0.9.0" log = "0.4.14" -nix = "0.23.1" +nix = "0.25.0" serde = "1.0.130" serde_json = "1.0.68" -clap = { version = "3.1.0", features = ["derive", "env", "wrap_help", "unicode"] } +clap = { version = "3.1.0", features = [ + "derive", + "env", + "wrap_help", + "unicode", +] } tempfile = "3.2.0" z85 = "3.0.3" --- a/cmds/install-secrets/src/main.rs +++ b/cmds/install-secrets/src/main.rs @@ -6,8 +6,8 @@ use nix::sys::stat::Mode; use nix::unistd::{chown, Group, User}; use serde::{Deserialize, Deserializer}; -use std::fs::{self, DirBuilder}; -use std::io::{self, Cursor, Read}; +use std::fs::{self, DirBuilder, File}; +use std::io::{self, Cursor, Read, Write}; use std::iter; use std::os::unix::prelude::PermissionsExt; use std::str::from_utf8; @@ -32,10 +32,13 @@ #[serde(deserialize_with = "from_z85")] secret: Option>, - public: String, + public: Option, - secret_hash: String, - public_path: String, + public_path: PathBuf, + stable_public_path: PathBuf, + + secret_path: PathBuf, + stable_secret_path: PathBuf, } fn from_z85<'de, D>(deserializer: D) -> Result>, D::Error> @@ -54,40 +57,28 @@ type Data = HashMap; -fn init_secret( - identity: &age::ssh::Identity, - dir: &Path, - name: &str, - value: DataItem, -) -> Result<()> { +fn init_secret(identity: &age::ssh::Identity, value: DataItem) -> Result<()> { + if let Some(public) = &value.public { + let mut hashed = File::create(&value.public_path)?; + let mut stable_dir = value.stable_public_path.parent().expect("not root"); + let mut stable_temp = + tempfile::NamedTempFile::new_in(stable_dir).context("failed to create tempfile")?; + hashed.write_all(public.as_bytes())?; + stable_temp.write_all(public.as_bytes())?; + stable_temp.flush()?; + fs::set_permissions(stable_temp.path(), fs::Permissions::from_mode(0o444)) + .context("perm")?; + fs::set_permissions(&value.public_path, fs::Permissions::from_mode(0o444)) + .context("perm")?; + + stable_temp + .persist(value.stable_public_path) + .context("failed to persist")?; + } 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() { - bail!("found escaping name"); - } - - let secret_dir = path - .parent() - .expect("path is in tempdir, so it should have parent"); - - if secret_dir != dir { - DirBuilder::new() - .recursive(true) - // o: xrw - // g: xr - // a: xr - .mode(0o755) - .create( - path.parent() - .expect("path is in tempdir, so it should have parent"), - ) - .context("failed to create secret directory")?; - } let mode = Mode::from_bits( u32::from_str_radix(&value.mode, 8).context("failed to parse mode as octal")?, @@ -99,10 +90,13 @@ let group = Group::from_name(&value.group) .context("failed to get group")? .ok_or_else(|| anyhow!("group not found"))?; - let mut tempfile = - tempfile::NamedTempFile::new_in(secret_dir).context("failed to create tempfile")?; - // File is owned by root, and only root can modify it + let mut stable_dir = value.stable_secret_path.parent().expect("not root"); + let mut stable_temp = + tempfile::NamedTempFile::new_in(stable_dir).context("failed to create tempfile")?; + let mut hashed = File::create(&value.secret_path)?; + + // File is owned by root, and only root can modify it let decrypted = { let mut input = Cursor::new(&secret); let decryptor = Decryptor::new(&mut input).context("failed to init decryptor")?; @@ -121,14 +115,20 @@ decrypted }; - io::copy(&mut Cursor::new(decrypted), &mut tempfile) + io::copy(&mut Cursor::new(&decrypted), &mut stable_temp) .context("failed to write decrypted file")?; + io::copy(&mut Cursor::new(decrypted), &mut hashed).context("failed to write decrypted file")?; // Make file owned by specified user and group, then change mode - chown(tempfile.path(), Some(user.uid), Some(group.gid)) + chown(stable_temp.path(), Some(user.uid), Some(group.gid)) .context("failed to apply user/group")?; - fs::set_permissions(tempfile.path(), fs::Permissions::from_mode(mode.bits())).unwrap(); - tempfile.persist(path).context("failed to persist")?; + chown(&value.secret_path, Some(user.uid), Some(group.gid)) + .context("failed to apply user/group")?; + fs::set_permissions(stable_temp.path(), fs::Permissions::from_mode(mode.bits())).unwrap(); + fs::set_permissions(&value.secret_path, fs::Permissions::from_mode(mode.bits())).unwrap(); + stable_temp + .persist(value.stable_secret_path) + .context("failed to persist")?; Ok(()) } @@ -143,7 +143,12 @@ let data_str = from_utf8(&data).context("failed to read data to string")?; let data: Data = serde_json::from_str(data_str).context("failed to parse data")?; - let tempdir = tempfile::tempdir_in("/run/").context("failed to create secrets tempdir")?; + if !fs::metadata("/run/secrets") + .map(|m| m.is_dir()) + .unwrap_or(false) + { + fs::create_dir("/run/secrets").context("failed to create secrets directory")?; + } let identity = age::ssh::Identity::from_buffer( &mut Cursor::new( @@ -155,7 +160,7 @@ let mut failed = false; for (name, value) in data { - if let Err(e) = init_secret(&identity, tempdir.path(), &name, value) { + if let Err(e) = init_secret(&identity, value) { error!( "{:?}", e.context(format!("failed to initialize secret {}", name)) @@ -167,26 +172,5 @@ bail!("one or more secrets failed"); } - if fs::metadata("/run/secrets") - .map(|m| m.is_dir()) - .unwrap_or(false) - { - // Already linked - renameat2( - None, - tempdir.path(), - None, - "/run/secrets", - RenameFlags::RENAME_EXCHANGE, - ) - .context("failed to exchange secret directories")?; - if tempdir.close().is_err() { - warn!("failed to unlink old secrets"); - } - } else { - // Link now - let persisted = tempdir.into_path(); - fs::rename(&persisted, "/run/secrets").context("failed to link secret directory")?; - } Ok(()) } --- a/flake.nix +++ b/flake.nix @@ -22,6 +22,7 @@ devShell = (pkgs.mkShell.override { stdenv = llvmPkgs.stdenv; }) { nativeBuildInputs = with pkgs; [ rust + lld cargo-edit cargo-udeps cargo-fuzz