From 3ac37809b7130a50c55505758fcf33abc13b76db Mon Sep 17 00:00:00 2001 From: spushk Date: Tue, 2 Jun 2026 06:10:46 +0500 Subject: [PATCH] chore!: minor fixes here and there & move CRDs into submods & introduce serializer helpers --- Cargo.lock | 1028 +++++------------ Cargo.toml | 16 +- README.md | 29 +- kuyaseg-controller/Cargo.toml | 7 +- kuyaseg-controller/src/bin/crdgen.rs | 3 + kuyaseg-controller/src/bin/generate_crds.rs | 14 - kuyaseg-controller/src/generator.rs | 2 - kuyaseg-controller/src/reporting/mod.rs | 1 + .../src/resource/generated/secret/mod.rs | 608 ---------- .../src/resource/generated/secret/v1alpha1.rs | 191 --- .../resource/generated/secret/v1alpha1/mod.rs | 354 ++++++ .../generated/secret/v1alpha1/pseudorandom.rs | 0 .../generated/secret/v1alpha1/random.rs | 0 .../src/resource/generated/ssh/mod.rs | 2 +- kuyaseg-controller/src/utils/mod.rs | 1 + kuyaseg-controller/src/utils/serializers.rs | 106 ++ 16 files changed, 815 insertions(+), 1547 deletions(-) create mode 100644 kuyaseg-controller/src/bin/crdgen.rs delete mode 100644 kuyaseg-controller/src/bin/generate_crds.rs delete mode 100644 kuyaseg-controller/src/resource/generated/secret/v1alpha1.rs create mode 100644 kuyaseg-controller/src/resource/generated/secret/v1alpha1/mod.rs create mode 100644 kuyaseg-controller/src/resource/generated/secret/v1alpha1/pseudorandom.rs create mode 100644 kuyaseg-controller/src/resource/generated/secret/v1alpha1/random.rs create mode 100644 kuyaseg-controller/src/utils/serializers.rs diff --git a/Cargo.lock b/Cargo.lock index 2d22c33..6aaf74c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,15 +8,15 @@ version = "0.6.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" dependencies = [ - "crypto-common 0.2.1", + "crypto-common 0.2.2", "inout", ] [[package]] name = "aes" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" +checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" dependencies = [ "cipher", "cpubits", @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.11.0-rc.3" +version = "0.11.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22c0c90bbe8d4f77c3ca9ddabe41a1f8382d6fc1f7cea89459d0f320371f972" +checksum = "da8c919c118108f144adecad74b425b804ad075580d605d9b33c2d6d1c62a2f8" dependencies = [ "aead", "aes", @@ -67,6 +67,15 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.102" @@ -122,17 +131,6 @@ dependencies = [ "syn", ] -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -141,9 +139,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-fips-sys" @@ -281,7 +279,7 @@ dependencies = [ "quote", "regex", "rustc-hash", - "shlex", + "shlex 1.3.0", "syn", ] @@ -303,6 +301,7 @@ dependencies = [ "cfg-if", "constant_time_eq", "cpufeatures 0.3.0", + "serde", "zeroize", ] @@ -326,10 +325,19 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.20.2" +name = "bs58" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytes" @@ -339,14 +347,14 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.62" +version = "1.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -364,12 +372,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chacha20" version = "0.10.0" @@ -384,13 +386,25 @@ dependencies = [ ] [[package]] -name = "cipher" -version = "0.5.1" +name = "chrono" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.1", + "crypto-common 0.2.2", "inout", "zeroize", ] @@ -417,9 +431,9 @@ dependencies = [ [[package]] name = "cmov" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" [[package]] name = "concurrent-queue" @@ -516,18 +530,18 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] [[package]] name = "ctr" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17469f8eb9bdbfad10f71f4cfddfd38b01143520c0e717d8796ccb4d44d44e42" +checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21" dependencies = [ "cipher", ] @@ -543,12 +557,12 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "5.0.0-pre.6" +version = "5.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3" +checksum = "4f359e08ca85e7bd759e1fd933ff2bccd81864c60a8fba0e259c7f822b0924bf" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "curve25519-dalek-derive", "digest 0.11.3", "fiat-crypto", @@ -601,6 +615,16 @@ dependencies = [ "syn", ] +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", + "serde_core", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -648,18 +672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.1", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "crypto-common 0.2.2", ] [[package]] @@ -691,9 +704,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "3.0.0-pre.7" +version = "3.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20449acd54b660981ae5caa2bcb56d1fe7f25f2e37a38ec507400fab034d4bb6" +checksum = "b011170fe4f04665565b4110afef66774fe9ffff278f3eb5b81cc73d26e27d60" dependencies = [ "curve25519-dalek", "ed25519", @@ -715,9 +728,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "enum-ordinalize" @@ -794,12 +807,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foldhash" version = "0.1.5" @@ -931,10 +938,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -944,11 +949,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -993,23 +996,10 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.4.14" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" @@ -1043,6 +1033,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hostname" version = "0.4.2" @@ -1056,9 +1052,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", "itoa", @@ -1111,15 +1107,14 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -1167,16 +1162,13 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", "bytes", "futures-channel", "futures-util", "http", "http-body", "hyper", - "ipnet", "libc", - "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -1185,85 +1177,27 @@ dependencies = [ ] [[package]] -name = "icu_collections" -version = "2.2.0" +name = "iana-time-zone" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "icu_locale_core" -version = "2.2.0" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", + "cc", ] [[package]] @@ -1279,24 +1213,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "idna" -version = "1.1.0" +name = "indexmap" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", + "autocfg", + "hashbrown 0.12.3", + "serde", ] [[package]] @@ -1320,12 +1244,6 @@ dependencies = [ "hybrid-array", ] -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - [[package]] name = "itertools" version = "0.13.0" @@ -1352,9 +1270,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.24" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" +checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -1362,14 +1280,14 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-link", ] [[package]] name = "jiff-static" -version = "0.2.24" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" +checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" dependencies = [ "proc-macro2", "quote", @@ -1403,9 +1321,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -1456,7 +1374,7 @@ checksum = "51b326f5219dd55872a72c1b6ddd1b830b8334996c667449c29391d657d78d5e" dependencies = [ "base64", "jiff", - "schemars", + "schemars 1.2.1", "serde", "serde_json", ] @@ -1521,7 +1439,7 @@ dependencies = [ "jiff", "json-patch", "k8s-openapi", - "schemars", + "schemars 1.2.1", "serde", "serde-value", "serde_json", @@ -1585,15 +1503,15 @@ dependencies = [ "kube", "mimalloc", "opentelemetry", - "opentelemetry-otlp", "opentelemetry_sdk", "prometheus-client", "rand 0.10.1", "rand_hc", - "schemars", + "schemars 1.2.1", "scopeguard", "serde", "serde_json", + "serde_with", "serde_yaml", "smallvec", "ssh-key", @@ -1636,19 +1554,13 @@ dependencies = [ [[package]] name = "libmimalloc-sys" -version = "0.1.47" +version = "0.1.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1eacfa31c33ec25e873c136ba5669f00f9866d0688bea7be4d3f7e43067df6" +checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9" dependencies = [ "cc", ] -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - [[package]] name = "lock_api" version = "0.4.14" @@ -1660,15 +1572,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "matchers" @@ -1687,15 +1593,15 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "mimalloc" -version = "0.1.50" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3627c4272df786b9260cabaa46aec1d59c93ede723d4c3ef646c503816b0640" +checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862" dependencies = [ "libmimalloc-sys", ] @@ -1714,9 +1620,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -1742,6 +1648,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + [[package]] name = "num-traits" version = "0.2.19" @@ -1765,9 +1677,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "opentelemetry" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" +checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" dependencies = [ "futures-core", "futures-sink", @@ -1777,66 +1689,20 @@ dependencies = [ "tracing", ] -[[package]] -name = "opentelemetry-http" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" -dependencies = [ - "async-trait", - "bytes", - "http", - "opentelemetry", - "reqwest", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" -dependencies = [ - "http", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-proto", - "opentelemetry_sdk", - "prost", - "reqwest", - "thiserror", - "tokio", - "tonic", - "tracing", -] - -[[package]] -name = "opentelemetry-proto" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" -dependencies = [ - "opentelemetry", - "opentelemetry_sdk", - "prost", - "tonic", - "tonic-prost", -] - [[package]] name = "opentelemetry_sdk" -version = "0.31.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" +checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9" dependencies = [ "futures-channel", "futures-executor", "futures-util", "opentelemetry", "percent-encoding", + "portable-atomic", "rand 0.9.4", "thiserror", - "tokio", - "tokio-stream", ] [[package]] @@ -2009,13 +1875,10 @@ dependencies = [ ] [[package]] -name = "potential_utf" -version = "0.1.5" +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" @@ -2068,84 +1931,6 @@ dependencies = [ "syn", ] -[[package]] -name = "prost" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.4", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" version = "1.0.45" @@ -2280,46 +2065,6 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "ring" version = "0.17.14" @@ -2366,9 +2111,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -2382,7 +2127,6 @@ version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ - "web-time", "zeroize", ] @@ -2418,6 +2162,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars" version = "1.2.1" @@ -2550,9 +2306,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -2584,13 +2340,45 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap", + "indexmap 2.14.0", "itoa", "ryu", "serde", @@ -2634,6 +2422,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -2664,9 +2458,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -2674,9 +2468,9 @@ dependencies = [ [[package]] name = "ssh-cipher" -version = "0.3.0-rc.9" +version = "0.3.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10db6f219196a8528f9ec904d9d45cdad692d65b0e57e72be4dedd1c5fddce36" +checksum = "68126a396f08c64551b44cdb65865ef1d78fdf16ac02b53f78588227f60466fe" dependencies = [ "aes", "aes-gcm", @@ -2690,9 +2484,9 @@ dependencies = [ [[package]] name = "ssh-encoding" -version = "0.3.0-rc.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abf34aa716da5d5b4c496936d042ea282ab392092cd68a72ef6a8863ff8c96a" +checksum = "7b54d0ed0498daf3f78d82e00e28c8eec9d75a067c4cfbcc7a0f7d0f4077749e" dependencies = [ "base64ct", "ctutils", @@ -2718,12 +2512,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - [[package]] name = "strsim" version = "0.11.1" @@ -2752,20 +2540,6 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "thiserror" @@ -2797,13 +2571,34 @@ dependencies = [ ] [[package]] -name = "tinystr" -version = "0.8.3" +name = "time" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ - "displaydoc", - "zerovec", + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", ] [[package]] @@ -2827,7 +2622,6 @@ version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ - "bytes", "libc", "mio", "pin-project-lite", @@ -2858,17 +2652,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.18" @@ -2883,43 +2666,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tonic" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" -dependencies = [ - "async-trait", - "base64", - "bytes", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "sync_wrapper", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic-prost" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" -dependencies = [ - "bytes", - "prost", - "tonic", -] - [[package]] name = "tower" version = "0.5.3" @@ -2928,9 +2674,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap", "pin-project-lite", - "slab", "sync_wrapper", "tokio", "tokio-util", @@ -2941,14 +2685,13 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "base64", "bitflags", "bytes", - "futures-util", "http", "http-body", "mime", @@ -2957,7 +2700,6 @@ dependencies = [ "tower-layer", "tower-service", "tracing", - "url", ] [[package]] @@ -3018,9 +2760,9 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.32.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" +checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" dependencies = [ "js-sys", "opentelemetry", @@ -3058,9 +2800,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "ucd-trie" @@ -3086,7 +2828,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" dependencies = [ - "crypto-common 0.2.1", + "crypto-common 0.2.2", "ctutils", ] @@ -3108,24 +2850,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "valuable" version = "0.1.1" @@ -3173,9 +2897,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -3184,21 +2908,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3206,9 +2920,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -3219,9 +2933,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -3243,7 +2957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -3256,20 +2970,10 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.14.0", "semver", ] -[[package]] -name = "web-sys" -version = "0.3.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "web-time" version = "1.1.0" @@ -3280,28 +2984,72 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -3319,31 +3067,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -3352,96 +3083,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -3476,7 +3159,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.14.0", "prettyplease", "syn", "wasm-metadata", @@ -3507,7 +3190,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags", - "indexmap", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -3526,7 +3209,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.14.0", "log", "semver", "serde", @@ -3536,76 +3219,26 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zeroize" version = "1.8.2" @@ -3626,39 +3259,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index 9015c63..a64b8e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,15 +22,12 @@ tokio-util.version = '^0.7' axum.version = "^0.8" ## Tracing tracing.version = "^0.1" -tracing.features = ["max_level_trace", "release_max_level_info"] +tracing.features = ["max_level_debug", "release_max_level_info"] tracing-subscriber.version = "^0.3" -tracing-opentelemetry.version = "^0.32" -opentelemetry.version = "^0.31" -opentelemetry.features = ["trace"] -opentelemetry-otlp.version = "^0.31" -opentelemetry-otlp.features = ["grpc-tonic", "reqwest-rustls"] -opentelemetry_sdk.version = "^0.31" -opentelemetry_sdk.features = ["rt-tokio"] +tracing-opentelemetry.version = "^0.33" +opentelemetry.version = "^0.32" +opentelemetry_sdk.version = "^0.32" +prometheus-client.version = "^0.24" ## Utils jiff.version = "^0.2" scopeguard.version = "^1" @@ -42,6 +39,8 @@ schemars.version = "^1" base64.version = "^0.22" serde.version = "^1" serde.features = ["derive"] +serde_with.version = "3" +serde_with.features = ["hex", "schemars_1"] serde_json.version = "^1" serde_yaml.version = "^0.9" ## k8s @@ -49,7 +48,6 @@ kube.version = "^3" kube.features = ["derive"] k8s-openapi.version = "^0.27" k8s-openapi.features = ["latest", "schemars"] -prometheus-client.version = "^0.24" ## Codegen thiserror.version = "^2" anyhow.version = "^1" diff --git a/README.md b/README.md index da2a5bc..2d2df58 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,8 @@ Both resources inherit namespace and name (as prefix) from `RandomSsh` resource > ⚠️ This feature exists primarily for testing purposes, where security is less of a concern then repeatability. > -> While there are use-cases for predictable secrets in production, you should consider [ESO](https://external-secrets.io) +> While there are use-cases for predictable secrets in production (e.g. DB credentials in some cases), +> you should consider [ESO](https://external-secrets.io) > with secure external storage to share secrets between clusters and keep secrets stable. > > If the seed secret gets compromised - every past and future generated secret can be easliy computed. @@ -126,6 +127,24 @@ Secret values generated by deterministic generator depend on: Thus, if seed secret remains stable and DeterministicSecret specification does not change - generated keys remain stable and predictable +To set the `seed` for the controller, deploy a `v1/Secret` and target the controller to it with `KUYASEG_DETERMINISTIC_SEED` env variable. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: controller-seed + namespace: testing +stringData: + my_seed: aabbcc123 +``` + +`KUYASEG_DETERMINISTIC_SEED=controller-seed/my_seed` + +When the later part `/my_seed` is omitted, controller will expect the `seed` key to contain seed value. + +When the seed *value* changes, all known secrets are reconciled. + ## Feature comparison We know there are plenty of decent, mature and well-adopted solutions like the one by [mittwald](https://github.com/mittwald/kubernetes-secret-generator) and even [ESO](https://external-secrets.io) which can also generate secrets. @@ -156,11 +175,11 @@ To produce random secrets (`RandomSecret`, `RandomSshSecret`), operator uses [aw To produce deterministic secrets (`DeterministicSecret`, `DeterministicSsh`), operator uses [Hc128](https://docs.rs/rand_hc/latest/rand_hc/) CSPRNG. -### Secret persistence & memory +### Secret persistence -Random secrets defined by `RandomSecret` and `RandomSsh` are unstable. Deletion of generated `Secret` will lead to new random value on next reconcilation. +Random secrets defined by `RandomSecret` and `RandomSsh` are unstable: deletion of generated `Secret` will produce new random value on next reconcilation. -Deterministic secrets defined by `DeterministicSecret` and `DeterministicSsh` are stable as long as the base `seed` remains unchanged. -Deletion of generated `Secret` resources will lead to the same pseudo-random value put into the secret on next reconcilation as long as spec remains the same. +Deterministic secrets defined by `DeterministicSecret` and `DeterministicSsh` are stable as long as the base `seed` and spec/version remain unchanged. +Deletion of generated `Secret` resources will lead to the same pseudo-random value put into the secret on next reconcilation. Best effort is taken to ensure that secrets do not remain in memory longer then needed and/or after crush. diff --git a/kuyaseg-controller/Cargo.toml b/kuyaseg-controller/Cargo.toml index 5435d05..8affe9a 100644 --- a/kuyaseg-controller/Cargo.toml +++ b/kuyaseg-controller/Cargo.toml @@ -11,8 +11,8 @@ publish.workspace = true [[bin]] doc = false -name = "generate_crds" -path = "src/bin/generate_crds.rs" +name = "crdgen" +path = "src/bin/crdgen.rs" [dependencies] ## Rt @@ -26,7 +26,6 @@ tracing-subscriber.workspace = true tracing-subscriber.features = ["env-filter"] tracing-opentelemetry.workspace = true opentelemetry.workspace = true -opentelemetry-otlp.workspace = true opentelemetry_sdk.workspace = true prometheus-client.workspace = true ## Utils @@ -39,6 +38,7 @@ itertools.workspace = true schemars.workspace = true base64.workspace = true serde.workspace = true +serde_with.workspace = true serde_json.workspace = true serde_yaml.workspace = true ## k8s @@ -55,4 +55,5 @@ ssh-key.workspace = true ssh-key.features = ["ed25519"] rand_hc.workspace = true blake3.workspace = true +blake3.features = ["serde"] zeroize.workspace = true diff --git a/kuyaseg-controller/src/bin/crdgen.rs b/kuyaseg-controller/src/bin/crdgen.rs new file mode 100644 index 0000000..8045fc9 --- /dev/null +++ b/kuyaseg-controller/src/bin/crdgen.rs @@ -0,0 +1,3 @@ +use kube::CustomResourceExt; + +fn main() {} diff --git a/kuyaseg-controller/src/bin/generate_crds.rs b/kuyaseg-controller/src/bin/generate_crds.rs deleted file mode 100644 index 4390cf7..0000000 --- a/kuyaseg-controller/src/bin/generate_crds.rs +++ /dev/null @@ -1,14 +0,0 @@ -use kube::CustomResourceExt; - -fn main() { - print!( - "{}", - serde_yaml::to_string(&kuyaseg_controller::resource::generated::secret::v1alpha1::RandomSecret::crd()) - .expect("RandomSecret v1alpha1 CRD") - ); - print!( - "{}", - serde_yaml::to_string(&kuyaseg_controller::resource::generated::secret::v1alpha1::DeterministicSecret::crd()) - .expect("DeterministicSecret v1alpha1 CRD") - ) -} diff --git a/kuyaseg-controller/src/generator.rs b/kuyaseg-controller/src/generator.rs index 00baf8f..f359164 100644 --- a/kuyaseg-controller/src/generator.rs +++ b/kuyaseg-controller/src/generator.rs @@ -240,8 +240,6 @@ mod test { verify_ssh_pair(&pair); } - // ====== Deterministic (Hc128Rng) ====== - #[test] fn deterministic_bytes() { // we believe that generation is the same as for random, so our concern here is only value stability diff --git a/kuyaseg-controller/src/reporting/mod.rs b/kuyaseg-controller/src/reporting/mod.rs index 7e352a8..6132d3f 100644 --- a/kuyaseg-controller/src/reporting/mod.rs +++ b/kuyaseg-controller/src/reporting/mod.rs @@ -50,6 +50,7 @@ pub struct ReportingInit { pub pro_registry: Option, } +/// For opentelemetry TraceId to be present, tracing_opentelemetry::layer must also be set pub fn init(k8s_client: kube::Client, ct: CancellationToken) -> anyhow::Result { let config = ReportingConfig::builder() .env() diff --git a/kuyaseg-controller/src/resource/generated/secret/mod.rs b/kuyaseg-controller/src/resource/generated/secret/mod.rs index b919aba..32a5a9d 100644 --- a/kuyaseg-controller/src/resource/generated/secret/mod.rs +++ b/kuyaseg-controller/src/resource/generated/secret/mod.rs @@ -1,609 +1 @@ -use std::collections::{ - BTreeMap, - HashMap, -}; - -use anyhow::Context; -use kube::{ - Api, - Resource, -}; -use schemars::JsonSchema; -use serde::de::DeserializeOwned; -use serde::{ - Deserialize, - Serialize, -}; -use serde_json::json; -use zeroize::Zeroize; - pub mod v1alpha1; - -use crate::utils::zeroizers::{ - self, - zeroize_btree, - zeroize_hashmap, -}; - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -pub struct GeneratedSecretSpec { - #[serde(default)] - pub keys: Vec, - #[serde(default)] - pub template: GeneratedSecretTemplate, -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -pub struct GeneratedSecretStatus { - /// Stored all generated keys for matching v1/Secret resource, hashed by key name. - /// The matcher value is (version, shape hash) - /// - /// Shape hash is obtained with Blake3 hash. - pub generated: BTreeMap, -} - -impl GeneratedSecretStatus { - /// Determine whether the key from spec was already generated, based on: - /// - Key name - /// - Key version - /// - Key shape hash - fn was_generated(&self, spec: &GeneratedSecretKey) -> bool { - let Some(stored) = self.generated.get(&spec.key) else { - return false; - }; - - (stored.0 == spec.version) && (stored.1 == spec.shape.stable_hash()) - } -} - -impl GeneratedSecretSpec { - /// Reconcile generated secret from specification, following the specific type `T` generation strategy. - /// This method propagates upstream. - /// - /// This method family assumes that upstream resource has `GeneratedSecretStatus` status. - /// - /// I wrote this because - hell no - I won't write this code twice for both Random and Deterministic secrets! - async fn reconcile_spec( - &self, - name: &str, - namespace: &str, - client: kube::Client, - uid: Option<&str>, - ) -> anyhow::Result<()> - where - T: HasSecretGenerator - + Resource - + DeserializeOwned, - { - // discover target secret & branch for update/create - let target_api = Api::::namespaced(client.clone(), namespace); - let maybe_secret = scopeguard::guard( - { - target_api - .get_opt(name) - .await - .inspect_err(|e| { - tracing::error!( - debug=?e, - critical = true, - reason = "SecretDiscoveryFailed", - action = "Reconcile", - "Failed to discover target Secret" - ) - }) - .context("Discover target secret")? - }, - |mut v| { - v.as_mut().map(zeroizers::zeroize_k8s_secret); - }, - ); - - match maybe_secret.as_ref() { - Some(yep) => self - .update_from_spec::(name, namespace, client, yep) - .await - .context("Update existing Secret")?, - None => self - .create_new_from_spec::(name, namespace, client, uid) - .await - .context("Create new Secret")?, - } - - Ok(()) - } - - async fn create_new_from_spec( - &self, - name: &str, - namespace: &str, - client: kube::Client, - uid: Option<&str>, - ) -> anyhow::Result<()> - where - T: HasSecretGenerator - + Resource - + DeserializeOwned, - { - use k8s_openapi::apimachinery::pkg::apis::meta::v1::OwnerReference; - - let self_api = Api::::namespaced(client.clone(), namespace); - let target_api = Api::::namespaced(client.clone(), namespace); - - let generated = self - .keys - .iter() - .map(|key| (key.key.clone(), (key.version, key.shape.stable_hash()))) - .collect::>(); - - // first we try with dry-run, no need to guard as no data here - let owner_ref = uid.map(|uid| OwnerReference { - api_version: T::api_version(&()).to_string(), - kind: T::kind(&()).to_string(), - name: name.to_string(), - uid: uid.to_string(), - controller: Some(true), - ..Default::default() - }); - let partial_target = { - let mut secret = self.template.to_safe_partial_secret(Some(false), Some("Opaque".into())); - secret.metadata.owner_references = owner_ref.as_ref().map(|oref| vec![oref.clone()]); - secret - }; - let patch = kube::api::Patch::Apply(json!({ - "apiVersion": T::api_version(&()), - "kind": T::kind(&()), - "status": GeneratedSecretStatus { - generated - } - })); - - // dry-run - { - target_api - .create( - &kube::api::PostParams { - dry_run: true, - field_manager: Some(env!("CARGO_PKG_NAME").into()), - }, - &partial_target, - ) - .await - .inspect_err(|e| { - tracing::error!( - debug=?e, - reason = "SecretCreationFailed", - action = "CreateNew", - "Failed to create Secret resource" - ) - }) - .context("Dry-run create Secret")?; - - self_api - .patch_status( - name, - &kube::api::PatchParams { - dry_run: true, - force: true, - field_manager: Some(env!("CARGO_PKG_NAME").into()), - field_validation: Some(kube::api::ValidationDirective::Strict), - }, - &patch, - ) - .await.inspect_err(|e| tracing::error!(debug=?e, reason="SelfUpdateFailed", action = "CreateNew", "Failed to update self status")) - .context("Dry-run update status")?; - } - - // desperate run - { - let target = scopeguard::guard( - k8s_openapi::api::core::v1::Secret { - data: Some(T::generate_data(self).context("Create new Secret")?), - ..partial_target - }, - |mut v| zeroizers::zeroize_k8s_secret(&mut v), - ); - - target_api - .create( - &kube::api::PostParams { - dry_run: false, - field_manager: Some(env!("CARGO_PKG_NAME").into()), - }, - &target, - ) - .await - .inspect_err(|e| { - tracing::error!( - debug=?e, - reason = "SecretCreationFailed", - action = "CreateNew", - "Failed to create Secret resource" - ) - }) - .context("Create Secret")?; - - if let Err(e) = self_api - .patch_status( - name, - &kube::api::PatchParams { - dry_run: false, - force: true, - field_manager: Some(env!("CARGO_PKG_NAME").into()), - field_validation: Some(kube::api::ValidationDirective::Strict), - }, - &patch, - ) - .await - .inspect_err(|e| tracing::error!(debug=?e, reason="SelfUpdateFailed", action = "CreateNew", "Failed to update self status")) - .context("Update status") - { - tracing::error!( - reason = "SecretOrphanCleanup", - action = "CreateNew", - "Deleting orphan secret after failed status update" - ); - target_api.delete(name, &kube::api::DeleteParams::default()).await.context("Orphan secret on fail")?; - return Err(e); - } - } - - Ok(()) - } - - async fn update_from_spec( - &self, - name: &str, - namespace: &str, - client: kube::Client, - old_ref: &k8s_openapi::api::core::v1::Secret, - ) -> anyhow::Result<()> - where - T: HasSecretGenerator - + Resource - + DeserializeOwned, - { - todo!("WIP") - } -} - -pub(super) trait HasSecretGenerator { - fn generate_data(spec: &GeneratedSecretSpec) -> anyhow::Result>; -} - -#[cfg_attr(any(debug_assertions, test), derive(Debug, Eq, PartialEq))] -#[derive(Default, Clone, Serialize, Deserialize, JsonSchema)] -pub struct GeneratedSecretTemplate { - /// Add custom metadata to generated secret - #[serde(default)] - pub metadata: GeneratedSecretMetadata, - /// Base64 (standard+pad) encoded data - #[serde(default)] - pub data: HashMap, - /// Verbatim string data, which will be incoded as bytes into secret - #[serde(rename = "stringData")] - #[serde(default)] - pub string_data: BTreeMap, -} - -impl GeneratedSecretTemplate { - /// Does not retain data. - pub fn to_safe_partial_secret( - &self, - immutable: Option, - type_: Option, - ) -> k8s_openapi::api::core::v1::Secret { - k8s_openapi::api::core::v1::Secret { - type_, - immutable, - data: None, - metadata: self.metadata.clone().into_partial_metadata(), - string_data: None, - } - } -} - -impl Zeroize for GeneratedSecretTemplate { - fn zeroize(&mut self) { - zeroize_btree(&mut self.string_data); - zeroize_hashmap(&mut self.data); - } -} - -impl Drop for GeneratedSecretTemplate { - fn drop(&mut self) { - self.zeroize(); - } -} - -#[cfg_attr(any(debug_assertions, test), derive(Debug, Eq, PartialEq))] -#[derive(Default, Clone, Serialize, Deserialize, JsonSchema)] -pub struct GeneratedSecretMetadata { - /// Add annotations to generated secret - #[serde(default)] - pub annotations: BTreeMap, - /// Add labels to generated secret - #[serde(default)] - pub labels: BTreeMap, -} - -impl GeneratedSecretMetadata { - pub fn into_partial_metadata(self) -> kube::api::ObjectMeta { - kube::api::ObjectMeta { - annotations: Some(self.annotations), - labels: Some(self.labels), - ..Default::default() - } - } -} - -#[cfg_attr(any(debug_assertions, test), derive(Debug, PartialEq, Eq))] -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -pub struct GeneratedSecretKey { - /// The name of the key to write generated value into - pub key: String, - /// The version of generated value. Changing this will force new value to be generated - pub version: usize, - /// The type/kind specification for the generated value. May be `password` or `bytes`. Contains subfields - pub shape: GeneratedSecretShape, -} - -#[cfg_attr(any(debug_assertions, test), derive(Debug, PartialEq, Eq))] -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -pub enum GeneratedSecretShape { - Password { - /// The length of password in characters. Defaults to 32 - #[serde(default = "GeneratedSecretShape::default_pwd_length")] - length: usize, - /// The string of characters generator allowed to use. Defaults to alphanumeric lowercase+uppercase - #[serde(default = "GeneratedSecretShape::default_pwd_dict")] - dictionary: String, - }, - Bytes { - /// The size of secret in bytes. Defaults to 64 - #[serde(default = "GeneratedSecretShape::default_bytes_size")] - size: usize, - }, -} - -impl GeneratedSecretShape { - /// Base64 encoded stable (blake3) hash - pub fn stable_hash(&self) -> String { - use base64::prelude::*; - - let hash = { - let mut hasher = blake3::Hasher::new(); - match self { - Self::Bytes { size } => { - hasher.update(b"bytes"); - hasher.update(&size.to_le_bytes()); - } - Self::Password { length, dictionary } => { - hasher.update(b"password"); - hasher.update(&length.to_le_bytes()); - hasher.update(dictionary.as_bytes()); - } - } - hasher.finalize() - }; - - BASE64_STANDARD.encode(hash.as_bytes()) - } - - pub fn default_pwd_length() -> usize { - 32 - } - - pub fn default_pwd_dict() -> String { - String::from("abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ") - } - - pub fn default_bytes_size() -> usize { - 64 - } -} - -impl Zeroize for GeneratedSecretShape { - fn zeroize(&mut self) { - match self { - Self::Bytes { size } => { - size.zeroize(); - } - Self::Password { length, dictionary } => { - length.zeroize(); - dictionary.zeroize(); - } - } - } -} - -impl Drop for GeneratedSecretShape { - fn drop(&mut self) { - self.zeroize(); - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn deser_template_metadata() { - // Full sample - let sample = " - annotations: - my.custom-annotation: value - labels: - some-custom.label: value - "; - let expect = GeneratedSecretMetadata { - annotations: BTreeMap::from_iter([("my.custom-annotation".into(), "value".into())]), - labels: BTreeMap::from_iter([("some-custom.label".into(), "value".into())]), - }; - let parsed: GeneratedSecretMetadata = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Full sample didn't match"); - - // Empty annotations & labels - let sample = " - annotations: {} - labels: {} - "; - let expect = GeneratedSecretMetadata { - annotations: BTreeMap::new(), - labels: BTreeMap::new(), - }; - let parsed: GeneratedSecretMetadata = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty annotations&labels didn't match"); - - // Empty - let sample = ""; - let expect = GeneratedSecretMetadata::default(); - let parsed: GeneratedSecretMetadata = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty didn't match"); - } - - #[test] - fn deser_template() { - // full sample - let sample = " - data: - key0: abcde - stringData: - key1: fghij - metadata: - annotations: - my.custom-annotation: value - labels: - some-custom.label: value - "; - let expect = GeneratedSecretTemplate { - data: HashMap::from_iter([("key0".into(), "abcde".into())]), - string_data: BTreeMap::from_iter([("key1".into(), "fghij".into())]), - metadata: GeneratedSecretMetadata { - annotations: BTreeMap::from_iter([("my.custom-annotation".into(), "value".into())]), - labels: BTreeMap::from_iter([("some-custom.label".into(), "value".into())]), - }, - }; - let parsed: GeneratedSecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Full sample didn't match"); - - // Empty metadata - let sample = " - data: - key0: abcde - stringData: - key1: fghij - metadata: {} - "; - let expect = GeneratedSecretTemplate { - data: HashMap::from_iter([("key0".into(), "abcde".into())]), - string_data: BTreeMap::from_iter([("key1".into(), "fghij".into())]), - metadata: GeneratedSecretMetadata::default(), - }; - let parsed: GeneratedSecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty metadata didn't match"); - - // No metadata - let sample = " - data: - key0: abcde - stringData: - key1: fghij - "; - let expect = GeneratedSecretTemplate { - data: HashMap::from_iter([("key0".into(), "abcde".into())]), - string_data: BTreeMap::from_iter([("key1".into(), "fghij".into())]), - metadata: GeneratedSecretMetadata::default(), - }; - let parsed: GeneratedSecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "No metadata didn't match"); - - // Empty data & stringdata - let sample = " - data: {} - stringData: {} - "; - let expect = GeneratedSecretTemplate { - data: HashMap::new(), - string_data: BTreeMap::new(), - metadata: GeneratedSecretMetadata::default(), - }; - let parsed: GeneratedSecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty data&stringData didn't match"); - - // Empty - let sample = ""; - let expect = GeneratedSecretTemplate::default(); - let parsed: GeneratedSecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty didn't match"); - } - - #[test] - fn deser_key_spec() { - // Full password - let sample = " - key: aabbcc - version: 12 - shape: !Password - length: 123 - dictionary: abcdefg_1 - "; - let expect = GeneratedSecretKey { - key: "aabbcc".into(), - version: 12, - shape: GeneratedSecretShape::Password { - length: 123, - dictionary: "abcdefg_1".into(), - }, - }; - let parsed: GeneratedSecretKey = serde_yaml::from_str(sample).unwrap(); - assert_eq!(expect, parsed, "Full password mismatch"); - - // Empty password - let sample = " - key: aabbcc - version: 12 - shape: !Password - "; - let expect = GeneratedSecretKey { - key: "aabbcc".into(), - version: 12, - shape: GeneratedSecretShape::Password { - length: GeneratedSecretShape::default_pwd_length(), - dictionary: GeneratedSecretShape::default_pwd_dict(), - }, - }; - let parsed: GeneratedSecretKey = serde_yaml::from_str(sample).unwrap(); - assert_eq!(expect, parsed, "Empty password mismatch"); - - // Full bytes - let sample = " - key: aabbcc - version: 12 - shape: !Bytes - size: 1234 - "; - let expect = GeneratedSecretKey { - key: "aabbcc".into(), - version: 12, - shape: GeneratedSecretShape::Bytes { size: 1234 }, - }; - let parsed: GeneratedSecretKey = serde_yaml::from_str(sample).unwrap(); - assert_eq!(expect, parsed, "Full bytes mismatch"); - - // Empty bytes - let sample = " - key: aabbcc - version: 12 - shape: !Bytes - "; - let expect = GeneratedSecretKey { - key: "aabbcc".into(), - version: 12, - shape: GeneratedSecretShape::Bytes { - size: GeneratedSecretShape::default_bytes_size(), - }, - }; - let parsed: GeneratedSecretKey = serde_yaml::from_str(sample).unwrap(); - assert_eq!(expect, parsed, "Empty bytes mismatch"); - } -} diff --git a/kuyaseg-controller/src/resource/generated/secret/v1alpha1.rs b/kuyaseg-controller/src/resource/generated/secret/v1alpha1.rs deleted file mode 100644 index 1111bf6..0000000 --- a/kuyaseg-controller/src/resource/generated/secret/v1alpha1.rs +++ /dev/null @@ -1,191 +0,0 @@ -use std::collections::BTreeMap; - -use anyhow::Context; -use k8s_openapi::ByteString; -use kube::runtime::controller::Action; -use kube::{ - Client, - CustomResource, - Resource, - ResourceExt, -}; -use rand::prelude::SeedableRng; -use rand_hc::Hc128Rng; -use schemars::JsonSchema; -use serde::{ - Deserialize, - Serialize, -}; - -use crate::generator; -use crate::resource::generated::secret::{ - GeneratedSecretShape, - GeneratedSecretSpec, - GeneratedSecretStatus, - HasSecretGenerator, -}; - -/// Spec to generate random v1/Secret resource. -/// The resource is generated with the same name in the same namespace as this CR is deployed. -/// -/// The generated Secret data will change: -/// - On next reconcilation when: -/// - Generated v1/Secret was deleted (all regen) -/// - Key spec was changed (per-key regen) -/// -/// Altering the `template` *will not* change generated values -#[derive(Debug, Clone, Serialize, Deserialize, CustomResource, JsonSchema)] -#[kube( - namespaced, - kind = "RandomSecret", - group = "generator.kuyaseg.spushk.ru", - version = "v1alpha1", - status = "GeneratedSecretStatus" -)] -#[kube( - singular = "randomsecret", - plural = "randomsecrets", - shortname = "randsec", - category = "all", - category = "generators", - category = "secret-generators" -)] -#[serde(transparent)] -pub struct RandomSecretSpec(GeneratedSecretSpec); - -impl RandomSecret { - pub async fn reconcile(&self, client: Client) -> anyhow::Result { - let kind = Self::kind(&()); - let api = Self::api_version(&()); - let name = &self.name_any(); - let _span_nons = tracing::info_span!( - "RandomSecret.reconcile", - k8s_kind = kind.as_ref(), - k8s_api = api.as_ref(), - k8s_name = name, - k8s_uid = self.uid(), - ) - .entered(); - - let Some(namespace) = self.namespace() else { - tracing::error!( - reason = "RandomSecretNeedNamespace", - action = "Reconcile", - "RandomSecret must have namespace" - ); - return Err(anyhow::Error::msg("RandomSecret needed namespace")); - }; - let _span = tracing::info_span!("RandomSecret.reconcile.ns", k8s_ns = namespace).entered(); - - self.spec.0.reconcile_spec::(name, &namespace, client, self.uid().as_deref()).await.inspect_err(|e| tracing::error!(debug=?e, reason="RandomSecretReconciliationFailed", action = "Reconcile", "Failed to reconcile RandomSecret {namespace}{name}")).context("Reconcile")?; - - // TODO: verify if all desired events fire - Ok(Action::await_change()) - } -} - -impl HasSecretGenerator for RandomSecret { - fn generate_data(spec: &GeneratedSecretSpec) -> anyhow::Result> { - let mut rng = generator::AwsRng::new(); - let mut data = BTreeMap::new(); - - for key in spec.keys.iter() { - match &key.shape { - GeneratedSecretShape::Bytes { size } => { - data.insert(key.key.clone(), ByteString(generator::bytes(&mut rng, *size))); - } - GeneratedSecretShape::Password { length, dictionary } => { - data.insert(key.key.clone(), ByteString(generator::str_bytes(&mut rng, *length, dictionary.chars()).inspect_err(|e| tracing::error!(debug=?e, reason="PasswordGenFailed", action="Generate", "Failed to generate new random password")).context("Generate password")?)); - } - } - } - - Ok(data) - } -} - -/// Spec to generate DETERMINISTIC v1/Secret resource. -/// The resource is generated with the same name in the same namespace as this CR is deployed. -/// -/// The generated Secret data will change on next reconcilation when key spec was changed (per-key regen) -/// -/// Altering the `template` *will not* change generated values. -/// Removing generated v1/Secret *will not* change generated values. -#[derive(Debug, Clone, Serialize, Deserialize, CustomResource, JsonSchema)] -#[kube( - namespaced, - kind = "DeterministicSecret", - group = "generator.kuyaseg.spushk.ru", - version = "v1alpha1", - status = "GeneratedSecretStatus" -)] -#[kube( - singular = "deterministicsecret", - plural = "deterministicsecrets", - shortname = "detsec", - category = "all", - category = "generators", - category = "secret-generators" -)] -#[serde(transparent)] -pub struct DeterministicSecretSpec(GeneratedSecretSpec); - -impl DeterministicSecret { - pub async fn reconcile(&self, client: Client) -> anyhow::Result { - let kind = Self::kind(&()); - let api = Self::api_version(&()); - let name = &self.name_any(); - let _span_nons = tracing::info_span!( - "DeterministicSecret.reconcile", - k8s_kind = kind.as_ref(), - k8s_api = api.as_ref(), - k8s_name = name, - k8s_uid = self.uid(), - ) - .entered(); - - let Some(namespace) = self.namespace() else { - tracing::error!( - reason = "DeterministicSecretNeedNamespace", - action = "Reconcile", - "DeterministicSecret must have namespace" - ); - return Err(anyhow::Error::msg("DeterministicSecret needed namespace")); - }; - let _span = tracing::info_span!("DeterministicSecret.reconcile.ns", k8s_ns = namespace).entered(); - - self.spec.0.reconcile_spec::(name, &namespace, client, self.uid().as_deref()).await.inspect_err(|e| tracing::error!(debug=?e, reason="DeterministicSecretReconciliationFailed", action = "Reconcile", "Failed to reconcile DeterministicSecret {namespace}{name}")).context("Reconcile")?; - - // TODO: verify if all desired events fire - Ok(Action::await_change()) - } -} - -impl HasSecretGenerator for DeterministicSecret { - fn generate_data(spec: &GeneratedSecretSpec) -> anyhow::Result> { - let seed = std::env::var("KUYASEG_SEED").inspect_err(|e| tracing::error!(debug=?e, reason="DeterministicSecretNeedSeed", "Refuse to generate deterministic secret without seed. Set with KUYASEG_SEED env")).context("Get external seed")?; - let mut data = BTreeMap::new(); - - for key in spec.keys.iter() { - let mut rng = { - let mut hasher = blake3::Hasher::new(); - hasher.update(seed.as_bytes()); - hasher.update(key.key.as_bytes()); - hasher.update(&key.version.to_le_bytes()); - hasher.update(key.shape.stable_hash().as_bytes()); - Hc128Rng::from_seed(hasher.finalize().into()) - }; - - match &key.shape { - GeneratedSecretShape::Bytes { size } => { - data.insert(key.key.clone(), ByteString(generator::bytes(&mut rng, *size))); - } - GeneratedSecretShape::Password { length, dictionary } => { - data.insert(key.key.clone(), ByteString(generator::str_bytes(&mut rng, *length, dictionary.chars()).inspect_err(|e| tracing::error!(debug=?e, reason="PasswordGenFailed", action="Generate", "Failed to generate new random password")).context("Generate password")?)); - } - } - } - - Ok(data) - } -} diff --git a/kuyaseg-controller/src/resource/generated/secret/v1alpha1/mod.rs b/kuyaseg-controller/src/resource/generated/secret/v1alpha1/mod.rs new file mode 100644 index 0000000..871bd5b --- /dev/null +++ b/kuyaseg-controller/src/resource/generated/secret/v1alpha1/mod.rs @@ -0,0 +1,354 @@ +use std::collections::{ + BTreeMap, + HashMap, +}; + +use k8s_openapi::ByteString; +use schemars::JsonSchema; +use serde::{ + Deserialize, + Serialize, +}; +use serde_with::serde_as; +use zeroize::Zeroize; + +use crate::utils::{ + serializers, + zeroizers, +}; + +mod pseudorandom; +pub use pseudorandom::*; + +mod random; +pub use random::*; + +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct SecretStatus { + pub key_tracker: HashMap, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde_as] +pub struct KeyStatus { + /// Version is tracked separately, for change must set NE status + pub version: usize, + #[schemars(schema_with = "String::json_schema")] + #[serde_as(as = "serde_with::hex::Hex")] + pub spec_hash: blake3::Hash, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(test, derive(PartialEq))] +pub struct KeySpec { + /// Name of the key to put generated data into + pub key: String, + /// Tracks the key generation version within the same shape. + /// Increasing this number will force value regeneration on next reconcilation + pub version: usize, + /// Determines the shape of generated data + pub shape: KeyShape, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(test, derive(PartialEq))] +pub enum KeyShape { + /// Generate arbitrary bytes + Bytes { + /// Size of generated sequence in bytes. + /// + /// Defaults to 64 + #[serde(default = "KeyShape::default_bytes_size")] + size: usize, + }, + /// Generate string password + Password { + /// The length of password in characters. + /// + /// Defaults to 24 + #[serde(default = "KeyShape::default_pwd_length")] + length: usize, + /// The string of characters allowed to be present in password. + /// When dictionary length is > 4, each character in generated password is guaranteed to repeat + /// with period at least 3. + /// + /// Defaults to lowercase+uppercase alphanumeric and `-` + #[serde(default = "KeyShape::default_pwd_dict")] + dictionary: String, + }, +} + +impl KeyShape { + /// BLAKE3 hash, incorporating shape and according properties + pub fn stable_hash(&self) -> blake3::Hash { + let mut hasher = blake3::Hasher::new(); + match self { + Self::Bytes { size } => { + hasher.update(b"bytes"); + hasher.update(&size.to_le_bytes()); + } + Self::Password { length, dictionary } => { + hasher.update(b"password"); + hasher.update(&length.to_le_bytes()); + hasher.update(dictionary.as_bytes()); + } + } + hasher.finalize() + } + + pub fn default_bytes_size() -> usize { + 64 + } + pub fn default_pwd_length() -> usize { + 24 + } + pub fn default_pwd_dict() -> String { + String::from("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-") + } +} + +impl Zeroize for KeyShape { + #[inline] + fn zeroize(&mut self) { + match self { + Self::Bytes { size } => size.zeroize(), + Self::Password { length, dictionary } => { + length.zeroize(); + dictionary.zeroize(); + } + } + } +} + +impl Drop for KeyShape { + fn drop(&mut self) { + self.zeroize(); + } +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(test, derive(PartialEq))] +pub struct SecretTemplate { + /// Define created Secret resource metadata + #[serde(default)] + pub metadata: SecretMetadata, + /// Include static data into created Secret + #[serde(default)] + #[schemars(schema_with = "BTreeMap::::json_schema")] + pub data: BTreeMap, + /// Include static string data into created Secret + #[serde(default)] + #[serde(rename = "stringData")] + #[serde(with = "serializers::btreemap_to_bytestring_inplace")] + #[schemars(schema_with = "BTreeMap::::json_schema")] + pub string_data: BTreeMap, +} + +impl Zeroize for SecretTemplate { + #[inline] + fn zeroize(&mut self) { + zeroizers::zeroize_btree_of_bytestring(&mut self.data); + zeroizers::zeroize_btree_of_bytestring(&mut self.string_data); + } +} + +impl Drop for SecretTemplate { + fn drop(&mut self) { + self.zeroize(); + } +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(test, derive(PartialEq))] +pub struct SecretMetadata { + #[serde(default)] + pub annotations: BTreeMap, + #[serde(default)] + pub labels: BTreeMap, +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn deser_template_metadata() { + // Full sample + let sample = " + annotations: + my.custom-annotation: value + labels: + some-custom.label: value + "; + let expect = SecretMetadata { + annotations: BTreeMap::from_iter([("my.custom-annotation".into(), "value".into())]), + labels: BTreeMap::from_iter([("some-custom.label".into(), "value".into())]), + }; + let parsed: SecretMetadata = serde_yaml::from_str(sample).unwrap(); + assert_eq!(parsed, expect, "Full sample didn't match"); + + // Empty annotations & labels + let sample = " + annotations: {} + labels: {} + "; + let expect = SecretMetadata { + annotations: BTreeMap::new(), + labels: BTreeMap::new(), + }; + let parsed: SecretMetadata = serde_yaml::from_str(sample).unwrap(); + assert_eq!(parsed, expect, "Empty annotations&labels didn't match"); + + // Empty + let sample = ""; + let expect = SecretMetadata::default(); + let parsed: SecretMetadata = serde_yaml::from_str(sample).unwrap(); + assert_eq!(parsed, expect, "Empty didn't match"); + } + + #[test] + fn deser_template() { + // full sample + let sample = " + data: + key0: YWJjZGU= + stringData: + key1: fghij + metadata: + annotations: + my.custom-annotation: value + labels: + some-custom.label: value + "; + let expect = SecretTemplate { + data: BTreeMap::from_iter([("key0".into(), ByteString("abcde".to_string().into_bytes()))]), + string_data: BTreeMap::from_iter([("key1".into(), ByteString("fghij".to_string().into_bytes()))]), + metadata: SecretMetadata { + annotations: BTreeMap::from_iter([("my.custom-annotation".into(), "value".into())]), + labels: BTreeMap::from_iter([("some-custom.label".into(), "value".into())]), + }, + }; + let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); + assert_eq!(parsed, expect, "Full sample didn't match"); + + // Empty metadata + let sample = " + data: + key0: YWJjZGU= + stringData: + key1: fghij + metadata: {} + "; + let expect = SecretTemplate { + data: BTreeMap::from_iter([("key0".into(), ByteString("abcde".to_string().into_bytes()))]), + string_data: BTreeMap::from_iter([("key1".into(), ByteString("fghij".to_string().into_bytes()))]), + metadata: SecretMetadata::default(), + }; + let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); + assert_eq!(parsed, expect, "Empty metadata didn't match"); + + // No metadata + let sample = " + data: + key0: YWJjZGU= + stringData: + key1: fghij + "; + let expect = SecretTemplate { + data: BTreeMap::from_iter([("key0".into(), ByteString("abcde".to_string().into_bytes()))]), + string_data: BTreeMap::from_iter([("key1".into(), ByteString("fghij".to_string().into_bytes()))]), + metadata: SecretMetadata::default(), + }; + let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); + assert_eq!(parsed, expect, "No metadata didn't match"); + + // Empty data & stringdata + let sample = " + data: {} + stringData: {} + "; + let expect = SecretTemplate { + data: BTreeMap::new(), + string_data: BTreeMap::new(), + metadata: SecretMetadata::default(), + }; + let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); + assert_eq!(parsed, expect, "Empty data&stringData didn't match"); + + // Empty + let sample = ""; + let expect = SecretTemplate::default(); + let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); + assert_eq!(parsed, expect, "Empty didn't match"); + } + + #[test] + fn deser_key_spec() { + // Full password + let sample = " + key: aabbcc + version: 12 + shape: !Password + length: 123 + dictionary: abcdefg_1 + "; + let expect = KeySpec { + key: "aabbcc".into(), + version: 12, + shape: KeyShape::Password { + length: 123, + dictionary: "abcdefg_1".into(), + }, + }; + let parsed: KeySpec = serde_yaml::from_str(sample).unwrap(); + assert_eq!(expect, parsed, "Full password mismatch"); + + // Empty password + let sample = " + key: aabbcc + version: 12 + shape: !Password + "; + let expect = KeySpec { + key: "aabbcc".into(), + version: 12, + shape: KeyShape::Password { + length: KeyShape::default_pwd_length(), + dictionary: KeyShape::default_pwd_dict(), + }, + }; + let parsed: KeySpec = serde_yaml::from_str(sample).unwrap(); + assert_eq!(expect, parsed, "Empty password mismatch"); + + // Full bytes + let sample = " + key: aabbcc + version: 12 + shape: !Bytes + size: 1234 + "; + let expect = KeySpec { + key: "aabbcc".into(), + version: 12, + shape: KeyShape::Bytes { size: 1234 }, + }; + let parsed: KeySpec = serde_yaml::from_str(sample).unwrap(); + assert_eq!(expect, parsed, "Full bytes mismatch"); + + // Empty bytes + let sample = " + key: aabbcc + version: 12 + shape: !Bytes + "; + let expect = KeySpec { + key: "aabbcc".into(), + version: 12, + shape: KeyShape::Bytes { + size: KeyShape::default_bytes_size(), + }, + }; + let parsed: KeySpec = serde_yaml::from_str(sample).unwrap(); + assert_eq!(expect, parsed, "Empty bytes mismatch"); + } +} diff --git a/kuyaseg-controller/src/resource/generated/secret/v1alpha1/pseudorandom.rs b/kuyaseg-controller/src/resource/generated/secret/v1alpha1/pseudorandom.rs new file mode 100644 index 0000000..e69de29 diff --git a/kuyaseg-controller/src/resource/generated/secret/v1alpha1/random.rs b/kuyaseg-controller/src/resource/generated/secret/v1alpha1/random.rs new file mode 100644 index 0000000..e69de29 diff --git a/kuyaseg-controller/src/resource/generated/ssh/mod.rs b/kuyaseg-controller/src/resource/generated/ssh/mod.rs index e51933e..8b13789 100644 --- a/kuyaseg-controller/src/resource/generated/ssh/mod.rs +++ b/kuyaseg-controller/src/resource/generated/ssh/mod.rs @@ -1 +1 @@ -pub type GeneratedSshSecretTemplate = super::secret::GeneratedSecretTemplate; + diff --git a/kuyaseg-controller/src/utils/mod.rs b/kuyaseg-controller/src/utils/mod.rs index 0269ccf..659e158 100644 --- a/kuyaseg-controller/src/utils/mod.rs +++ b/kuyaseg-controller/src/utils/mod.rs @@ -1 +1,2 @@ +pub mod serializers; pub mod zeroizers; diff --git a/kuyaseg-controller/src/utils/serializers.rs b/kuyaseg-controller/src/utils/serializers.rs new file mode 100644 index 0000000..83468f5 --- /dev/null +++ b/kuyaseg-controller/src/utils/serializers.rs @@ -0,0 +1,106 @@ +/// in-place conversion of String into ByteString, verbatim, utf8-lossy +/// +/// e.g. `"abc" -> ByteString("abc".to_string().into_bytes())` +pub mod str_to_bytestring_inplace { + use k8s_openapi::ByteString; + use serde::de::Visitor; + use serde::{ + Deserializer, + Serializer, + }; + + pub fn serialize(value: &ByteString, se: S) -> Result + where + S: Serializer, + { + let value = String::from_utf8_lossy(&value.0); + se.serialize_str(&value) + } + + pub fn deserialize<'de, D>(de: D) -> Result + where + D: Deserializer<'de>, + { + de.deserialize_str(StringBytestringVisitor) + } + + struct StringBytestringVisitor; + + impl<'de> Visitor<'de> for StringBytestringVisitor { + type Value = ByteString; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(ByteString(v.as_bytes().into())) + } + } +} + +/// in-place conversion of BTreeMap to BTreeMap, verbatim, utf8-lossy +/// +///e.g. `{"abc": "def"} -> {"abc": ByteString("def".to_string().into_bytes())}` +pub mod btreemap_to_bytestring_inplace { + use std::collections::BTreeMap; + use std::marker::PhantomData; + + use k8s_openapi::ByteString; + use serde::de::Visitor; + use serde::ser::SerializeMap; + use serde::{ + Deserialize, + Deserializer, + Serialize, + Serializer, + }; + + pub fn serialize(value: &BTreeMap, se: S) -> Result + where + S: Serializer, + K: Serialize, + { + let mut map = se.serialize_map(Some(value.len()))?; + value + .iter() + .try_for_each(|(k, v)| map.serialize_entry(k, &String::from_utf8_lossy(&v.0)))?; + map.end() + } + + pub fn deserialize<'de, K, D>(de: D) -> Result, D::Error> + where + D: Deserializer<'de>, + K: Ord + Deserialize<'de>, + { + de.deserialize_map(BTreeVisitor(PhantomData)) + } + + struct BTreeVisitor(PhantomData); + + impl<'de, K: Ord + Deserialize<'de>> Visitor<'de> for BTreeVisitor { + type Value = BTreeMap; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "map {ty} -> String", ty = std::any::type_name::()) + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut btree = BTreeMap::new(); + while let Some((key, value)) = map.next_entry::()? { + if btree.contains_key(&key) { + return Err(serde::de::Error::custom("Duplicate key")); + } + btree.insert(key, ByteString(value.as_bytes().into())); + } + + Ok(btree) + } + } +}