difftreelog
feat move secret generation helpers to core
in: trunk
13 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -38,9 +38,9 @@
[[package]]
name = "aes"
-version = "0.8.3"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
@@ -63,14 +63,14 @@
[[package]]
name = "age"
-version = "0.9.2"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d55a4d912c80a92762ffd1c884065f3f9646467d22c95390e824a0ff7def472"
+checksum = "edeef7d7b199195a2d7d7a8155d2d04aee736e60c5c7bdd7097d115369a8817d"
dependencies = [
"aes",
"aes-gcm",
"age-core",
- "base64 0.13.1",
+ "base64",
"bcrypt-pbkdf",
"bech32",
"cbc",
@@ -79,7 +79,6 @@
"cookie-factory",
"ctr",
"curve25519-dalek",
- "hkdf",
"hmac",
"i18n-embed",
"i18n-embed-fl",
@@ -87,8 +86,7 @@
"nom",
"num-traits",
"pin-project",
- "rand 0.7.3",
- "rand 0.8.5",
+ "rand",
"rsa",
"rust-embed",
"scrypt",
@@ -100,17 +98,17 @@
[[package]]
name = "age-core"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d2e815ac879dc23c1139e720d21c6cd4d1276345c772587285d965a69b8f32"
+checksum = "a5f11899bc2bbddd135edbc30c36b1924fa59d0746bb45beb5933fafe3fe509b"
dependencies = [
- "base64 0.13.1",
+ "base64",
"chacha20poly1305",
"cookie-factory",
"hkdf",
"io_tee",
"nom",
- "rand 0.8.5",
+ "rand",
"secrecy",
"sha2",
]
@@ -150,29 +148,10 @@
]
[[package]]
-name = "ansi-str"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cf4578926a981ab0ca955dc023541d19de37112bc24c1a197bd806d3d86ad1d"
-dependencies = [
- "ansitok",
-]
-
-[[package]]
-name = "ansitok"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83"
-dependencies = [
- "nom",
- "vte 0.10.1",
-]
-
-[[package]]
name = "anstream"
-version = "0.6.5"
+version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
+checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -184,9 +163,9 @@
[[package]]
name = "anstyle"
-version = "1.0.4"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
@@ -218,9 +197,9 @@
[[package]]
name = "anyhow"
-version = "1.0.77"
+version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9"
+checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "arc-swap"
@@ -230,12 +209,6 @@
[[package]]
name = "arrayvec"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
-
-[[package]]
-name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
@@ -248,18 +221,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
-]
-
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
+ "syn 2.0.49",
]
[[package]]
@@ -285,15 +247,9 @@
[[package]]
name = "base64"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
-
-[[package]]
-name = "base64"
-version = "0.21.5"
+version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64ct"
@@ -303,9 +259,9 @@
[[package]]
name = "bcrypt-pbkdf"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3806a8db60cf56efee531616a34a6aaa9a114d6da2add861b0fa4a188881b2c7"
+checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2"
dependencies = [
"blowfish",
"pbkdf2",
@@ -338,9 +294,9 @@
[[package]]
name = "bitflags"
-version = "2.4.1"
+version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
dependencies = [
"serde",
]
@@ -375,9 +331,9 @@
[[package]]
name = "bumpalo"
-version = "3.14.0"
+version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f"
[[package]]
name = "bytecount"
@@ -456,9 +412,9 @@
[[package]]
name = "chrono"
-version = "0.4.31"
+version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
dependencies = [
"android-tzdata",
"iana-time-zone",
@@ -466,7 +422,7 @@
"num-traits",
"serde",
"wasm-bindgen",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.0",
]
[[package]]
@@ -482,9 +438,9 @@
[[package]]
name = "clap"
-version = "4.4.12"
+version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d"
+checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
dependencies = [
"clap_builder",
"clap_derive",
@@ -492,14 +448,14 @@
[[package]]
name = "clap_builder"
-version = "4.4.12"
+version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
+checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
- "strsim",
+ "strsim 0.11.0",
"terminal_size",
"unicase",
"unicode-width",
@@ -507,21 +463,21 @@
[[package]]
name = "clap_derive"
-version = "4.4.7"
+version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
name = "clap_lex"
-version = "0.6.0"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
@@ -531,15 +487,15 @@
[[package]]
name = "console"
-version = "0.15.7"
+version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
+checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
- "windows-sys 0.45.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -568,9 +524,9 @@
[[package]]
name = "cpufeatures"
-version = "0.2.11"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
@@ -582,7 +538,7 @@
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
- "rand_core 0.6.4",
+ "rand_core",
"typenum",
]
@@ -597,18 +553,32 @@
[[package]]
name = "curve25519-dalek"
-version = "3.2.0"
+version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
+checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
dependencies = [
- "byteorder",
- "digest 0.9.0",
- "rand_core 0.5.1",
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
"subtle",
"zeroize",
]
[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.49",
+]
+
+[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -623,9 +593,9 @@
[[package]]
name = "der"
-version = "0.6.1"
+version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
dependencies = [
"const-oid",
"zeroize",
@@ -639,15 +609,6 @@
dependencies = [
"powerfmt",
"serde",
-]
-
-[[package]]
-name = "digest"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
-dependencies = [
- "generic-array",
]
[[package]]
@@ -660,27 +621,6 @@
"const-oid",
"crypto-common",
"subtle",
-]
-
-[[package]]
-name = "dirs"
-version = "5.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
-dependencies = [
- "dirs-sys",
-]
-
-[[package]]
-name = "dirs-sys"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
-dependencies = [
- "libc",
- "option-ext",
- "redox_users",
- "windows-sys 0.48.0",
]
[[package]]
@@ -691,14 +631,14 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
name = "either"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]]
name = "encode_unicode"
@@ -707,19 +647,6 @@
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
-name = "env_logger"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
-dependencies = [
- "humantime",
- "is-terminal",
- "log",
- "regex",
- "termcolor",
-]
-
-[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -742,6 +669,12 @@
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
+name = "fiat-crypto"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382"
+
+[[package]]
name = "find-crate"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -759,7 +692,7 @@
"age-core",
"anyhow",
"async-trait",
- "base64 0.21.5",
+ "base64",
"better-command",
"chrono",
"clap",
@@ -797,12 +730,12 @@
"age",
"anyhow",
"clap",
- "env_logger",
- "log",
"nix",
"serde",
"serde_json",
"tempfile",
+ "tracing",
+ "tracing-subscriber",
"z85",
]
@@ -912,7 +845,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
@@ -957,24 +890,13 @@
[[package]]
name = "getrandom"
-version = "0.1.16"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
]
[[package]]
@@ -1013,20 +935,11 @@
[[package]]
name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
[[package]]
-name = "hermit-abi"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
-
-[[package]]
name = "hkdf"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1041,7 +954,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
- "digest 0.10.7",
+ "digest",
]
[[package]]
@@ -1062,12 +975,6 @@
checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1"
[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
-[[package]]
name = "i18n-config"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1077,15 +984,15 @@
"serde",
"serde_derive",
"thiserror",
- "toml 0.8.8",
+ "toml 0.8.10",
"unic-langid",
]
[[package]]
name = "i18n-embed"
-version = "0.13.9"
+version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92a86226a7a16632de6723449ee5fe70bac5af718bc642ee9ca2f0f6e14fa1fa"
+checksum = "94205d95764f5bb9db9ea98fa77f89653365ca748e27161f5bbea2ffd50e459c"
dependencies = [
"arc-swap",
"fluent",
@@ -1104,9 +1011,9 @@
[[package]]
name = "i18n-embed-fl"
-version = "0.6.7"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d26a3d3569737dfaac7fc1c4078e6af07471c3060b8e570bcd83cdd5f4685395"
+checksum = "9fc1f8715195dffc4caddcf1cf3128da15fe5d8a137606ea8856c9300047d5a2"
dependencies = [
"dashmap",
"find-crate",
@@ -1118,8 +1025,8 @@
"proc-macro-error",
"proc-macro2",
"quote",
- "strsim",
- "syn 2.0.48",
+ "strsim 0.10.0",
+ "syn 2.0.49",
"unic-langid",
]
@@ -1133,14 +1040,14 @@
"i18n-config",
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
name = "iana-time-zone"
-version = "0.1.59"
+version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -1161,9 +1068,9 @@
[[package]]
name = "indexmap"
-version = "2.1.0"
+version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
@@ -1171,9 +1078,9 @@
[[package]]
name = "indicatif"
-version = "0.17.7"
+version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
+checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3"
dependencies = [
"console",
"instant",
@@ -1229,26 +1136,26 @@
[[package]]
name = "is-terminal"
-version = "0.4.10"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
+checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
dependencies = [
- "hermit-abi 0.3.3",
- "rustix",
+ "hermit-abi",
+ "libc",
"windows-sys 0.52.0",
]
[[package]]
name = "is_ci"
-version = "1.1.1"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
+checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
[[package]]
name = "itertools"
-version = "0.11.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
@@ -1261,9 +1168,9 @@
[[package]]
name = "js-sys"
-version = "0.3.66"
+version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
+checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
dependencies = [
"wasm-bindgen",
]
@@ -1279,9 +1186,9 @@
[[package]]
name = "libc"
-version = "0.2.151"
+version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libm"
@@ -1300,17 +1207,6 @@
]
[[package]]
-name = "libredox"
-version = "0.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
-dependencies = [
- "bitflags 2.4.1",
- "libc",
- "redox_syscall",
-]
-
-[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1318,9 +1214,9 @@
[[package]]
name = "linux-raw-sys"
-version = "0.4.12"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "lock_api"
@@ -1385,9 +1281,9 @@
[[package]]
name = "miniz_oxide"
-version = "0.7.1"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
@@ -1399,7 +1295,7 @@
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [
"libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
"windows-sys 0.48.0",
]
@@ -1409,7 +1305,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
"cfg-if",
"libc",
]
@@ -1460,26 +1356,31 @@
"num-integer",
"num-iter",
"num-traits",
- "rand 0.8.5",
+ "rand",
"smallvec",
"zeroize",
]
[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
name = "num-integer"
-version = "0.1.45"
+version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
- "autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
-version = "0.1.43"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
dependencies = [
"autocfg",
"num-integer",
@@ -1488,9 +1389,9 @@
[[package]]
name = "num-traits"
-version = "0.2.17"
+version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
"libm",
@@ -1502,7 +1403,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
- "hermit-abi 0.3.3",
+ "hermit-abi",
"libc",
]
@@ -1535,11 +1436,10 @@
[[package]]
name = "openssh"
-version = "0.10.2"
+version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8274f2bf1fc3785406a3ff07c92c15590c00e84efb883da77b671562ca9a6115"
+checksum = "cab71dc3fc68747816c7eecdffcede064d6bac9621fd658bf1ab5414e91558a3"
dependencies = [
- "dirs",
"libc",
"once_cell",
"shell-escape",
@@ -1548,12 +1448,6 @@
"tokio",
"tokio-pipe",
]
-
-[[package]]
-name = "option-ext"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "overload"
@@ -1563,21 +1457,19 @@
[[package]]
name = "owo-colors"
-version = "3.5.0"
+version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
dependencies = [
"supports-color",
]
[[package]]
name = "papergrid"
-version = "0.10.0"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2ccbe15f2b6db62f9a9871642746427e297b0ceb85f9a7f1ee5ff47d184d0c8"
+checksum = "9ad43c07024ef767f9160710b3a6773976194758c7919b17e63b863db0bdf7fb"
dependencies = [
- "ansi-str",
- "ansitok",
"bytecount",
"fnv",
"unicode-width",
@@ -1608,11 +1500,12 @@
[[package]]
name = "pbkdf2"
-version = "0.11.0"
+version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
- "digest 0.10.7",
+ "digest",
+ "hmac",
]
[[package]]
@@ -1644,22 +1537,22 @@
[[package]]
name = "pin-project"
-version = "1.1.3"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
+checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
-version = "1.1.3"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
+checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
@@ -1676,27 +1569,32 @@
[[package]]
name = "pkcs1"
-version = "0.4.1"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719"
+checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
- "zeroize",
]
[[package]]
name = "pkcs8"
-version = "0.9.0"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]]
+name = "platforms"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
+
+[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1763,9 +1661,9 @@
[[package]]
name = "proc-macro2"
-version = "1.0.75"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
@@ -1788,19 +1686,6 @@
"log",
"parking_lot",
"scheduled-thread-pool",
-]
-
-[[package]]
-name = "rand"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
-dependencies = [
- "getrandom 0.1.16",
- "libc",
- "rand_chacha 0.2.2",
- "rand_core 0.5.1",
- "rand_hc",
]
[[package]]
@@ -1810,18 +1695,8 @@
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
- "rand_chacha 0.3.1",
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
-dependencies = [
- "ppv-lite86",
- "rand_core 0.5.1",
+ "rand_chacha",
+ "rand_core",
]
[[package]]
@@ -1831,16 +1706,7 @@
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
-dependencies = [
- "getrandom 0.1.16",
+ "rand_core",
]
[[package]]
@@ -1848,17 +1714,8 @@
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom 0.2.11",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
- "rand_core 0.5.1",
+ "getrandom",
]
[[package]]
@@ -1868,28 +1725,17 @@
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
-dependencies = [
- "getrandom 0.2.11",
- "libredox",
- "thiserror",
]
[[package]]
name = "regex"
-version = "1.10.2"
+version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata 0.4.3",
+ "regex-automata 0.4.5",
"regex-syntax 0.8.2",
]
@@ -1904,9 +1750,9 @@
[[package]]
name = "regex-automata"
-version = "0.4.3"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
dependencies = [
"aho-corasick",
"memchr",
@@ -1950,8 +1796,8 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
dependencies = [
- "base64 0.21.5",
- "bitflags 2.4.1",
+ "base64",
+ "bitflags 2.4.2",
"serde",
"serde_derive",
]
@@ -1971,30 +1817,29 @@
[[package]]
name = "rsa"
-version = "0.7.2"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c"
+checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
dependencies = [
- "byteorder",
- "digest 0.10.7",
+ "const-oid",
+ "digest",
"num-bigint-dig",
"num-integer",
- "num-iter",
"num-traits",
"pkcs1",
"pkcs8",
- "rand_core 0.6.4",
+ "rand_core",
"signature",
- "smallvec",
+ "spki",
"subtle",
"zeroize",
]
[[package]]
name = "rust-embed"
-version = "6.8.1"
+version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661"
+checksum = "a82c0bbc10308ed323529fd3c1dce8badda635aa319a5ff0e6466f33b8101e3f"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
@@ -2003,22 +1848,22 @@
[[package]]
name = "rust-embed-impl"
-version = "6.8.1"
+version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac"
+checksum = "6227c01b1783cdfee1bcf844eb44594cd16ec71c35305bf1c9fb5aade2735e16"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
- "syn 2.0.48",
+ "syn 2.0.49",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
-version = "7.8.1"
+version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74"
+checksum = "8cb0a25bfbb2d4b4402179c2cf030387d9990857ce08a32592c6238db9fa8665"
dependencies = [
"sha2",
"walkdir",
@@ -2037,12 +1882,21 @@
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
name = "rustix"
-version = "0.38.28"
+version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
+checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
"errno",
"libc",
"linux-raw-sys",
@@ -2090,11 +1944,10 @@
[[package]]
name = "scrypt"
-version = "0.10.0"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d"
+checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
dependencies = [
- "hmac",
"pbkdf2",
"salsa20",
"sha2",
@@ -2125,10 +1978,16 @@
checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
[[package]]
+name = "semver"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
+
+[[package]]
name = "serde"
-version = "1.0.193"
+version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
+checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
@@ -2144,20 +2003,20 @@
[[package]]
name = "serde_derive"
-version = "1.0.193"
+version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
+checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
name = "serde_json"
-version = "1.0.108"
+version = "1.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
dependencies = [
"itoa",
"ryu",
@@ -2181,7 +2040,7 @@
dependencies = [
"cfg-if",
"cpufeatures",
- "digest 0.10.7",
+ "digest",
]
[[package]]
@@ -2201,9 +2060,9 @@
[[package]]
name = "shlex"
-version = "1.2.0"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
@@ -2216,12 +2075,12 @@
[[package]]
name = "signature"
-version = "1.6.4"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
- "digest 0.10.7",
- "rand_core 0.6.4",
+ "digest",
+ "rand_core",
]
[[package]]
@@ -2235,9 +2094,9 @@
[[package]]
name = "smallvec"
-version = "1.11.2"
+version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
+checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "smol_str"
@@ -2266,9 +2125,9 @@
[[package]]
name = "spki"
-version = "0.6.0"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
@@ -2281,6 +2140,12 @@
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
+name = "strsim"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
+
+[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2288,11 +2153,11 @@
[[package]]
name = "supports-color"
-version = "1.3.1"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f"
+checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89"
dependencies = [
- "atty",
+ "is-terminal",
"is_ci",
]
@@ -2309,9 +2174,9 @@
[[package]]
name = "syn"
-version = "2.0.48"
+version = "2.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
dependencies = [
"proc-macro2",
"quote",
@@ -2320,12 +2185,10 @@
[[package]]
name = "tabled"
-version = "0.14.0"
+version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfe9c3632da101aba5131ed63f9eed38665f8b3c68703a6bb18124835c1a5d22"
+checksum = "4c998b0c8b921495196a48aabaf1901ff28be0760136e31604f7967b0792050e"
dependencies = [
- "ansi-str",
- "ansitok",
"papergrid",
"tabled_derive",
"unicode-width",
@@ -2333,9 +2196,9 @@
[[package]]
name = "tabled_derive"
-version = "0.6.0"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4"
+checksum = "4c138f99377e5d653a371cdad263615634cfc8467685dfe8e73e2b8e98f44b17"
dependencies = [
"heck",
"proc-macro-error",
@@ -2346,27 +2209,17 @@
[[package]]
name = "tempfile"
-version = "3.9.0"
+version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
+checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67"
dependencies = [
"cfg-if",
"fastrand",
- "redox_syscall",
"rustix",
"windows-sys 0.52.0",
]
[[package]]
-name = "termcolor"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
name = "terminal_size"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2384,22 +2237,22 @@
[[package]]
name = "thiserror"
-version = "1.0.53"
+version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09"
+checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.53"
+version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19"
+checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
@@ -2414,11 +2267,12 @@
[[package]]
name = "time"
-version = "0.3.31"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
+checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
dependencies = [
"deranged",
+ "num-conv",
"powerfmt",
"serde",
"time-core",
@@ -2433,10 +2287,11 @@
[[package]]
name = "time-macros"
-version = "0.2.16"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
+checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
dependencies = [
+ "num-conv",
"time-core",
]
@@ -2451,9 +2306,9 @@
[[package]]
name = "tokio"
-version = "1.35.1"
+version = "1.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
+checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
dependencies = [
"backtrace",
"bytes",
@@ -2475,7 +2330,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
@@ -2513,9 +2368,9 @@
[[package]]
name = "toml"
-version = "0.8.8"
+version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
+checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290"
dependencies = [
"serde",
"serde_spanned",
@@ -2534,9 +2389,9 @@
[[package]]
name = "toml_edit"
-version = "0.21.0"
+version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
+checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6"
dependencies = [
"indexmap",
"serde",
@@ -2564,7 +2419,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
[[package]]
@@ -2716,18 +2571,7 @@
"itoa",
"log",
"unicode-width",
- "vte 0.11.1",
-]
-
-[[package]]
-name = "vte"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
-dependencies = [
- "arrayvec 0.5.2",
- "utf8parse",
- "vte_generate_state_changes",
+ "vte",
]
[[package]]
@@ -2736,7 +2580,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
dependencies = [
- "arrayvec 0.7.4",
+ "arrayvec",
"utf8parse",
"vte_generate_state_changes",
]
@@ -2763,21 +2607,15 @@
[[package]]
name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
-
-[[package]]
-name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.89"
+version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
+checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -2785,24 +2623,24 @@
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.89"
+version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
+checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.89"
+version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
+checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2810,22 +2648,22 @@
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.89"
+version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
+checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.89"
+version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
[[package]]
name = "winapi"
@@ -2869,15 +2707,6 @@
[[package]]
name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
-[[package]]
-name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
@@ -2892,21 +2721,6 @@
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
]
[[package]]
@@ -2938,12 +2752,6 @@
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
@@ -2959,12 +2767,6 @@
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
-[[package]]
-name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
@@ -2974,12 +2776,6 @@
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
@@ -2992,12 +2788,6 @@
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
@@ -3010,12 +2800,6 @@
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
@@ -3028,12 +2812,6 @@
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -3046,12 +2824,6 @@
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
@@ -3067,21 +2839,22 @@
[[package]]
name = "winnow"
-version = "0.5.31"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c"
+checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401"
dependencies = [
"memchr",
]
[[package]]
name = "x25519-dalek"
-version = "1.1.1"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
+checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
dependencies = [
"curve25519-dalek",
- "rand_core 0.5.1",
+ "rand_core",
+ "serde",
"zeroize",
]
@@ -3108,5 +2881,5 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.48",
+ "syn 2.0.49",
]
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,5 +6,5 @@
nixlike = { path = "./crates/nixlike" }
better-command = { path = "./crates/better-command" }
bifrostlink = "0.1.0"
-uuid = { version = "1.3.3", features = ["v4"] }
-tokio = { version = "1.33.0", features = ["fs", "rt", "macros", "sync", "time", "rt-multi-thread"] }
+uuid = { version = "1.7.0", features = ["v4"] }
+tokio = { version = "1.36.0", features = ["fs", "rt", "macros", "sync", "time", "rt-multi-thread"] }
README.adocdiffbeforeafterboth--- a/README.adoc
+++ b/README.adoc
@@ -11,3 +11,164 @@
- Modules can configure multiple hosts at once (I.e for wireguard/kubernetes installation)
- Secrets can be securely stored in Git (No one except target hosts can decrypt them), automatically regenerated, reencrypted, etc.
- Automatic rollback on deployment failure, which will work, as long as system is passing initrd stage (So still be carefull with root filesystem mount)
+
+== Secret generator example
+
+TODO:: This section should into some kind of fleet documentation... But as there is none, it is just left here as-is.
+
+=== Quickly run securely setup gitlab
+
+[source,nix]
+----
+{config, ...}: {
+ secrets = let ownership = { owner = "gitlab"; group = "gitlab"; }; in {
+ gitlab-initial-root = {
+ generator = {mkPassword}: mkPassword {};
+ } // ownership;
+ gitlab-secret = {
+ generator = {mkPassword}: mkPassword {};
+ } // ownership;
+ gitlab-otp = {
+ generator = {mkPassword}: mkPassword {};
+ } // ownership;
+ gitlab-db = {
+ generator = {mkPassword}: mkPassword {};
+ } // ownership;
+ gitlab-jws = {
+ generator = {mkRsa}: mkRsa {};
+ } // ownership;
+ };
+ services.gitlab = let secrets = config.secrets; in {
+ enable = true;
+ initialRootPasswordFile = secrets.gitlab-initial-root.secretPath;
+ secrets = {
+ secretFile = secrets.gitlab-secret.secretPath;
+ otpFile = secrets.gitlab-otp.secretPath;
+ dbFile = secrets.gitlab-db.secretPath;
+ jwsFile = secrets.gitlab-jws.secretPath;
+ };
+ };
+}
+----
+
+=== Securely initialize kubernetes secrets
+
+In my homelab and clusters, I almost always have some sort of HSM, and to issue new kubernetes certs I directly connect to it.
+This setup should probably split into multiple steps, where I allow target machine to generate CSR, then copy it to the HSM machine, and then sign it there... But this is just the plan.
+I want to build ansible-like script execution in fleet for this kind of tasks.
+
+[source,nix]
+----
+{...}: {
+ # First I define required secret generators:
+ nixpkgs.overlays = [
+ (final: prev: let
+ lib = final.lib;
+ in {
+ readKubernetesCa = {impureOn}:
+ final.mkImpureSecretGenerator ''
+ cd ~/ca
+
+ cert=kubernetes-intermediateCA.crt
+
+ expires_at=$(openssl x509 -in $cert -noout -enddate | cut -d= -f2 | xargs -I{} date -u -d {} +"%Y-%m-%dT%H:%M:%S.%NZ")
+ echo -n $expires_at > $out/expires_at
+
+ cat $cert > $out/public
+ ''
+ impureOn;
+ mkKubernetesCert = {
+ subj,
+ sans ? [],
+ impureOn,
+ }:
+ final.mkImpureSecretGenerator ''
+ cd ~/ca
+
+ params=$(sudo mktemp)
+ csr=$(sudo mktemp)
+ cert=$(sudo mktemp)
+ sudo openssl ecparam -genkey -name secp384r1 -out $params
+ sudo openssl req -new -key $params \
+ -subj "${lib.strings.concatStringsSep "" (lib.attrsets.mapAttrsToList (k: v: "/${k}=${v}") subj)}" \
+ ${lib.optionalString (sans != []) "-addext \"subjectAltName = ${lib.strings.concatStringsSep "," sans}\""} \
+ -out $csr
+ sudo hsms x509 -req -days 365 -in $csr -CA kubernetes-intermediateCA.crt -CAkey "pkcs11:object=[CENSORED] Kubernetes Intermediate CA;type=private" -CAcreateserial -copy_extensions copy -out $cert
+
+ expires_at=$(sudo openssl x509 -in $cert -noout -enddate | cut -d= -f2 | xargs -I{} date -u -d {} +"%Y-%m-%dT%H:%M:%S.%NZ")
+ echo -n $expires_at > $out/expires_at
+
+ sudo cat $params | encrypt > $out/secret
+ sudo cat $cert > $out/public
+ ''
+ impureOn;
+ })
+ ];
+ # Those secret generators are impure, thus they are run in system environment.
+ # Probably there needs to be a dedicated user for that kind of tasks, but this is my current setup, don't judge.
+ # I write a couple of scripts for executing openssl with HSM.
+ environment.systemPackages = [
+ pkgs.openssl.bin
+ (pkgs.writeShellApplication {
+ name = "hsms";
+ text = ''
+ set -eu
+ export OPENSSL_CONF=${openssl-conf}
+ # Yay, using secrets to generate secrets!
+ HSM_PIN=$(cat ${config.secrets.hsm-pin.secretPath})
+ exec ${pkgs.openssl}/bin/openssl "$@" -keyform=engine -CAkeyform=engine -engine=pkcs11 -passin=pass:"$HSM_PIN"
+ '';
+ })
+ (pkgs.writeShellApplication {
+ name = "hsmt";
+ text = ''
+ set -eu
+ HSM_PIN=$(cat ${config.secrets.hsm-pin.secretPath})
+ exec ${pkgs.opensc}/bin/pkcs11-tool -l --pin="$HSM_PIN" "$@"
+ '';
+ })
+ ];
+ # And finally, I have secrets, which are shared between machines.
+ # Note that this example is somewhat wrong, as this goes not into the machine configuration, but to fleet configuration.
+ sharedSecrets = {
+ "ca.pem" = {
+ # This is just the public key, no need to regenerate it to change owner list
+ regenerateOnOwnerAdded = false;
+ # For secret regeneration/reencryption, we need to specify which machines SHOULD have it.
+ expectedOwners = ["controlplane-1" "controlplane-2" "worker-1" "worker-2"];
+ generator = {readKubernetesCa}:
+ readKubernetesCa {
+ impureOn = "[CENSORED]";
+ };
+ };
+ "kube-admin.pem" = {
+ regenerateOnOwnerAdded = false;
+ expectedOwners = ["cluster-admin"];
+ generator = {mkKubernetesCert}:
+ mkKubernetesCert {
+ subj = {
+ CN = "admin";
+ O = "system:masters";
+ };
+ impureOn = "[CENSORED]";
+ };
+ };
+ "kube-apiserver.pem" = {
+ # This secret depends on machine SANS, so if owner list has been changed, then we need to regenerate it.
+ # However, SANS dependency is in fact handled by secret seed, and secret is regenerated if the seed is changed...
+ #
+ # In this case regeneration is added as a half-assed security measure, as if apiserver is removed, we don't
+ # want for it to be able to pretend like it is a valid server.
+ #
+ # However, certificate revokation is complicated in my setup, and I can't show it here.
+ regenerateOnOwnerAdded = true;
+ expectedOwners = ["controlplane-1" "controlplane-2"];
+ generator = {mkKubernetesCert}:
+ mkKubernetesCert {
+ inherit sans;
+ subj.CN = "kubernetes";
+ impureOn = "[CENSORED]";
+ };
+ };
+}
+----
cmds/fleet/Cargo.tomldiffbeforeafterboth--- a/cmds/fleet/Cargo.toml
+++ b/cmds/fleet/Cargo.toml
@@ -12,17 +12,17 @@
anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
-time = { version = "0.3.30", features = ["serde"] }
-tempfile = "3.8"
-once_cell = "1.18.0"
-hostname = "0.3.1"
-age-core = "0.9.0"
-peg = "0.8.2"
-age = { version = "0.9.2", features = ["ssh", "armor"] }
-base64 = "0.21.5"
-chrono = { version = "0.4.31", features = ["serde"] }
-z85 = "3.0.5"
-clap = { version = "4.4.7", features = [
+time = { version = "0.3", features = ["serde"] }
+tempfile = "3.10"
+once_cell = "1.19"
+hostname = "0.3"
+age-core = "0.10"
+peg = "0.8"
+age = { version = "0.10", features = ["ssh", "armor"] }
+base64 = "0.21"
+chrono = { version = "0.4", features = ["serde"] }
+z85 = "3.0"
+clap = { version = "4.5", features = [
"derive",
"env",
"wrap_help",
@@ -30,18 +30,18 @@
] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
-tokio-util = { version = "0.7.10", features = ["codec"] }
-async-trait = "0.1.74"
-futures = "0.3.29"
-tracing-indicatif = "0.3.5"
-indicatif = "0.17.7"
-itertools = "0.11.0"
-shlex = "1.2.0"
-tabled = { version = "0.14.0", features = ["color"] }
-owo-colors = { version = "3.5.0", features = ["supports-color", "supports-colors"] }
+tokio-util = { version = "0.7", features = ["codec"] }
+async-trait = "0.1"
+futures = "0.3"
+tracing-indicatif = "0.3"
+indicatif = "0.17"
+itertools = "0.12"
+shlex = "1.3"
+tabled = { version = "0.15" }
+owo-colors = { version = "4.0", features = ["supports-color", "supports-colors"] }
r2d2 = "0.8.10"
-abort-on-drop = "0.2.2"
-unindent = "0.2.3"
-regex = "1.10.2"
-openssh = "0.10.1"
-human-repr = "1.1.0"
+abort-on-drop = "0.2"
+unindent = "0.2"
+regex = "1.10"
+openssh = "0.10"
+human-repr = "1.1"
cmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth1use crate::{2 better_nix_eval::Field,3 fleetdata::{FleetSecret, FleetSharedSecret, SecretData},4 host::Config,5 nix_go, nix_go_json,6};7use anyhow::{anyhow, bail, ensure, Context, Result};8use chrono::{DateTime, Utc};9use clap::Parser;10use owo_colors::OwoColorize;11use serde::Deserialize;12use std::{13 collections::{BTreeSet, HashSet},14 io::{self, Cursor, Read},15 path::PathBuf,16};17use tabled::{Table, Tabled};18use tokio::fs::read_to_string;19use tracing::{error, info, info_span, warn, Instrument};2021#[derive(Parser)]22pub enum Secret {23 /// Force load host keys for all defined hosts24 ForceKeys,25 /// Add secret, data should be provided in stdin26 AddShared {27 /// Secret name28 name: String,29 /// Secret owners30 machines: Vec<String>,31 /// Override secret if already present32 #[clap(long)]33 force: bool,34 /// Secret public part35 #[clap(long)]36 public: Option<String>,37 /// Load public part from specified file38 #[clap(long)]39 public_file: Option<PathBuf>,4041 /// Create a notification on secret expiration42 #[clap(long)]43 expires_at: Option<DateTime<Utc>>,4445 /// Secret with this name already exists, override its value while keeping the same owners.46 #[clap(long)]47 re_add: bool,48 },49 /// Add secret, data should be provided in stdin50 Add {51 /// Secret name52 name: String,53 /// Secret owners54 machine: String,55 /// Override secret if already present56 #[clap(long)]57 force: bool,58 #[clap(long)]59 public: Option<String>,60 #[clap(long)]61 public_file: Option<PathBuf>,62 },63 /// Read secret from remote host, requires sudo on said host64 Read {65 name: String,66 machine: String,67 #[clap(long)]68 plaintext: bool,69 },70 UpdateShared {71 name: String,7273 #[clap(long)]74 machines: Option<Vec<String>>,7576 #[clap(long)]77 add_machines: Vec<String>,78 #[clap(long)]79 remove_machines: Vec<String>,8081 /// Which host should we use to decrypt82 #[clap(long)]83 prefer_identities: Vec<String>,84 },85 Regenerate {86 /// Which host should we use to decrypt, in case if reencryption is required, without87 /// regeneration88 #[clap(long)]89 prefer_identities: Vec<String>,90 },91 List {},92}9394#[tracing::instrument(skip(config, secret, field, prefer_identities))]95async fn update_owner_set(96 secret_name: &str,97 config: &Config,98 mut secret: FleetSharedSecret,99 field: Field,100 updated_set: &[String],101 prefer_identities: &[String],102) -> Result<FleetSharedSecret> {103 let original_set = secret.owners.clone();104105 let set = original_set.iter().collect::<BTreeSet<_>>();106 let expected_set = updated_set.iter().collect::<BTreeSet<_>>();107108 if set == expected_set {109 info!("no need to update owner list, it is already correct");110 return Ok(secret);111 }112113 let should_regenerate = if set.difference(&expected_set).next().is_some() {114 // TODO: Remove this warning for revokable secrets.115 warn!("host was removed from secret owners, but until this host rebuild, the secret will still be stored on it.");116 nix_go_json!(field.regenerateOnOwnerRemoved)117 } else if expected_set.difference(&set).next().is_some() {118 nix_go_json!(field.regenerateOnOwnerAdded)119 } else {120 false121 };122123 if should_regenerate {124 info!("secret is owner-dependent, will regenerate");125 let generated = generate_shared(config, secret_name, field, updated_set.to_vec()).await?;126 Ok(generated)127 } else {128 let identity_holder = if !prefer_identities.is_empty() {129 prefer_identities130 .iter()131 .find(|i| original_set.iter().any(|s| s == *i))132 } else {133 secret.owners.first()134 };135 let Some(identity_holder) = identity_holder else {136 bail!("no available holder found");137 };138139 if let Some(data) = secret.secret.secret {140 let host = config.host(identity_holder).await?;141 let encrypted = host.reencrypt(data, updated_set.to_vec()).await?;142 secret.secret.secret = Some(encrypted);143 }144145 secret.owners = updated_set.to_vec();146 Ok(secret)147 }148}149150#[derive(Deserialize)]151#[serde(rename_all = "camelCase")]152enum GeneratorKind {153 Impure,154}155156async fn generate_impure(157 config: &Config,158 _display_name: &str,159 secret: Field,160 default_generator: Field,161 owners: &[String],162) -> Result<FleetSecret> {163 let config_field = &config.config_unchecked_field;164 let generator = nix_go!(secret.generator);165166 let on: String = nix_go_json!(default_generator.impureOn);167 let call_package = nix_go!(168 config_field.hosts[{ on }]169 .nixosSystem170 .config171 .nixpkgs172 .resolvedPkgs173 .callPackage174 );175176 let host = config.host(&on).await?;177178 let generator = nix_go!(call_package(generator)(Obj {}));179 let generator = generator.build().await?;180 let generator = generator181 .get("out")182 .ok_or_else(|| anyhow!("missing generateImpure out"))?;183 let generator = host.remote_derivation(generator).await?;184185 let mut recipients = String::new();186 for owner in owners {187 let key = config.key(owner).await?;188 recipients.push_str(&format!("-r \"{key}\" "));189 }190 recipients.push_str("-e");191192 let out = host.mktemp_dir().await?;193194 let mut gen = host.cmd(generator).await?;195 gen.env("rageArgs", recipients).env("out", &out);196 gen.run().await.context("impure generator")?;197198 {199 let marker = host.read_file_text(format!("{out}/marker")).await?;200 ensure!(marker == "SUCCESS", "generation not succeeded");201 }202203 let public = host.read_file_text(format!("{out}/public")).await.ok();204 let secret = host.read_file_bin(format!("{out}/secret")).await.ok();205 if let Some(secret) = &secret {206 ensure!(207 age::Decryptor::new(Cursor::new(&secret)).is_ok(),208 "builder produced non-encrypted value as secret, this is highly insecure, and not allowed."209 );210 }211212 let created_at = host.read_file_value(format!("{out}/created_at")).await?;213 let expires_at = host.read_file_value(format!("{out}/expires_at")).await.ok();214215 Ok(FleetSecret {216 created_at,217 expires_at,218 public,219 secret: secret.map(SecretData),220 })221}222async fn generate(223 config: &Config,224 display_name: &str,225 secret: Field,226 owners: &[String],227) -> Result<FleetSecret> {228 let generator = nix_go!(secret.generator);229 // Can't properly check on nix module system level230 {231 let gen_ty = generator.type_of().await?;232 if gen_ty == "null" {233 bail!("secret has no generator defined, can't automatically generate it.");234 }235 if gen_ty != "lambda" {236 bail!("generator should be lambda, got {gen_ty}");237 }238 }239 let default_pkgs = &config.default_pkgs;240 let default_call_package = nix_go!(default_pkgs.callPackage);241 // Generators provide additional information in passthru, to access242 // passthru we should call generator, but information about where this generator is supposed to build243 // is located in passthru... Thus evaluating generator on host.244 //245 // Maybe it is also possible to do some magic with __functor?246 //247 // I don't want to make modules always responsible for additional secret data anyway,248 // so it should be in derivation, and not in the secret data itself.249 let default_generator = nix_go!(default_call_package(generator)(Obj {}));250251 let kind: GeneratorKind = nix_go_json!(default_generator.generatorKind);252253 match kind {254 GeneratorKind::Impure => {255 generate_impure(config, display_name, secret, default_generator, owners).await256 }257 }258}259async fn generate_shared(260 config: &Config,261 display_name: &str,262 secret: Field,263 expected_owners: Vec<String>,264) -> Result<FleetSharedSecret> {265 // let owners: Vec<String> = nix_go_json!(secret.expectedOwners);266 Ok(FleetSharedSecret {267 secret: generate(config, display_name, secret, &expected_owners).await?,268 owners: expected_owners,269 })270}271272async fn parse_public(273 public: Option<String>,274 public_file: Option<PathBuf>,275) -> Result<Option<String>> {276 Ok(match (public, public_file) {277 (Some(v), None) => Some(v),278 (None, Some(v)) => Some(read_to_string(v).await?),279 (Some(_), Some(_)) => {280 bail!("only public or public_file should be set")281 }282 (None, None) => None,283 })284}285286fn parse_machines(287 initial: Vec<String>,288 machines: Option<Vec<String>>,289 mut add_machines: Vec<String>,290 mut remove_machines: Vec<String>,291) -> Result<Vec<String>> {292 if machines.is_none() && add_machines.is_empty() && remove_machines.is_empty() {293 bail!("no operation");294 }295296 let initial_machines = initial.clone();297 let mut target_machines = initial;298 info!("Currently encrypted for {initial_machines:?}");299300 // ensure!(machines.is_some() || !add_machines.is_empty() || )301 if let Some(machines) = machines {302 ensure!(303 add_machines.is_empty() && remove_machines.is_empty(),304 "can't combine --machines and --add-machines/--remove-machines"305 );306 let target = initial_machines.iter().collect::<HashSet<_>>();307 let source = machines.iter().collect::<HashSet<_>>();308 for removed in target.difference(&source) {309 remove_machines.push((*removed).clone());310 }311 for added in source.difference(&target) {312 add_machines.push((*added).clone());313 }314 }315316 for machine in &remove_machines {317 let mut removed = false;318 while let Some(pos) = target_machines.iter().position(|m| m == machine) {319 target_machines.swap_remove(pos);320 removed = true;321 }322 if !removed {323 warn!("secret is not enabled for {machine}");324 }325 }326 for machine in &add_machines {327 if target_machines.iter().any(|m| m == machine) {328 warn!("secret is already added to {machine}");329 } else {330 target_machines.push(machine.to_owned());331 }332 }333 if !remove_machines.is_empty() {334 // TODO: maybe force secret regeneration?335 // Not that useful without revokation.336 warn!("secret will not be regenerated for removed machines, and until host rebuild, they will still possess the ability to decode secret");337 }338 Ok(target_machines)339}340impl Secret {341 pub async fn run(self, config: &Config) -> Result<()> {342 match self {343 Secret::ForceKeys => {344 for host in config.list_hosts().await? {345 if config.should_skip(&host.name) {346 continue;347 }348 config.key(&host.name).await?;349 }350 }351 Secret::AddShared {352 mut machines,353 name,354 force,355 public,356 public_file,357 expires_at,358 re_add,359 } => {360 let exists = config.has_shared(&name);361 if exists && !force && !re_add {362 bail!("secret already defined");363 }364 if re_add {365 // Fixme: use clap to limit this usage366 ensure!(!force, "--force and --readd are not compatible");367 ensure!(exists, "secret doesn't exists");368 ensure!(369 machines.is_empty(),370 "you can't use machines argument for --readd"371 );372 let shared = config.shared_secret(&name)?;373 machines = shared.owners;374 }375376 let recipients = config.recipients(machines.clone()).await?;377378 let secret = {379 let mut input = vec![];380 io::stdin().read_to_end(&mut input)?;381382 if input.is_empty() {383 None384 } else {385 Some(386 SecretData::encrypt(recipients, input)387 .ok_or_else(|| anyhow!("no recipients provided"))?,388 )389 }390 };391 let public = parse_public(public, public_file).await?;392 config.replace_shared(393 name,394 FleetSharedSecret {395 owners: machines,396 secret: FleetSecret {397 created_at: Utc::now(),398 expires_at,399 secret,400 public,401 },402 },403 );404 }405 Secret::Add {406 machine,407 name,408 force,409 public,410 public_file,411 } => {412 let recipient = config.recipient(&machine).await?;413414 let secret = {415 let mut input = vec![];416 io::stdin().read_to_end(&mut input)?;417 if input.is_empty() {418 bail!("no data provided")419 }420421 Some(SecretData::encrypt(vec![recipient], input).expect("recipient provided"))422 };423424 if config.has_secret(&machine, &name) && !force {425 bail!("secret already defined");426 }427 let public = parse_public(public, public_file).await?;428429 config.insert_secret(430 &machine,431 name,432 FleetSecret {433 created_at: Utc::now(),434 expires_at: None,435 secret,436 public,437 },438 );439 }440 #[allow(clippy::await_holding_refcell_ref)]441 Secret::Read {442 name,443 machine,444 plaintext,445 } => {446 let secret = config.host_secret(&machine, &name)?;447 let Some(secret) = secret.secret else {448 bail!("no secret {name}");449 };450 let host = config.host(&machine).await?;451 let data = host.decrypt(secret).await?;452 if plaintext {453 let s = String::from_utf8(data).context("output is not utf8")?;454 print!("{s}");455 } else {456 println!("{}", z85::encode(&data));457 }458 }459 Secret::UpdateShared {460 name,461 machines,462 add_machines,463 remove_machines,464 prefer_identities,465 } => {466 let secret = config.shared_secret(&name)?;467 if secret.secret.secret.is_none() {468 bail!("no secret");469 }470471 let initial_machines = secret.owners.clone();472 let target_machines = parse_machines(473 initial_machines.clone(),474 machines,475 add_machines,476 remove_machines,477 )?;478479 if target_machines.is_empty() {480 info!("no machines left for secret, removing it");481 config.remove_shared(&name);482 return Ok(());483 }484485 let config_field = &config.config_unchecked_field;486 let field = nix_go!(config_field.sharedSecrets[{ name }]);487488 let updated = update_owner_set(489 &name,490 config,491 secret,492 field,493 &target_machines,494 &prefer_identities,495 )496 .await?;497 config.replace_shared(name, updated);498 }499 Secret::Regenerate { prefer_identities } => {500 info!("checking for secrets to regenerate");501 {502 let _span = info_span!("shared").entered();503 let expected_shared_set = config504 .list_configured_shared()505 .await?506 .into_iter()507 .collect::<HashSet<_>>();508 let shared_set = config.list_shared().into_iter().collect::<HashSet<_>>();509 for missing in expected_shared_set.difference(&shared_set) {510 let config_field = &config.config_unchecked_field;511 let secret = nix_go!(config_field.sharedSecrets[{ missing }]);512 let expected_owners: Option<Vec<String>> =513 nix_go_json!(secret.expectedOwners);514 let Some(expected_owners) = expected_owners else {515 // TODO: Might still need to regenerate516 continue;517 };518 info!("generating secret: {missing}");519 let shared = generate_shared(config, missing, secret, expected_owners)520 .in_current_span()521 .await?;522 config.replace_shared(missing.to_string(), shared)523 }524 }525 for host in config.list_hosts().await? {526 let _span = info_span!("host", host = host.name).entered();527 let expected_set = host528 .list_configured_secrets()529 .in_current_span()530 .await?531 .into_iter()532 .collect::<HashSet<_>>();533 let stored_set = config534 .list_secrets(&host.name)535 .into_iter()536 .collect::<HashSet<_>>();537 for missing in expected_set.difference(&stored_set) {538 info!("generating secret: {missing}");539 let secret = host.secret_field(missing).in_current_span().await?;540 let generated =541 match generate(config, missing, secret, &[host.name.clone()])542 .in_current_span()543 .await544 {545 Ok(v) => v,546 Err(e) => {547 error!("{e}");548 continue;549 }550 };551 config.insert_secret(&host.name, missing.to_string(), generated)552 }553 }554 let mut to_remove = Vec::new();555 for name in &config.list_shared() {556 info!("updating secret: {name}");557 let data = config.shared_secret(name)?;558 let config_field = &config.config_unchecked_field;559 let expected_owners: Vec<String> =560 nix_go_json!(config_field.sharedSecrets[{ name }].expectedOwners);561 if expected_owners.is_empty() {562 warn!("secret was removed from fleet config: {name}, removing from data");563 to_remove.push(name.to_string());564 continue;565 }566567 let secret = nix_go!(config_field.sharedSecrets[{ name }]);568 config.replace_shared(569 name.to_owned(),570 update_owner_set(571 name,572 config,573 data,574 secret,575 &expected_owners,576 &prefer_identities,577 )578 .await?,579 );580 }581 for k in to_remove {582 config.remove_shared(&k);583 }584 }585 Secret::List {} => {586 let _span = info_span!("loading secrets").entered();587 let configured = config.list_configured_shared().await?;588 #[derive(Tabled)]589 struct SecretDisplay {590 #[tabled(rename = "Name")]591 name: String,592 #[tabled(rename = "Owners")]593 owners: String,594 }595 let mut table = vec![];596 for name in configured.iter().cloned() {597 let config = config.clone();598 let expected_owners = config.shared_secret_expected_owners(&name).await?;599 let data = config.shared_secret(&name)?;600 let owners = data601 .owners602 .iter()603 .map(|o| {604 if expected_owners.contains(o) {605 o.green().to_string()606 } else {607 o.red().to_string()608 }609 })610 .collect::<Vec<_>>();611 table.push(SecretDisplay {612 owners: owners.join(", "),613 name,614 })615 }616 info!("loaded\n{}", Table::new(table).to_string())617 }618 }619 Ok(())620 }621}1use crate::{2 better_nix_eval::Field,3 command::MyCommand,4 fleetdata::{FleetSecret, FleetSharedSecret, SecretData},5 host::Config,6 nix_go, nix_go_json,7};8use anyhow::{anyhow, bail, ensure, Context, Result};9use chrono::{DateTime, Utc};10use clap::Parser;11use owo_colors::OwoColorize;12use serde::{de::DeserializeOwned, Deserialize};13use std::{14 collections::{BTreeSet, HashSet},15 io::{self, Cursor, Read},16 path::{Path, PathBuf},17 str::FromStr,18};19use tabled::{Table, Tabled};20use tempfile::tempdir;21use tokio::fs::{self, read_to_string};22use tracing::{error, info, info_span, warn, Instrument};2324#[derive(Parser)]25pub enum Secret {26 /// Force load host keys for all defined hosts27 ForceKeys,28 /// Add secret, data should be provided in stdin29 AddShared {30 /// Secret name31 name: String,32 /// Secret owners33 machines: Vec<String>,34 /// Override secret if already present35 #[clap(long)]36 force: bool,37 /// Secret public part38 #[clap(long)]39 public: Option<String>,40 /// Load public part from specified file41 #[clap(long)]42 public_file: Option<PathBuf>,4344 /// Create a notification on secret expiration45 #[clap(long)]46 expires_at: Option<DateTime<Utc>>,4748 /// Secret with this name already exists, override its value while keeping the same owners.49 #[clap(long)]50 re_add: bool,51 },52 /// Add secret, data should be provided in stdin53 Add {54 /// Secret name55 name: String,56 /// Secret owners57 machine: String,58 /// Override secret if already present59 #[clap(long)]60 force: bool,61 #[clap(long)]62 public: Option<String>,63 #[clap(long)]64 public_file: Option<PathBuf>,65 },66 /// Read secret from remote host, requires sudo on said host67 Read {68 name: String,69 machine: String,70 #[clap(long)]71 plaintext: bool,72 },73 ReadPublic {74 name: String,75 machine: String,76 },77 UpdateShared {78 name: String,7980 #[clap(long)]81 machines: Option<Vec<String>>,8283 #[clap(long)]84 add_machines: Vec<String>,85 #[clap(long)]86 remove_machines: Vec<String>,8788 /// Which host should we use to decrypt89 #[clap(long)]90 prefer_identities: Vec<String>,91 },92 Regenerate {93 /// Which host should we use to decrypt, in case if reencryption is required, without94 /// regeneration95 #[clap(long)]96 prefer_identities: Vec<String>,97 },98 List {},99}100101#[tracing::instrument(skip(config, secret, field, prefer_identities))]102async fn update_owner_set(103 secret_name: &str,104 config: &Config,105 mut secret: FleetSharedSecret,106 field: Field,107 updated_set: &[String],108 prefer_identities: &[String],109) -> Result<FleetSharedSecret> {110 let original_set = secret.owners.clone();111112 let set = original_set.iter().collect::<BTreeSet<_>>();113 let expected_set = updated_set.iter().collect::<BTreeSet<_>>();114115 if set == expected_set {116 info!("no need to update owner list, it is already correct");117 return Ok(secret);118 }119120 let should_regenerate = if set.difference(&expected_set).next().is_some() {121 // TODO: Remove this warning for revokable secrets.122 warn!("host was removed from secret owners, but until this host rebuild, the secret will still be stored on it.");123 nix_go_json!(field.regenerateOnOwnerRemoved)124 } else if expected_set.difference(&set).next().is_some() {125 nix_go_json!(field.regenerateOnOwnerAdded)126 } else {127 false128 };129130 if should_regenerate {131 info!("secret is owner-dependent, will regenerate");132 let generated = generate_shared(config, secret_name, field, updated_set.to_vec()).await?;133 Ok(generated)134 } else {135 let identity_holder = if !prefer_identities.is_empty() {136 prefer_identities137 .iter()138 .find(|i| original_set.iter().any(|s| s == *i))139 } else {140 secret.owners.first()141 };142 let Some(identity_holder) = identity_holder else {143 bail!("no available holder found");144 };145146 if let Some(data) = secret.secret.secret {147 let host = config.host(identity_holder).await?;148 let encrypted = host.reencrypt(data, updated_set.to_vec()).await?;149 secret.secret.secret = Some(encrypted);150 }151152 secret.owners = updated_set.to_vec();153 Ok(secret)154 }155}156157#[derive(Deserialize)]158#[serde(rename_all = "camelCase")]159enum GeneratorKind {160 Impure,161 Pure,162}163164async fn generate_pure(165 config: &Config,166 _display_name: &str,167 secret: Field,168 default_generator: Field,169 owners: &[String],170) -> Result<FleetSecret> {171 // TODO: pure secrets are supposed to be generated by nix daemon itself,172 // inside of a sandbox... But we aren't here yet.173 let config_field = &config.config_unchecked_field;174 let generator = nix_go!(secret.generator);175 let default_pkgs = &config.default_pkgs;176177 let call_package = nix_go!(default_pkgs.callPackage);178179 let generator = nix_go!(call_package(generator)(Obj {}));180 let generator = generator.build().await?;181 let generator = generator182 .get("out")183 .ok_or_else(|| anyhow!("missing generate out"))?;184185 let mut recipients = String::new();186 for owner in owners {187 let key = config.key(owner).await?;188 recipients.push_str(&format!("-r \"{key}\" "));189 }190 recipients.push_str("-e");191192 let out = tempdir()?;193194 let mut gen = MyCommand::new(generator);195 gen.env("rageArgs", recipients);196 gen.env(197 "out",198 out.path().to_str().expect("sane tempdir should be utf-8"),199 );200 gen.run().await.context("impure generator")?;201202 {203 let mut marker_path = out.path().to_owned();204 marker_path.push("marker");205 let marker = fs::read_to_string(&marker_path).await?;206 ensure!(marker == "SUCCESS", "generation not succeeded");207 }208209 let mut public_path = out.path().to_owned();210 public_path.push("public");211 let mut secret_path = out.path().to_owned();212 secret_path.push("secret");213 let public = fs::read_to_string(&public_path).await.ok();214 let secret = fs::read(&secret_path).await.ok();215 if let Some(secret) = &secret {216 ensure!(217 age::Decryptor::new(Cursor::new(&secret)).is_ok(),218 "builder produced non-encrypted value as secret, this is highly insecure, and not allowed."219 );220 }221222 let mut created_at_path = out.path().to_owned();223 created_at_path.push("created_at");224 let mut expires_at_path = out.path().to_owned();225 expires_at_path.push("expires_at");226227 async fn read_value<T: FromStr>(path: &Path) -> Result<T> {228 dbg!(path);229 let raw = fs::read(path).await?;230 let raw = String::from_utf8(raw)?;231 raw.parse().map_err(|_| anyhow!("fromStr failed"))232 }233234 let created_at = read_value(&created_at_path).await?;235 let expires_at = read_value(&expires_at_path).await.ok();236237 Ok(FleetSecret {238 created_at,239 expires_at,240 public,241 secret: secret.map(SecretData),242 })243}244async fn generate_impure(245 config: &Config,246 _display_name: &str,247 secret: Field,248 default_generator: Field,249 owners: &[String],250) -> Result<FleetSecret> {251 let config_field = &config.config_unchecked_field;252 let generator = nix_go!(secret.generator);253254 let on: String = nix_go_json!(default_generator.impureOn);255 let call_package = nix_go!(256 config_field.hosts[{ on }]257 .nixosSystem258 .config259 .nixpkgs260 .resolvedPkgs261 .callPackage262 );263264 let host = config.host(&on).await?;265266 let generator = nix_go!(call_package(generator)(Obj {}));267 let generator = generator.build().await?;268 let generator = generator269 .get("out")270 .ok_or_else(|| anyhow!("missing generateImpure out"))?;271 let generator = host.remote_derivation(generator).await?;272273 let mut recipients = String::new();274 for owner in owners {275 let key = config.key(owner).await?;276 recipients.push_str(&format!("-r \"{key}\" "));277 }278 recipients.push_str("-e");279280 let out = host.mktemp_dir().await?;281282 let mut gen = host.cmd(generator).await?;283 gen.env("rageArgs", recipients).env("out", &out);284 gen.run().await.context("impure generator")?;285286 {287 let marker = host.read_file_text(format!("{out}/marker")).await?;288 ensure!(marker == "SUCCESS", "generation not succeeded");289 }290291 let public = host.read_file_text(format!("{out}/public")).await.ok();292 let secret = host.read_file_bin(format!("{out}/secret")).await.ok();293 if let Some(secret) = &secret {294 ensure!(295 age::Decryptor::new(Cursor::new(&secret)).is_ok(),296 "builder produced non-encrypted value as secret, this is highly insecure, and not allowed."297 );298 }299300 let created_at = host.read_file_value(format!("{out}/created_at")).await?;301 let expires_at = host.read_file_value(format!("{out}/expires_at")).await.ok();302303 Ok(FleetSecret {304 created_at,305 expires_at,306 public,307 secret: secret.map(SecretData),308 })309}310async fn generate(311 config: &Config,312 display_name: &str,313 secret: Field,314 owners: &[String],315) -> Result<FleetSecret> {316 let generator = nix_go!(secret.generator);317 // Can't properly check on nix module system level318 {319 let gen_ty = generator.type_of().await?;320 if gen_ty == "null" {321 bail!("secret has no generator defined, can't automatically generate it.");322 }323 if gen_ty != "lambda" {324 bail!("generator should be lambda, got {gen_ty}");325 }326 }327 let default_pkgs = &config.default_pkgs;328 let default_call_package = nix_go!(default_pkgs.callPackage);329 // Generators provide additional information in passthru, to access330 // passthru we should call generator, but information about where this generator is supposed to build331 // is located in passthru... Thus evaluating generator on host.332 //333 // Maybe it is also possible to do some magic with __functor?334 //335 // I don't want to make modules always responsible for additional secret data anyway,336 // so it should be in derivation, and not in the secret data itself.337 let default_generator = nix_go!(default_call_package(generator)(Obj {}));338339 let kind: GeneratorKind = nix_go_json!(default_generator.generatorKind);340341 match kind {342 GeneratorKind::Impure => {343 generate_impure(config, display_name, secret, default_generator, owners).await344 }345 GeneratorKind::Pure => {346 generate_pure(config, display_name, secret, default_generator, owners).await347 }348 }349}350async fn generate_shared(351 config: &Config,352 display_name: &str,353 secret: Field,354 expected_owners: Vec<String>,355) -> Result<FleetSharedSecret> {356 // let owners: Vec<String> = nix_go_json!(secret.expectedOwners);357 Ok(FleetSharedSecret {358 secret: generate(config, display_name, secret, &expected_owners).await?,359 owners: expected_owners,360 })361}362363async fn parse_public(364 public: Option<String>,365 public_file: Option<PathBuf>,366) -> Result<Option<String>> {367 Ok(match (public, public_file) {368 (Some(v), None) => Some(v),369 (None, Some(v)) => Some(read_to_string(v).await?),370 (Some(_), Some(_)) => {371 bail!("only public or public_file should be set")372 }373 (None, None) => None,374 })375}376377fn parse_machines(378 initial: Vec<String>,379 machines: Option<Vec<String>>,380 mut add_machines: Vec<String>,381 mut remove_machines: Vec<String>,382) -> Result<Vec<String>> {383 if machines.is_none() && add_machines.is_empty() && remove_machines.is_empty() {384 bail!("no operation");385 }386387 let initial_machines = initial.clone();388 let mut target_machines = initial;389 info!("Currently encrypted for {initial_machines:?}");390391 // ensure!(machines.is_some() || !add_machines.is_empty() || )392 if let Some(machines) = machines {393 ensure!(394 add_machines.is_empty() && remove_machines.is_empty(),395 "can't combine --machines and --add-machines/--remove-machines"396 );397 let target = initial_machines.iter().collect::<HashSet<_>>();398 let source = machines.iter().collect::<HashSet<_>>();399 for removed in target.difference(&source) {400 remove_machines.push((*removed).clone());401 }402 for added in source.difference(&target) {403 add_machines.push((*added).clone());404 }405 }406407 for machine in &remove_machines {408 let mut removed = false;409 while let Some(pos) = target_machines.iter().position(|m| m == machine) {410 target_machines.swap_remove(pos);411 removed = true;412 }413 if !removed {414 warn!("secret is not enabled for {machine}");415 }416 }417 for machine in &add_machines {418 if target_machines.iter().any(|m| m == machine) {419 warn!("secret is already added to {machine}");420 } else {421 target_machines.push(machine.to_owned());422 }423 }424 if !remove_machines.is_empty() {425 // TODO: maybe force secret regeneration?426 // Not that useful without revokation.427 warn!("secret will not be regenerated for removed machines, and until host rebuild, they will still possess the ability to decode secret");428 }429 Ok(target_machines)430}431impl Secret {432 pub async fn run(self, config: &Config) -> Result<()> {433 match self {434 Secret::ForceKeys => {435 for host in config.list_hosts().await? {436 if config.should_skip(&host.name) {437 continue;438 }439 config.key(&host.name).await?;440 }441 }442 Secret::AddShared {443 mut machines,444 name,445 force,446 public,447 public_file,448 expires_at,449 re_add,450 } => {451 // TODO: Forbid updating secrets with set expectedOwners (= not user-managed).452453 let exists = config.has_shared(&name);454 if exists && !force && !re_add {455 bail!("secret already defined");456 }457 if re_add {458 // Fixme: use clap to limit this usage459 ensure!(!force, "--force and --readd are not compatible");460 ensure!(exists, "secret doesn't exists");461 ensure!(462 machines.is_empty(),463 "you can't use machines argument for --readd"464 );465 let shared = config.shared_secret(&name)?;466 machines = shared.owners;467 }468469 let recipients = config.recipients(machines.clone()).await?;470471 let secret = {472 let mut input = vec![];473 io::stdin().read_to_end(&mut input)?;474475 if input.is_empty() {476 None477 } else {478 Some(479 SecretData::encrypt(recipients, input)480 .ok_or_else(|| anyhow!("no recipients provided"))?,481 )482 }483 };484 let public = parse_public(public, public_file).await?;485 config.replace_shared(486 name,487 FleetSharedSecret {488 owners: machines,489 secret: FleetSecret {490 created_at: Utc::now(),491 expires_at,492 secret,493 public,494 },495 },496 );497 }498 Secret::Add {499 machine,500 name,501 force,502 public,503 public_file,504 } => {505 let recipient = config.recipient(&machine).await?;506507 let secret = {508 let mut input = vec![];509 io::stdin().read_to_end(&mut input)?;510 if input.is_empty() {511 bail!("no data provided")512 }513514 Some(SecretData::encrypt(vec![recipient], input).expect("recipient provided"))515 };516517 if config.has_secret(&machine, &name) && !force {518 bail!("secret already defined");519 }520 let public = parse_public(public, public_file).await?;521522 config.insert_secret(523 &machine,524 name,525 FleetSecret {526 created_at: Utc::now(),527 expires_at: None,528 secret,529 public,530 },531 );532 }533 #[allow(clippy::await_holding_refcell_ref)]534 Secret::Read {535 name,536 machine,537 plaintext,538 } => {539 let secret = config.host_secret(&machine, &name)?;540 let Some(secret) = secret.secret else {541 bail!("no secret {name}");542 };543 let host = config.host(&machine).await?;544 let data = host.decrypt(secret).await?;545 if plaintext {546 let s = String::from_utf8(data).context("output is not utf8")?;547 print!("{s}");548 } else {549 println!("{}", z85::encode(&data));550 }551 }552 Secret::ReadPublic {553 name,554 machine,555 } => {556 let secret = config.host_secret(&machine, &name)?;557 let Some(public) = secret.public else {558 bail!("no secret {name}");559 };560 print!("{public}");561 }562 Secret::UpdateShared {563 name,564 machines,565 add_machines,566 remove_machines,567 prefer_identities,568 } => {569 // TODO: Forbid updating secrets with set expectedOwners (= not user-managed).570571 let secret = config.shared_secret(&name)?;572 if secret.secret.secret.is_none() {573 bail!("no secret");574 }575576 let initial_machines = secret.owners.clone();577 let target_machines = parse_machines(578 initial_machines.clone(),579 machines,580 add_machines,581 remove_machines,582 )?;583584 if target_machines.is_empty() {585 info!("no machines left for secret, removing it");586 config.remove_shared(&name);587 return Ok(());588 }589590 let config_field = &config.config_unchecked_field;591 let field = nix_go!(config_field.sharedSecrets[{ name }]);592593 let updated = update_owner_set(594 &name,595 config,596 secret,597 field,598 &target_machines,599 &prefer_identities,600 )601 .await?;602 config.replace_shared(name, updated);603 }604 Secret::Regenerate { prefer_identities } => {605 info!("checking for secrets to regenerate");606 {607 let _span = info_span!("shared").entered();608 let expected_shared_set = config609 .list_configured_shared()610 .await?611 .into_iter()612 .collect::<HashSet<_>>();613 let shared_set = config.list_shared().into_iter().collect::<HashSet<_>>();614 for missing in expected_shared_set.difference(&shared_set) {615 let config_field = &config.config_unchecked_field;616 let secret = nix_go!(config_field.sharedSecrets[{ missing }]);617 let expected_owners: Option<Vec<String>> =618 nix_go_json!(secret.expectedOwners);619 let Some(expected_owners) = expected_owners else {620 // TODO: Might still need to regenerate621 continue;622 };623 info!("generating secret: {missing}");624 let shared = generate_shared(config, missing, secret, expected_owners)625 .in_current_span()626 .await?;627 config.replace_shared(missing.to_string(), shared)628 }629 }630 for host in config.list_hosts().await? {631 let _span = info_span!("host", host = host.name).entered();632 let expected_set = host633 .list_configured_secrets()634 .in_current_span()635 .await?636 .into_iter()637 .collect::<HashSet<_>>();638 let stored_set = config639 .list_secrets(&host.name)640 .into_iter()641 .collect::<HashSet<_>>();642 for missing in expected_set.difference(&stored_set) {643 info!("generating secret: {missing}");644 let secret = host.secret_field(missing).in_current_span().await?;645 let generated =646 match generate(config, missing, secret, &[host.name.clone()])647 .in_current_span()648 .await649 {650 Ok(v) => v,651 Err(e) => {652 error!("{e}");653 continue;654 }655 };656 config.insert_secret(&host.name, missing.to_string(), generated)657 }658 }659 let mut to_remove = Vec::new();660 for name in &config.list_shared() {661 info!("updating secret: {name}");662 let data = config.shared_secret(name)?;663 let config_field = &config.config_unchecked_field;664 let expected_owners: Vec<String> =665 nix_go_json!(config_field.sharedSecrets[{ name }].expectedOwners);666 if expected_owners.is_empty() {667 warn!("secret was removed from fleet config: {name}, removing from data");668 to_remove.push(name.to_string());669 continue;670 }671672 let secret = nix_go!(config_field.sharedSecrets[{ name }]);673 config.replace_shared(674 name.to_owned(),675 update_owner_set(676 name,677 config,678 data,679 secret,680 &expected_owners,681 &prefer_identities,682 )683 .await?,684 );685 }686 for k in to_remove {687 config.remove_shared(&k);688 }689 }690 Secret::List {} => {691 let _span = info_span!("loading secrets").entered();692 let configured = config.list_configured_shared().await?;693 #[derive(Tabled)]694 struct SecretDisplay {695 #[tabled(rename = "Name")]696 name: String,697 #[tabled(rename = "Owners")]698 owners: String,699 }700 let mut table = vec![];701 for name in configured.iter().cloned() {702 let config = config.clone();703 let expected_owners = config.shared_secret_expected_owners(&name).await?;704 let data = config.shared_secret(&name)?;705 let owners = data706 .owners707 .iter()708 .map(|o| {709 if expected_owners.contains(o) {710 o.green().to_string()711 } else {712 o.red().to_string()713 }714 })715 .collect::<Vec<_>>();716 table.push(SecretDisplay {717 owners: owners.join(", "),718 name,719 })720 }721 info!("loaded\n{}", Table::new(table).to_string())722 }723 }724 Ok(())725 }726}cmds/fleet/src/main.rsdiffbeforeafterboth--- a/cmds/fleet/src/main.rs
+++ b/cmds/fleet/src/main.rs
@@ -11,9 +11,8 @@
mod fleetdata;
-use std::ffi::OsString;
-use std::process::exit;
use std::time::Duration;
+use std::{ffi::OsString, process::ExitCode};
use anyhow::{bail, Result};
use clap::Parser;
@@ -62,6 +61,7 @@
path.push(entry.path());
let mut status = MyCommand::new("nix");
+ status.args(&config.nix_args);
status.arg("store").arg("prefetch-file").arg(path);
status.run_nix_string().instrument(span).await?;
Ok(())
@@ -118,7 +118,11 @@
return;
};
let pos = state.pos();
- let _ = write!(writer, "{} / {}", pos.human_count_bare(), len.human_count_bare());
+ if pos > len {
+ let _ = write!(writer, "{}", pos.human_count_bare());
+ } else {
+ let _ = write!(writer, "{} / {}", pos.human_count_bare(), len.human_count_bare());
+ }
})
.with_key(
"color_start",
@@ -151,7 +155,7 @@
tracing_subscriber::fmt::layer()
.without_time()
.with_target(true)
- .with_writer(indicatif_layer.get_stderr_writer())
+ .with_writer(indicatif_layer.get_stdout_writer())
.with_filter(filter), // .withou,
)
.with(indicatif_layer)
@@ -159,12 +163,15 @@
}
#[tokio::main]
-async fn main() {
+async fn main() -> ExitCode {
setup_logging();
if let Err(e) = main_real().await {
+ // If I remove this line, the next error!() line gets eaten.
+ info!("fixme: this line gets eaten by tracing-indicatif on levels info+");
error!("{e:#}");
- exit(1);
+ return ExitCode::FAILURE;
}
+ ExitCode::SUCCESS
}
async fn main_real() -> Result<()> {
cmds/install-secrets/Cargo.tomldiffbeforeafterboth--- a/cmds/install-secrets/Cargo.toml
+++ b/cmds/install-secrets/Cargo.toml
@@ -4,18 +4,18 @@
edition = "2021"
[dependencies]
-age = { version = "0.9.2", features = ["ssh"] }
-anyhow = "1.0.75"
-env_logger = "0.10.0"
-log = "0.4.20"
+age = { version = "0.10.0", features = ["ssh"] }
+anyhow = "1.0.79"
+tracing-subscriber = "0.3"
+tracing = "0.1"
nix = {version = "0.27.1", features = ["user", "fs"]}
-serde = { version = "1.0.190", features = ["derive"] }
-serde_json = "1.0.107"
-clap = { version = "4.4.7", features = [
+serde = { version = "1.0.196", features = ["derive"] }
+serde_json = "1.0.113"
+clap = { version = "4.5.1", features = [
"derive",
"env",
"wrap_help",
"unicode",
] }
-tempfile = "3.8.1"
+tempfile = "3.10.0"
z85 = "3.0.5"
cmds/install-secrets/src/main.rsdiffbeforeafterboth--- a/cmds/install-secrets/src/main.rs
+++ b/cmds/install-secrets/src/main.rs
@@ -2,9 +2,8 @@
use age::{Encryptor, Identity, Recipient};
use anyhow::{anyhow, bail, Context, Result};
use clap::Parser;
-use log::{error, info, warn};
use nix::sys::stat::Mode;
-use nix::unistd::{User, Group, chown};
+use nix::unistd::{chown, Group, User};
use serde::{Deserialize, Deserializer};
use std::fmt::{self, Display};
use std::fs::{self, File};
@@ -14,6 +13,9 @@
use std::path::Path;
use std::str::{from_utf8, FromStr};
use std::{collections::HashMap, path::PathBuf};
+use tracing::{error, info, warn};
+use tracing_subscriber::filter::LevelFilter;
+use tracing_subscriber::EnvFilter;
#[derive(Clone, Debug)]
struct SecretWrapper(Vec<u8>);
@@ -228,8 +230,13 @@
}
fn main() -> anyhow::Result<()> {
- env_logger::Builder::new()
- .filter_level(log::LevelFilter::Info)
+ tracing_subscriber::fmt()
+ .with_env_filter(
+ EnvFilter::builder()
+ .with_default_directive(LevelFilter::INFO.into())
+ .from_env_lossy(),
+ )
+ .without_time()
.init();
let opts = Opts::parse();
crates/better-command/Cargo.tomldiffbeforeafterboth--- a/crates/better-command/Cargo.toml
+++ b/crates/better-command/Cargo.toml
@@ -3,12 +3,10 @@
version = "0.1.0"
edition = "2021"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
[dependencies]
-once_cell = "1.19.0"
-regex = "1.10.2"
-serde = { version = "1.0.193", features = ["derive"] }
-serde_json = "1.0.108"
-tracing = "0.1.40"
-tracing-indicatif = "0.3.6"
+once_cell = "1.19"
+regex = "1.10"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+tracing = "0.1"
+tracing-indicatif = "0.3"
crates/nixlike/Cargo.tomldiffbeforeafterboth--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -7,8 +7,8 @@
alejandra = {git = "https://github.com/kamadorueda/alejandra"}
linked-hash-map = "0.5.6"
peg = "0.8.2"
-serde = "1.0.190"
-thiserror = "1.0.50"
-serde_json = "1.0.107"
+serde = "1.0.196"
+thiserror = "1.0.57"
+serde_json = "1.0.113"
ron = "0.8.1"
serde-transcode = "1.1.1"
flake.lockdiffbeforeafterboth--- a/flake.lock
+++ b/flake.lock
@@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
- "lastModified": 1701680307,
- "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
+ "lastModified": 1705309234,
+ "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
+ "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
@@ -23,11 +23,11 @@
"systems": "systems_2"
},
"locked": {
- "lastModified": 1681202837,
- "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
+ "lastModified": 1705309234,
+ "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "cfacdce06f30d2b68473a46042957675eebb3401",
+ "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
@@ -38,11 +38,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1704409229,
- "narHash": "sha256-Vc41cRJ3trOnocovLe0zZE35pK5Lfuo/zHk0xx3CNDY=",
+ "lastModified": 1708177587,
+ "narHash": "sha256-Tj/YV9kdC+I7V/kjrq3Bdl8z2VIHT5hiAG74s52vLgw=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "786f788914f2a6e94cedf361541894e972b8fd23",
+ "rev": "3c43b81701e73452df1c080b05770407da9e16d6",
"type": "github"
},
"original": {
@@ -67,11 +67,11 @@
]
},
"locked": {
- "lastModified": 1704075545,
- "narHash": "sha256-L3zgOuVKhPjKsVLc3yTm2YJ6+BATyZBury7wnhyc8QU=",
+ "lastModified": 1708135817,
+ "narHash": "sha256-EUMO/K3+Wgh0THOLoRXhxrh6G/pQ7BlJ8No+ciy1nKA=",
"owner": "oxalica",
"repo": "rust-overlay",
- "rev": "a0df72e106322b67e9c6e591fe870380bd0da0d5",
+ "rev": "c77e68d33a84ce3f9e86905c0f2ef78d5defad28",
"type": "github"
},
"original": {
flake.nixdiffbeforeafterboth--- a/flake.nix
+++ b/flake.nix
@@ -29,7 +29,7 @@
llvmPkgs = pkgs.buildPackages.llvmPackages_11;
rust =
(pkgs.rustChannelOf {
- date = "2024-01-01";
+ date = "2024-02-10";
channel = "nightly";
})
.default
@@ -38,12 +38,14 @@
packages = (import ./pkgs) pkgs pkgs;
devShell = (pkgs.mkShell.override {stdenv = llvmPkgs.stdenv;}) {
nativeBuildInputs = with pkgs; [
+ alejandra
rust
lld
cargo-edit
cargo-udeps
cargo-fuzz
cargo-watch
+ cargo-outdated
pkg-config
openssl
modules/fleet/secrets.nixdiffbeforeafterboth--- a/modules/fleet/secrets.nix
+++ b/modules/fleet/secrets.nix
@@ -1,5 +1,11 @@
-{ lib, fleetLib, config, ... }: with lib; with fleetLib;
-let
+{
+ lib,
+ fleetLib,
+ config,
+ ...
+}:
+with lib;
+with fleetLib; let
sharedSecret = with types; ({config, ...}: {
options = {
expectedOwners = mkOption {
@@ -9,13 +15,14 @@
Secrets would be decrypted and stored to /run/secrets/$\{name} on owners
'';
+ default = null;
};
# TODO: Aren't those options may be just desugared to data/expectedData?
regenerateOnOwnerAdded = mkOption {
type = bool;
description = ''
Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted.
-
+
You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs.
'';
};
@@ -24,7 +31,7 @@
type = bool;
description = ''
Should this secret be removed on owner removal, or it may be just reencrypted
-
+
Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing.
Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked
in some other way than by this secret ownership, I.e by firewall/etc.
@@ -55,7 +62,7 @@
Imported from fleet.nix
'';
- default = [ ];
+ default = [];
};
# TODO: Make secret generator generate arbitrary number of secret/public parts?
# Make it generate a folder, where all files except suffixed by .enc are public, and the rest are secret?
@@ -96,43 +103,121 @@
};
};
};
-in
-{
+in {
options = with types; {
sharedSecrets = mkOption {
type = attrsOf (submodule sharedSecret);
- default = { };
+ default = {};
description = "Shared secrets";
};
hostSecrets = mkOption {
type = attrsOf (attrsOf (submodule hostSecret));
- default = { };
+ default = {};
description = "Host secrets. Imported from fleet.nix";
internal = true;
};
};
config = {
- assertions = mapAttrsToList
+ assertions =
+ mapAttrsToList
(name: secret: {
assertion = secret.expectedOwners == null || builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners;
message = "Shared secret ${name} is expected to be encrypted for ${builtins.toJSON secret.expectedOwners}, but it is encrypted for ${builtins.toJSON secret.owners}. Run fleet secrets regenerate to fix";
})
config.sharedSecrets;
hosts = hostsToAttrs (host: {
- modules =
- let
- cleanupSecret = (secretName: v: {
- inherit (v) public secret;
- shared = true;
- });
- in
- [
- {
- secrets = (mapAttrs cleanupSecret
+ modules = let
+ cleanupSecret = secretName: v: {
+ inherit (v) public secret;
+ shared = true;
+ };
+ in [
+ {
+ secrets =
+ (
+ mapAttrs cleanupSecret
(filterAttrs (_: v: builtins.elem host v.owners) config.sharedSecrets)
- ) // (mapAttrs cleanupSecret (config.hostSecrets.${host} or { }));
- }
- ];
+ )
+ // (mapAttrs cleanupSecret (config.hostSecrets.${host} or {}));
+ }
+ ];
});
+ # TODO: Should this attribute be moved to `nixpkgs.overlays`?
+ overlays = [
+ (final: prev: let
+ lib = final.lib;
+ in {
+ mkPassword = {size ? 32}:
+ final.mkSecretGenerator ''
+ ${final.coreutils}/bin/tr -dc 'A-Za-z0-9!?%=' < /dev/random \
+ | ${final.coreutils}/bin/head -c ${toString size} \
+ | encrypt > $out/secret
+ '';
+ mkRsa = {size ? 4096}:
+ final.mkSecretGenerator ''
+ ${final.openssl}/bin/openssl genrsa -out rsa_private.key ${toString size}
+ ${final.openssl}/bin/openssl rsa -in rsa_private.key -pubout -out rsa_public.key
+
+ sudo cat rsa_private.key | encrypt > $out/secret
+ sudo cat rsa_public.key > $out/public
+ '';
+ # TODO: Move to fleet
+ # TODO: Merge both generators to one with consistent options syntax?
+ # Impure generator is built on local machine, then built closure is copied to remote machine,
+ # and then it is ran in inpure context, so that this generator may access HSMs and other things.
+ mkImpureSecretGenerator = generatorText: machine:
+ (prev.writeShellScript "impureGenerator.sh" ''
+ #!/bin/sh
+ set -eu
+
+ # TODO: Provide encryption function as script passed to `callPackage generator {encrypt = ...;}`
+ function encrypt() {
+ eval ${final.rage}/bin/rage $rageArgs
+ }
+
+ created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")
+ echo -n $created_at > $out/created_at
+
+ ${generatorText}
+
+ echo -n SUCCESS > $out/marker
+ '')
+ .overrideAttrs (old: {
+ passthru = {
+ generatorKind = "impure";
+ impureOn = machine;
+ };
+ });
+ # TODO: Implement consistent naming
+ # Pure secret generator is supposed to be run entirely by nix, using `__impure` derivation type...
+ # But for now, it is ran the same way as `impureSecretGenerator`, but on the local machine.
+ mkSecretGenerator = generatorText:
+ (prev.writeShellScript "generator.sh" ''
+ #!/bin/sh
+ set -eu
+ # TODO: User should create output directory by themselves.
+ cd $out
+
+ # TODO: Provide encryption function as script passed to `callPackage generator {encrypt = ...;}`
+ function encrypt() {
+ eval ${final.rage}/bin/rage $rageArgs
+ }
+
+ created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")
+ echo -n $created_at > $out/created_at
+
+ ${generatorText}
+
+ echo -n SUCCESS > $out/marker
+ '')
+ .overrideAttrs (old: {
+ passthru = {
+ generatorKind = "pure";
+ };
+ # TODO: make nix daemon build secret, not just the script.
+ # __impure = true;
+ });
+ })
+ ];
};
}