diff --git a/Cargo.lock b/Cargo.lock
index 4a3a8ff4..1b50d352 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,74 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common 0.1.6",
+ "generic-array",
+]
+
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher 0.4.4",
+ "cpufeatures 0.2.17",
+]
+
+[[package]]
+name = "aes"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138"
+dependencies = [
+ "cipher 0.5.2",
+ "cpubits",
+ "cpufeatures 0.3.0",
+ "zeroize",
+]
+
+[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes 0.8.4",
+ "cipher 0.4.4",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
+name = "aes-keywrap"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10b6f24a1f796bc46415a1d0d18dc0a8203ccba088acf5def3291c4f61225522"
+dependencies = [
+ "aes 0.9.1",
+ "byteorder",
+]
+
+[[package]]
+name = "aes-kw"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41ac571010bd60765c56085a4f1d412012a9be2663b1a2f2b19b49318653fd0d"
+dependencies = [
+ "aes 0.9.1",
+ "const-oid 0.10.2",
+ "zeroize",
+]
+
[[package]]
name = "ahash"
version = "0.8.12"
@@ -146,6 +214,18 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
[[package]]
name = "async-broadcast"
version = "0.7.2"
@@ -216,6 +296,29 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "attester"
+version = "0.1.0"
+source = "git+https://github.com/confidential-containers/guest-components.git?rev=1fcebcb66a3c21b62e852819d5b55212c90eba2a#1fcebcb66a3c21b62e852819d5b55212c90eba2a"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "base64 0.22.1",
+ "cfg-if",
+ "crypto",
+ "hex",
+ "kbs-types",
+ "num-traits",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "sha2 0.11.0",
+ "strum 0.28.0",
+ "thiserror 2.0.18",
+ "tokio",
+ "tracing",
+]
+
[[package]]
name = "auto_impl"
version = "1.3.0"
@@ -276,6 +379,29 @@ dependencies = [
"thiserror 1.0.69",
]
+[[package]]
+name = "aws-lc-rs"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
+dependencies = [
+ "aws-lc-sys",
+ "untrusted 0.7.1",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.41.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
+dependencies = [
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+]
+
[[package]]
name = "axum"
version = "0.8.9"
@@ -409,6 +535,12 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
+[[package]]
+name = "binstring"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0669d5a35b64fdb5ab7fb19cae13148b6b5cbdf4b8247faf54ece47f699c8cef"
+
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -421,6 +553,17 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
+[[package]]
+name = "blake2b_simd"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "constant_time_eq",
+]
+
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -439,6 +582,15 @@ dependencies = [
"hybrid-array",
]
+[[package]]
+name = "bs58"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
+dependencies = [
+ "tinyvec",
+]
+
[[package]]
name = "bumpalo"
version = "3.19.0"
@@ -457,13 +609,25 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+[[package]]
+name = "cbor-codec"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e083a023562b37c52837e850131a51b1154cceb9d149f41ee3d386737b140f46"
+dependencies = [
+ "byteorder",
+ "libc",
+]
+
[[package]]
name = "cc"
-version = "1.2.45"
+version = "1.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe"
+checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
dependencies = [
"find-msvc-tools",
+ "jobserver",
+ "libc",
"shlex",
]
@@ -473,6 +637,17 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+[[package]]
+name = "chacha20"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "chrono"
version = "0.4.45"
@@ -487,6 +662,33 @@ dependencies = [
"windows-link 0.2.1",
]
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
[[package]]
name = "cipher"
version = "0.4.4"
@@ -494,7 +696,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common 0.1.6",
- "inout",
+ "inout 0.1.4",
+]
+
+[[package]]
+name = "cipher"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
+dependencies = [
+ "crypto-common 0.2.2",
+ "inout 0.2.2",
]
[[package]]
@@ -554,6 +766,32 @@ dependencies = [
"serde",
]
+[[package]]
+name = "cmake"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "cmov"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
+
+[[package]]
+name = "coarsetime"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e58eb270476aa4fc7843849f8a35063e8743b4dbcdf6dd0f8ea0886980c204c2"
+dependencies = [
+ "libc",
+ "wasix",
+ "wasm-bindgen",
+]
+
[[package]]
name = "colorchoice"
version = "1.0.4"
@@ -590,6 +828,15 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "concat-kdf"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816"
+dependencies = [
+ "digest 0.10.7",
+]
+
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@@ -631,6 +878,41 @@ dependencies = [
"unicode-xid",
]
+[[package]]
+name = "constant_time_eq"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
+
+[[package]]
+name = "cookie"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
+dependencies = [
+ "percent-encoding",
+ "time",
+ "version_check",
+]
+
+[[package]]
+name = "cookie_store"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
+dependencies = [
+ "cookie",
+ "document-features",
+ "idna",
+ "log",
+ "publicsuffix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -657,6 +939,23 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+[[package]]
+name = "cose-rust"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a140f41f55ff1f2126aed96961bad2387ae31d7f9bbd0e98ec888073beaac6f"
+dependencies = [
+ "cbor-codec",
+ "openssl",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "cpubits"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
+
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -690,6 +989,37 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+[[package]]
+name = "crunchy"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+
+[[package]]
+name = "crypto"
+version = "0.1.0"
+source = "git+https://github.com/confidential-containers/guest-components.git?rev=1fcebcb66a3c21b62e852819d5b55212c90eba2a#1fcebcb66a3c21b62e852819d5b55212c90eba2a"
+dependencies = [
+ "aes-gcm",
+ "aes-kw",
+ "anyhow",
+ "base64 0.22.1",
+ "concat-kdf",
+ "ctr",
+ "kbs-types",
+ "openssl",
+ "p256",
+ "p521",
+ "rand 0.10.1",
+ "rand 0.8.5",
+ "rsa",
+ "serde",
+ "serde_json",
+ "sha2 0.11.0",
+ "strum 0.28.0",
+ "zeroize",
+]
+
[[package]]
name = "crypto-bigint"
version = "0.5.5"
@@ -697,7 +1027,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
- "rand_core",
+ "rand_core 0.6.4",
"subtle",
"zeroize",
]
@@ -709,6 +1039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
+ "rand_core 0.6.4",
"typenum",
]
@@ -718,7 +1049,33 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
+ "getrandom 0.4.1",
"hybrid-array",
+ "rand_core 0.10.1",
+]
+
+[[package]]
+name = "ct-codecs"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49fb0c6640b4507ebd99ff67677009e381ba5eee1d14df78de4a3d16eb123c39"
+
+[[package]]
+name = "ctr"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+dependencies = [
+ "cipher 0.4.4",
+]
+
+[[package]]
+name = "ctutils"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
+dependencies = [
+ "cmov",
]
[[package]]
@@ -846,6 +1203,38 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "defmt"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f"
+dependencies = [
+ "bitflags 1.3.2",
+ "defmt-macros",
+]
+
+[[package]]
+name = "defmt-macros"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b"
+dependencies = [
+ "defmt-parser",
+ "proc-macro-error2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "defmt-parser"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
+dependencies = [
+ "thiserror 2.0.18",
+]
+
[[package]]
name = "der"
version = "0.7.10"
@@ -857,6 +1246,16 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "der"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b"
+dependencies = [
+ "const-oid 0.10.2",
+ "zeroize",
+]
+
[[package]]
name = "deranged"
version = "0.5.5"
@@ -864,6 +1263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
dependencies = [
"powerfmt",
+ "serde_core",
]
[[package]]
@@ -951,24 +1351,58 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "document-features"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
[[package]]
name = "dyn-clone"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+[[package]]
+name = "ear"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aec2877d084955915f48086ae07f49f4c89e33e3826d1f7af33b342274f8100"
+dependencies = [
+ "base64 0.22.1",
+ "ciborium",
+ "cose-rust",
+ "hex",
+ "jsonwebtoken",
+ "lazy_static",
+ "openssl",
+ "phf",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.18",
+]
+
[[package]]
name = "ecdsa"
version = "0.16.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
dependencies = [
- "der",
+ "der 0.7.10",
"digest 0.10.7",
"elliptic-curve",
"rfc6979",
- "signature",
- "spki",
+ "signature 2.2.0",
+ "spki 0.7.3",
]
[[package]]
@@ -977,8 +1411,18 @@ version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
- "pkcs8",
- "signature",
+ "pkcs8 0.10.2",
+ "signature 2.2.0",
+]
+
+[[package]]
+name = "ed25519-compact"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5c0284a5d4b1a2fae017a9fe55fd7d01699711f1b572493f16593e173ea2801"
+dependencies = [
+ "ct-codecs",
+ "getrandom 0.4.1",
]
[[package]]
@@ -1027,8 +1471,8 @@ dependencies = [
"group",
"hkdf",
"pem-rfc7468",
- "pkcs8",
- "rand_core",
+ "pkcs8 0.10.2",
+ "rand_core 0.6.4",
"sec1",
"subtle",
"zeroize",
@@ -1144,7 +1588,7 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
dependencies = [
- "rand_core",
+ "rand_core 0.6.4",
"subtle",
]
@@ -1156,9 +1600,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "find-msvc-tools"
-version = "0.1.4"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "flate2"
@@ -1322,8 +1766,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
]
[[package]]
@@ -1347,10 +1793,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi",
+ "rand_core 0.10.1",
"wasip2",
"wasip3",
+ "wasm-bindgen",
]
[[package]]
@@ -1365,6 +1814,16 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "ghash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
[[package]]
name = "gimli"
version = "0.32.3"
@@ -1391,6 +1850,19 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "git2"
+version = "0.20.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b"
+dependencies = [
+ "bitflags 2.10.0",
+ "libc",
+ "libgit2-sys",
+ "log",
+ "url",
+]
+
[[package]]
name = "glob"
version = "0.3.3"
@@ -1426,7 +1898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
- "rand_core",
+ "rand_core 0.6.4",
"subtle",
]
@@ -1468,6 +1940,17 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "half"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+ "zerocopy",
+]
+
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1542,6 +2025,30 @@ dependencies = [
"digest 0.10.7",
]
+[[package]]
+name = "hmac-sha1-compact"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0b3ba31f6dc772cc8221ce81dbbbd64fa1e668255a6737d95eeace59b5a8823"
+
+[[package]]
+name = "hmac-sha256"
+version = "1.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec9d92d097f4749b64e8cc33d924d9f40a2d4eb91402b458014b781f5733d60f"
+dependencies = [
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "hmac-sha512"
+version = "1.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "019ece39bbefc17f13f677a690328cb978dbf6790e141a3c24e66372cb38588b"
+dependencies = [
+ "digest 0.10.7",
+]
+
[[package]]
name = "hostname"
version = "0.4.1"
@@ -1635,6 +2142,7 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
dependencies = [
+ "ctutils",
"typenum",
]
@@ -1973,6 +2481,15 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "inout"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
+dependencies = [
+ "hybrid-array",
+]
+
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -2006,6 +2523,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
+[[package]]
+name = "is_debug"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fe266d2e243c931d8190177f20bf7f24eed45e96f39e87dc49a27b32d12d407"
+
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -2038,28 +2561,56 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
-version = "0.2.23"
+version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359"
+checksum = "34f877a98676d2fb664698d74cc6a51ce6c484ce8c770f05d0108ec9090aeb46"
dependencies = [
+ "defmt",
"jiff-static",
+ "jiff-tzdb-platform",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
+ "windows-link 0.2.1",
]
[[package]]
name = "jiff-static"
-version = "0.2.23"
+version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4"
+checksum = "0666b5ab5ecaca213fc2a85b8c0083d9004e84ee2d5f9a7e0017aaf50986f25f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
+[[package]]
+name = "jiff-tzdb"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076"
+
+[[package]]
+name = "jiff-tzdb-platform"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
+dependencies = [
+ "jiff-tzdb",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+dependencies = [
+ "getrandom 0.3.4",
+ "libc",
+]
+
[[package]]
name = "js-sys"
version = "0.3.85"
@@ -2107,10 +2658,11 @@ dependencies = [
[[package]]
name = "jsonwebtoken"
-version = "10.3.0"
+version = "10.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1"
+checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc"
dependencies = [
+ "aws-lc-rs",
"base64 0.22.1",
"ed25519-dalek",
"getrandom 0.2.16",
@@ -2118,12 +2670,65 @@ dependencies = [
"js-sys",
"p256",
"p384",
- "rand",
+ "pem",
+ "rand 0.8.5",
"rsa",
"serde",
"serde_json",
"sha2 0.10.9",
- "signature",
+ "signature 2.2.0",
+ "simple_asn1",
+ "zeroize",
+]
+
+[[package]]
+name = "jsonwebtoken-openssl"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53aec0291d5d8b37fbf826ccc23f5473536dad8dafda4161a7b6b56c2b0e0202"
+dependencies = [
+ "jsonwebtoken",
+ "openssl",
+]
+
+[[package]]
+name = "jwt-simple"
+version = "0.12.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3991f54af4b009bb6efe01aa5a4fcce9ca52f3de7a104a3f6b6e2ad36c852c48"
+dependencies = [
+ "anyhow",
+ "binstring",
+ "blake2b_simd",
+ "coarsetime",
+ "ct-codecs",
+ "ed25519-compact",
+ "hmac-sha1-compact",
+ "hmac-sha256",
+ "hmac-sha512",
+ "k256",
+ "p256",
+ "p384",
+ "rand 0.8.5",
+ "serde",
+ "serde_json",
+ "superboring",
+ "thiserror 2.0.18",
+ "zeroize",
+]
+
+[[package]]
+name = "k256"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
+dependencies = [
+ "cfg-if",
+ "ecdsa",
+ "elliptic-curve",
+ "once_cell",
+ "sha2 0.10.9",
+ "signature 2.2.0",
]
[[package]]
@@ -2146,9 +2751,79 @@ checksum = "d9c6922f6afe80418dd6019818af5d0d34584c371780ff09b9752370c25b4abb"
dependencies = [
"base64 0.22.1",
"jiff",
- "schemars",
+ "schemars 1.1.0",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "kbs-client"
+version = "0.1.0"
+source = "git+https://github.com/confidential-containers/trustee.git?rev=e65897a9ad4eb3ac69fa2ec75ed831200eb2acd7#e65897a9ad4eb3ac69fa2ec75ed831200eb2acd7"
+dependencies = [
+ "anyhow",
+ "base64 0.22.1",
+ "clap",
+ "jsonwebtoken",
+ "kbs_protocol",
+ "reqwest 0.13.2",
+ "serde",
+ "serde_json",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "kbs-types"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "010924a5f65328c9609598c895ea506c723e3c72e528aeaba0a9645d8330b718"
+dependencies = [
+ "base64 0.22.1",
+ "ear",
+ "serde",
+ "serde_json",
+ "sha2 0.10.9",
+ "sm3",
+ "strum 0.27.2",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "kbs_protocol"
+version = "0.1.0"
+source = "git+https://github.com/confidential-containers/guest-components.git?rev=1fcebcb66a3c21b62e852819d5b55212c90eba2a#1fcebcb66a3c21b62e852819d5b55212c90eba2a"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "attester",
+ "base64 0.22.1",
+ "crypto",
+ "jwt-simple",
+ "kbs-types",
+ "reqwest 0.13.2",
+ "resource_uri",
"serde",
"serde_json",
+ "serde_json_canonicalizer",
+ "sha2 0.11.0",
+ "shadow-rs",
+ "thiserror 2.0.18",
+ "tokio",
+ "tracing",
+ "url",
+ "zeroize",
+]
+
+[[package]]
+name = "keccak"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
]
[[package]]
@@ -2283,7 +2958,7 @@ dependencies = [
"http 1.4.2",
"jiff",
"k8s-openapi 0.27.1",
- "schemars",
+ "schemars 1.1.0",
"serde",
"serde-value",
"serde_json",
@@ -2302,7 +2977,7 @@ dependencies = [
"jiff",
"json-patch",
"k8s-openapi 0.28.0",
- "schemars",
+ "schemars 1.1.0",
"serde",
"serde-value",
"serde_json",
@@ -2385,12 +3060,36 @@ version = "0.2.184"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
+[[package]]
+name = "libgit2-sys"
+version = "0.18.5+1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "005d6ae6eac1912906073e069f7db60b1fa98e052a68227824afe3e3a1c59ca2"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "pkg-config",
+]
+
[[package]]
name = "libm"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
+[[package]]
+name = "libz-sys"
+version = "1.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
[[package]]
name = "lief"
version = "0.17.1"
@@ -2467,6 +3166,12 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
+[[package]]
+name = "litrs"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
+
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -2482,6 +3187,15 @@ version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+[[package]]
+name = "matchers"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
+dependencies = [
+ "regex-automata",
+]
+
[[package]]
name = "matchit"
version = "0.8.4"
@@ -2553,6 +3267,33 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "ml-dsa"
+version = "0.1.0-rc.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "163f15320f3fba11760c373af52d7f69d638482c2c350d877fb06513b1c3137c"
+dependencies = [
+ "const-oid 0.10.2",
+ "crypto-common 0.2.2",
+ "ctutils",
+ "hybrid-array",
+ "module-lattice",
+ "pkcs8 0.11.0",
+ "sha3",
+ "signature 3.0.0",
+]
+
+[[package]]
+name = "module-lattice"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe"
+dependencies = [
+ "ctutils",
+ "hybrid-array",
+ "num-traits",
+]
+
[[package]]
name = "moveit"
version = "0.6.0"
@@ -2585,6 +3326,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "num-bigint"
version = "0.4.6"
@@ -2597,25 +3347,25 @@ dependencies = [
[[package]]
name = "num-bigint-dig"
-version = "0.8.5"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82c79c15c05d4bf82b6f5ef163104cc81a760d8e874d38ac50ab67c8877b647b"
+checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
dependencies = [
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
- "rand",
+ "rand 0.8.5",
"smallvec",
"zeroize",
]
[[package]]
name = "num-conv"
-version = "0.1.0"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]]
name = "num-derive"
@@ -2751,6 +3501,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+[[package]]
+name = "opaque-debug"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+
[[package]]
name = "openssl"
version = "0.10.81"
@@ -2788,6 +3544,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
+[[package]]
+name = "openssl-src"
+version = "300.6.1+3.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "openssl-sys"
version = "0.9.117"
@@ -2796,6 +3561,7 @@ checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695"
dependencies = [
"cc",
"libc",
+ "openssl-src",
"pkg-config",
"vcpkg",
]
@@ -2813,9 +3579,13 @@ dependencies = [
"futures-util",
"hex",
"http 1.4.2",
+ "jiff",
"json-patch",
"jsonptr",
+ "jsonwebtoken",
+ "jsonwebtoken-openssl",
"k8s-openapi 0.28.0",
+ "kbs-client",
"kube 4.0.0",
"log",
"oci-client",
@@ -2879,7 +3649,7 @@ dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
- "rand_core",
+ "rand_core 0.6.4",
"sha2 0.10.9",
]
@@ -2948,36 +3718,79 @@ dependencies = [
]
[[package]]
-name = "pest_derive"
-version = "2.8.3"
+name = "pest_derive"
+version = "2.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a"
+dependencies = [
+ "pest",
+ "sha2 0.10.9",
+]
+
+[[package]]
+name = "phf"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+ "serde",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de"
+checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
dependencies = [
- "pest",
- "pest_generator",
+ "fastrand",
+ "phf_shared",
]
[[package]]
-name = "pest_generator"
-version = "2.8.3"
+name = "phf_macros"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843"
+checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
dependencies = [
- "pest",
- "pest_meta",
+ "phf_generator",
+ "phf_shared",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
-name = "pest_meta"
-version = "2.8.3"
+name = "phf_shared"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a"
+checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
dependencies = [
- "pest",
- "sha2 0.10.9",
+ "siphasher",
]
[[package]]
@@ -3018,9 +3831,9 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
- "der",
- "pkcs8",
- "spki",
+ "der 0.7.10",
+ "pkcs8 0.10.2",
+ "spki 0.7.3",
]
[[package]]
@@ -3029,8 +3842,18 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
- "der",
- "spki",
+ "der 0.7.10",
+ "spki 0.7.3",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7"
+dependencies = [
+ "der 0.8.0",
+ "spki 0.8.0",
]
[[package]]
@@ -3039,6 +3862,18 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+[[package]]
+name = "polyval"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "opaque-debug",
+ "universal-hash",
+]
+
[[package]]
name = "portable-atomic"
version = "1.11.1"
@@ -3152,6 +3987,22 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "psl-types"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
+
+[[package]]
+name = "publicsuffix"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
+dependencies = [
+ "idna",
+ "psl-types",
+]
+
[[package]]
name = "quote"
version = "1.0.45"
@@ -3175,7 +4026,18 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
- "rand_core",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
+dependencies = [
+ "chacha20",
+ "getrandom 0.4.1",
+ "rand_core 0.10.1",
]
[[package]]
@@ -3185,7 +4047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.6.4",
]
[[package]]
@@ -3197,6 +4059,12 @@ dependencies = [
"getrandom 0.2.16",
]
+[[package]]
+name = "rand_core"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -3327,6 +4195,8 @@ checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801"
dependencies = [
"base64 0.22.1",
"bytes",
+ "cookie",
+ "cookie_store",
"futures-core",
"futures-util",
"http 1.4.2",
@@ -3358,6 +4228,17 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "resource_uri"
+version = "0.1.0"
+source = "git+https://github.com/confidential-containers/guest-components.git?rev=1fcebcb66a3c21b62e852819d5b55212c90eba2a#1fcebcb66a3c21b62e852819d5b55212c90eba2a"
+dependencies = [
+ "anyhow",
+ "serde",
+ "serde_json",
+ "url",
+]
+
[[package]]
name = "rfc6979"
version = "0.4.0"
@@ -3378,15 +4259,15 @@ dependencies = [
"cfg-if",
"getrandom 0.2.16",
"libc",
- "untrusted",
+ "untrusted 0.9.0",
"windows-sys 0.52.0",
]
[[package]]
name = "rsa"
-version = "0.9.8"
+version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b"
+checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
dependencies = [
"const-oid 0.9.6",
"digest 0.10.7",
@@ -3394,11 +4275,11 @@ dependencies = [
"num-integer",
"num-traits",
"pkcs1",
- "pkcs8",
- "rand_core",
+ "pkcs8 0.10.2",
+ "rand_core 0.6.4",
"sha2 0.10.9",
- "signature",
- "spki",
+ "signature 2.2.0",
+ "spki 0.7.3",
"subtle",
"zeroize",
]
@@ -3495,7 +4376,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
- "untrusted",
+ "untrusted 0.9.0",
]
[[package]]
@@ -3506,7 +4387,7 @@ checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
dependencies = [
"ring",
"rustls-pki-types",
- "untrusted",
+ "untrusted 0.9.0",
]
[[package]]
@@ -3521,6 +4402,12 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+[[package]]
+name = "ryu-js"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15"
+
[[package]]
name = "schannel"
version = "0.1.28"
@@ -3530,6 +4417,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.1.0"
@@ -3568,7 +4467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring",
- "untrusted",
+ "untrusted 0.9.0",
]
[[package]]
@@ -3578,9 +4477,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
"base16ct",
- "der",
+ "der 0.7.10",
"generic-array",
- "pkcs8",
+ "pkcs8 0.10.2",
"subtle",
"zeroize",
]
@@ -3729,6 +4628,17 @@ dependencies = [
"zmij",
]
+[[package]]
+name = "serde_json_canonicalizer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe52319a927259afbfa5180c5157cd8167edfd3e8c254f9558c7fef44c5649f2"
+dependencies = [
+ "ryu-js",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "serde_path_to_error"
version = "0.1.20"
@@ -3761,6 +4671,38 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_with"
+version = "3.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c"
+dependencies = [
+ "base64 0.22.1",
+ "bs58",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.14.0",
+ "schemars 0.9.0",
+ "schemars 1.1.0",
+ "serde_core",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
+dependencies = [
+ "darling 0.23.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
@@ -3796,11 +4738,42 @@ dependencies = [
"digest 0.11.3",
]
+[[package]]
+name = "sha3"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1"
+dependencies = [
+ "digest 0.11.3",
+ "keccak",
+]
+
+[[package]]
+name = "shadow-rs"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dd39b4b2077bd36e60ca28c31d494046e747759cb9b507a7d177bb64787c39e"
+dependencies = [
+ "const_format",
+ "git2",
+ "is_debug",
+ "jiff",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
[[package]]
name = "shlex"
-version = "1.3.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "signal-hook-registry"
@@ -3818,7 +4791,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest 0.10.7",
- "rand_core",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "signature"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5"
+dependencies = [
+ "digest 0.11.3",
+ "rand_core 0.10.1",
]
[[package]]
@@ -3827,12 +4810,39 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+[[package]]
+name = "simple_asn1"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d"
+dependencies = [
+ "num-bigint",
+ "num-traits",
+ "thiserror 2.0.18",
+ "time",
+]
+
+[[package]]
+name = "siphasher"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
+
[[package]]
name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
+[[package]]
+name = "sm3"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebb9a3b702d0a7e33bc4d85a14456633d2b165c2ad839c5fd9a8417c1ab15860"
+dependencies = [
+ "digest 0.10.7",
+]
+
[[package]]
name = "smallvec"
version = "1.15.1"
@@ -3878,7 +4888,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
- "der",
+ "der 0.7.10",
+]
+
+[[package]]
+name = "spki"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f"
+dependencies = [
+ "base64ct",
+ "der 0.8.0",
]
[[package]]
@@ -3887,7 +4907,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f"
dependencies = [
- "cipher",
+ "cipher 0.4.4",
"ssh-encoding",
]
@@ -3912,11 +4932,11 @@ dependencies = [
"p256",
"p384",
"p521",
- "rand_core",
+ "rand_core 0.6.4",
"rsa",
"sec1",
"sha2 0.10.9",
- "signature",
+ "signature 2.2.0",
"ssh-cipher",
"ssh-encoding",
"subtle",
@@ -3940,6 +4960,9 @@ name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
+dependencies = [
+ "strum_macros 0.27.2",
+]
[[package]]
name = "strum"
@@ -3980,6 +5003,22 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+[[package]]
+name = "superboring"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efc6310a69b44420f3bf53d518077615b7d466cc57df7a80e404e7feb8c510f7"
+dependencies = [
+ "aes-gcm",
+ "aes-keywrap",
+ "getrandom 0.2.16",
+ "hmac-sha256",
+ "hmac-sha512",
+ "ml-dsa",
+ "rand 0.8.5",
+ "rsa",
+]
+
[[package]]
name = "supports-color"
version = "2.1.0"
@@ -4151,24 +5190,45 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "time"
-version = "0.3.44"
+version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
+ "itoa",
"num-conv",
"powerfmt",
- "serde",
+ "serde_core",
"time-core",
+ "time-macros",
]
[[package]]
name = "time-core"
-version = "0.1.6"
+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 = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
[[package]]
name = "tinystr"
@@ -4369,9 +5429,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
-version = "0.1.41"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"log",
"pin-project-lite",
@@ -4381,9 +5441,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.30"
+version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
@@ -4392,11 +5452,41 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.34"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
+checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
dependencies = [
+ "matchers",
+ "nu-ansi-term",
"once_cell",
+ "regex-automata",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
]
[[package]]
@@ -4434,7 +5524,7 @@ dependencies = [
"kube 4.0.0",
"log",
"percent-encoding",
- "rand_core",
+ "rand_core 0.6.4",
"serde",
"serde_json",
"serde_yaml",
@@ -4452,6 +5542,7 @@ version = "0.2.1"
dependencies = [
"anyhow",
"cfg-if",
+ "chrono",
"compute-pcrs-lib",
"k8s-openapi 0.28.0",
"kube 4.0.0",
@@ -4546,12 +5637,28 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common 0.1.6",
+ "subtle",
+]
+
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -4594,6 +5701,12 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -4639,6 +5752,15 @@ dependencies = [
"wit-bindgen 0.51.0",
]
+[[package]]
+name = "wasix"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae86f02046da16a333a9129d31451423e1657737ecdafed4193838a5f54c5cfe"
+dependencies = [
+ "wasi",
+]
+
[[package]]
name = "wasm-bindgen"
version = "0.2.108"
@@ -5264,6 +6386,20 @@ name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
[[package]]
name = "zerotrie"
diff --git a/Makefile b/Makefile
index e29e750b..e938e8a9 100644
--- a/Makefile
+++ b/Makefile
@@ -28,8 +28,10 @@ OPERATOR_IMAGE ?= $(REGISTRY)/trusted-cluster-operator:$(TAG)
COMPUTE_PCRS_IMAGE=$(REGISTRY)/compute-pcrs:$(TAG)
REG_SERVER_IMAGE=$(REGISTRY)/registration-server:$(TAG)
ATTESTATION_KEY_REGISTER_IMAGE=$(REGISTRY)/attestation-key-register:$(TAG)
-TRUSTEE_IMAGE ?= quay.io/trusted-execution-clusters/key-broker-service:v0.17.0
+
+TRUSTEE_IMAGE ?= quay.io/trusted-execution-clusters/key-broker-service:v0.20.0
TEST_IMAGE ?= quay.io/trusted-execution-clusters/fedora-coreos-kubevirt:42.20260622
+
# tagged as 42.20251012.2.0
APPROVED_IMAGE ?= quay.io/trusted-execution-clusters/fedora-coreos@sha256:6997f51fd27d1be1b5fc2e6cc3ebf16c17eb94d819b5d44ea8d6cf5f826ee773
diff --git a/operator/Cargo.toml b/operator/Cargo.toml
index 1496094a..8ca87628 100644
--- a/operator/Cargo.toml
+++ b/operator/Cargo.toml
@@ -31,6 +31,10 @@ serde_json.workspace = true
thiserror = "2.0.18"
tokio.workspace = true
toml = "1.1.2"
+kbs-client = {git = "https://github.com/confidential-containers/trustee.git", rev = "e65897a9ad4eb3ac69fa2ec75ed831200eb2acd7", default-features = false, features = ["native-tls"] }
+jsonwebtoken = { version = "10.4.0", default-features = false, features = ["use_pem"] }
+jsonwebtoken-openssl = "1.0.0"
+jiff = "0.2.29"
[dev-dependencies]
http.workspace = true
diff --git a/operator/src/attestation_key_register.rs b/operator/src/attestation_key_register.rs
index 332e5124..2fb6955b 100644
--- a/operator/src/attestation_key_register.rs
+++ b/operator/src/attestation_key_register.rs
@@ -331,7 +331,7 @@ async fn secret_reconcile(
match ev {
Event::Apply(_secret) => {
// On creation/update, just update the trustee deployment volumes
- trustee::update_attestation_keys(&ctx)
+ trustee::update_attestation_keys(ctx.client.clone())
.await
.map(|_| Action::await_change())
.map_err(|e| {
@@ -345,7 +345,7 @@ async fn secret_reconcile(
"AttestationKey secret {secret_name} is being deleted, updating trustee deployment volumes"
);
// Update trustee deployment - secrets with deletion_timestamp will be filtered out
- trustee::update_attestation_keys(&ctx)
+ trustee::update_attestation_keys(ctx.client.clone())
.await
.map(|_| Action::await_change())
.map_err(|e| {
diff --git a/operator/src/kbs-config.toml b/operator/src/kbs-config.toml
index fc1005d5..20b4a588 100644
--- a/operator/src/kbs-config.toml
+++ b/operator/src/kbs-config.toml
@@ -2,36 +2,35 @@
sockets = ["0.0.0.0:8080"]
[admin]
-type = "DenyAll"
+authorization_mode = "AuthenticatedAuthorization"
+
+ [admin.authentication.bearer_jwt]
+ identity_providers = [
+ { public_key_uri = "/opt/trustee/keys/public.pub" }
+ ]
+
+ [admin.authorization.regex_acl]
+ acls = [{ role = "admin", allowed_endpoints = "^/kbs/.+$" }]
[attestation_token]
-insecure_key = true
-attestation_token_type = "CoCo"
+insecure_header_jwk = true
[attestation_service]
type = "coco_as_builtin"
-work_dir = "/opt/trustee"
-policy_engine = "opa"
+timeout = 5
[attestation_service.attestation_token_broker]
- type = "Ear"
- policy_dir = "/opt/trustee/policies"
-
- [attestation_service.attestation_token_config]
duration_min = 5
[attestation_service.rvps_config]
type = "BuiltIn"
- [attestation_service.rvps_config.storage]
- type = "LocalJson"
- file_path = "/opt/trustee/reference-values.json"
-
-
[[plugins]]
name = "resource"
-type = "LocalFs"
-dir_path = "/opt/trustee/kbs-repository"
+storage_backend_type = "kvstorage"
+
+[storage_backend]
+storage_type = "LocalJson"
-[policy_engine]
-policy_path = "/opt/trustee/policy.rego"
+ [storage_backend.backends.local_json]
+ file_dir_path = "/opt/trustee/storage"
diff --git a/operator/src/main.rs b/operator/src/main.rs
index 03ff138e..97713675 100644
--- a/operator/src/main.rs
+++ b/operator/src/main.rs
@@ -15,7 +15,7 @@ use kube::runtime::controller::{Action, Controller};
use kube::runtime::reflector::{self, Store};
use kube::runtime::watcher;
use kube::{Api, Client};
-use log::{info, warn};
+use log::{error, info, warn};
use operator::{generate_owner_reference, upsert_condition};
use trusted_cluster_operator_lib::{TrustedExecutionCluster, TrustedExecutionClusterStatus};
@@ -28,12 +28,11 @@ mod register_server;
#[cfg(test)]
mod test_utils;
mod trustee;
-
use crate::conditions::*;
use operator::*;
/// Default tag for Trustee image
-const TRUSTEE_VERSION: &str = "v0.17.0";
+const TRUSTEE_VERSION: &str = "v0.20.0";
/// Default version tag for operator-managed component images
const COMPONENT_VERSION: &str = "v0.2.0";
/// Default registry
@@ -137,7 +136,6 @@ async fn reconcile(
warn!("Installation of a component failed: {e:?}\nRequeueing...");
return Ok(Action::requeue(Duration::from_secs(60)));
}
- reference_values::adopt_approved_images(kube_client, &cluster).await?;
let installed_condition = installed_condition(INSTALLED_REASON, generation, existing_status);
let changed = upsert_condition(&mut conditions, installed_condition);
@@ -167,10 +165,21 @@ async fn install_trustee_configuration(
.context("Failed to create the KBS configuration configmap")?;
info!("Generated configmap for the KBS configuration");
- trustee::generate_attestation_policy(client.clone(), owner_reference.clone())
- .await
- .context("Failed to create the attestation policy configmap")?;
- info!("Generated configmap for the attestation policy");
+ match reference_values::create_pcrs_config_map(client.clone()).await {
+ Ok(_) => info!("Created bare configmap for PCRs"),
+ Err(e) => error!("Failed to create the PCRs configmap: {e}"),
+ }
+
+ match trustee::generate_trustee_auth_keys_secret(client.clone(), owner_reference.clone()).await
+ {
+ Ok(_) => info!("Generate auth keys for the KBS API",),
+ Err(e) => error!("Failed to create the auth keys: {e}"),
+ }
+
+ match trustee::generate_rv_data(client.clone(), owner_reference.clone()).await {
+ Ok(_) => info!("Created configmap for reference values"),
+ Err(e) => error!("Failed to create the reference values configmap: {e}"),
+ }
let kbs_port = cluster.spec.trustee_kbs_port;
trustee::generate_kbs_service(client.clone(), owner_reference.clone(), kbs_port)
@@ -247,7 +256,7 @@ async fn install_attestation_key_register(
#[tokio::main]
async fn main() -> Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
-
+ let _ = jsonwebtoken_openssl::install_default();
let kube_client = Client::try_default().await?;
info!("trusted execution clusters operator",);
@@ -278,9 +287,9 @@ async fn main() -> Result<()> {
attestation_key_register::launch_ak_controller(ak_ctx.clone()).await;
attestation_key_register::launch_machine_ak_controller(ak_ctx.clone()).await;
attestation_key_register::launch_secret_ak_controller(ak_ctx).await;
- reference_values::create_pcrs_config_map(kube_client.clone()).await?;
reference_values::launch_rv_image_controller(kube_client.clone()).await;
reference_values::launch_rv_job_controller(kube_client.clone()).await;
+ trustee::launch_trustee_sync_controller(kube_client.clone()).await;
Controller::new(cl, watcher::Config::default())
.run(reconcile, controller_error_policy, ctx)
@@ -294,11 +303,9 @@ async fn main() -> Result<()> {
mod tests {
use http::{Method, Request, StatusCode};
use k8s_openapi::api::apps::v1::Deployment;
- use k8s_openapi::api::core::v1::{ConfigMap, Service};
+ use k8s_openapi::api::core::v1::{ConfigMap, Secret, Service};
use k8s_openapi::{apimachinery::pkg::apis::meta::v1::Time, jiff::Timestamp};
- use kube::api::ObjectList;
use kube::client::Body;
- use trusted_cluster_operator_lib::ApprovedImage;
use super::*;
use trusted_cluster_operator_test_utils::mock_client::*;
@@ -436,31 +443,26 @@ mod tests {
};
let clos = async |req: Request
, ctr| {
- if ctr < 8 && req.method() == Method::POST {
+ if ctr < 10 && req.method() == Method::POST {
use serde_json::to_string;
let resp = match ctr {
- // Trustee
- 0 => to_string(&ConfigMap::default()),
- 1 => to_string(&ConfigMap::default()),
- 2 => to_string(&Service::default()),
- 3 => to_string(&Deployment::default()),
- // Registration server
- 4 => to_string(&Deployment::default()),
- 5 => to_string(&Service::default()),
- // Attestation key register server
- 6 => to_string(&Deployment::default()),
- 7 => to_string(&Service::default()),
+ // install_trustee_configuration
+ 0 => to_string(&ConfigMap::default()), // trustee-data
+ 1 => to_string(&ConfigMap::default()), // image-pcrs
+ 2 => to_string(&Secret::default()), // trustee-auth
+ 3 => to_string(&ConfigMap::default()), // trustee-rv-data
+ 4 => to_string(&Service::default()), // kbs-service
+ 5 => to_string(&Deployment::default()), // trustee-deployment
+ // install_register_server
+ 6 => to_string(&Deployment::default()), // register-server
+ 7 => to_string(&Service::default()), // register-server-svc
+ // install_attestation_key_register
+ 8 => to_string(&Deployment::default()), // ak-register
+ 9 => to_string(&Service::default()), // ak-register-svc
_ => unreachable!("unexpected counter {ctr}"),
};
Ok(resp.unwrap())
- } else if ctr == 8 && req.method() == Method::GET {
- let object_list = ObjectList:: {
- items: Vec::new(),
- types: Default::default(),
- metadata: Default::default(),
- };
- Ok(serde_json::to_string(&object_list).unwrap())
- } else if ctr == 9 && req.method() == Method::PATCH {
+ } else if ctr == 10 && req.method() == Method::PATCH {
let body = req.into_body().collect_bytes().await.unwrap().to_vec();
let body = String::from_utf8_lossy(&body);
assert!(body.contains("ForeignCondition"),);
@@ -491,7 +493,7 @@ mod tests {
cluster.status = Some(TrustedExecutionClusterStatus {
conditions: Some(vec![pre_existing_installed, foreign_condition]),
});
- count_check!(10, clos, |client| {
+ count_check!(11, clos, |client| {
let result = reconcile(Arc::new(cluster), Arc::new(dummy_cluster_ctx(client))).await;
assert_eq!(result.unwrap(), Action::await_change());
});
diff --git a/operator/src/reference_values.rs b/operator/src/reference_values.rs
index f202c533..9566bfee 100644
--- a/operator/src/reference_values.rs
+++ b/operator/src/reference_values.rs
@@ -220,22 +220,6 @@ async fn adopt_approved_image(
Ok(())
}
-pub async fn adopt_approved_images(
- client: Client,
- cluster: &TrustedExecutionCluster,
-) -> Result<()> {
- let images: Api = Api::default_namespaced(client.clone());
- let images_list = images.list(&Default::default()).await?;
- for image in images_list.items.iter() {
- if image.metadata.deletion_timestamp.is_none()
- && let Some(name) = image.metadata.name.as_ref()
- {
- adopt_approved_image(client.clone(), name, cluster).await?;
- }
- }
- Ok(())
-}
-
async fn image_reconcile(
image: Arc,
client: Arc,
@@ -247,6 +231,12 @@ async fn image_reconcile(
.await
.map_err(|e| -> ControllerError { e.into() })?;
+ if let Some(ref cluster) = cluster {
+ adopt_approved_image(kube_client.clone(), &name, cluster)
+ .await
+ .map_err(|e| -> ControllerError { e.into() })?;
+ }
+
let images: Api = Api::default_namespaced(kube_client.clone());
finalizer(&images, APPROVED_IMAGE_FINALIZER, image, |ev| async {
match ev {
@@ -277,19 +267,6 @@ async fn image_add_reconcile(
info!("TrustedExecutionCluster is being deleted, deferring image processing for {name}");
return Ok(Action::requeue(Duration::from_secs(5)));
}
- let uid_owns = |uid: &String| {
- let refs = image.metadata.owner_references.as_ref();
- refs.map(|os| os.iter().any(|o| o.uid == *uid))
- };
- let cluster_owns = |cluster: &TrustedExecutionCluster| {
- let uid = cluster.metadata.uid.as_ref();
- uid.and_then(uid_owns).unwrap_or(false)
- };
- // Adopt the image by adding TEC as owner reference if not already owned
- if !cluster_owns(&cluster) {
- adopt_approved_image(client.clone(), name, &cluster).await?;
- }
-
let (action, reason) = match handle_new_image(client.clone(), image).await {
Ok(reason) => (Action::await_change(), reason),
Err(e) => {
@@ -428,7 +405,6 @@ mod tests {
use http::{Method, Request, StatusCode};
use k8s_openapi::api::batch::v1::JobStatus;
use k8s_openapi::apimachinery::pkg::apis::meta::v1::Time;
- use kube::api::ObjectList;
use kube::client::Body;
use trusted_cluster_operator_test_utils::mock_client::*;
use trusted_cluster_operator_test_utils::test_error_method;
@@ -491,12 +467,13 @@ mod tests {
Ok(serde_json::to_string(&dummy_pcrs_map()).unwrap())
}
(2, &Method::GET) | (3, &Method::PUT) => {
- assert!(req.uri().path().contains(trustee::TRUSTEE_DATA_MAP));
+ assert!(req.uri().path().contains(trustee::TRUSTEE_RV_MAP));
Ok(serde_json::to_string(&dummy_trustee_map()).unwrap())
}
+ (4, &Method::GET) => Err(StatusCode::NOT_FOUND),
_ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
};
- count_check!(4, clos, |client| {
+ count_check!(5, clos, |client| {
let job = Arc::new(dummy_job());
let result = job_reconcile(job, Arc::new(client)).await.unwrap();
assert_eq!(result, Action::await_change());
@@ -563,37 +540,6 @@ mod tests {
test_error_method!(clos, Method::PATCH);
}
- #[tokio::test]
- async fn test_adopt_approved_images() {
- let cluster = dummy_cluster();
- let clos = async |req: Request<_>, ctr| {
- if ctr == 0 && req.method() == Method::GET {
- let mut deleted = dummy_image();
- deleted.metadata.deletion_timestamp = Some(Time(Timestamp::now()));
- let list = ObjectList {
- items: vec![dummy_image(), deleted, dummy_image()],
- types: Default::default(),
- metadata: Default::default(),
- };
- Ok(serde_json::to_string(&list).unwrap())
- } else if ctr < 3 && req.method() == Method::PATCH {
- Ok(serde_json::to_string(&dummy_image()).unwrap())
- } else {
- panic!("unexpected API interaction: {req:?}, counter {ctr}")
- }
- };
- count_check!(3, clos, |client| {
- assert!(adopt_approved_images(client, &cluster).await.is_ok());
- });
- }
-
- #[tokio::test]
- async fn test_adopt_approved_images_error() {
- let cluster = dummy_cluster();
- let clos = |client| adopt_approved_images(client, &cluster);
- test_error_method!(clos, Method::GET);
- }
-
// handle_new_image and its caller image_add_reconcile are
// inherently online functions and not tested here
@@ -608,12 +554,13 @@ mod tests {
Ok(serde_json::to_string(&dummy_pcrs_map()).unwrap())
}
(3, &Method::GET) | (4, &Method::PUT) => {
- assert!(req.uri().path().contains(trustee::TRUSTEE_DATA_MAP));
+ assert!(req.uri().path().contains(trustee::TRUSTEE_RV_MAP));
Ok(serde_json::to_string(&dummy_trustee_map()).unwrap())
}
+ (5, &Method::GET) => Err(StatusCode::NOT_FOUND),
_ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
};
- count_check!(5, clos, |client| {
+ count_check!(6, clos, |client| {
assert!(image_remove_reconcile(client, image, cluster).await.is_ok());
});
}
diff --git a/operator/src/register_server.rs b/operator/src/register_server.rs
index 2a57ded2..96b689e3 100644
--- a/operator/src/register_server.rs
+++ b/operator/src/register_server.rs
@@ -139,7 +139,7 @@ async fn keygen_reconcile(
async {
let owner_reference = generate_owner_reference(&Arc::unwrap_or_clone(machine))?;
trustee::generate_secret(kube_client.clone(), id, owner_reference).await?;
- trustee::mount_secret(kube_client, id).await
+ trustee::send_secret(kube_client, id).await
}
.await
.map(|_| Action::await_change())
@@ -185,10 +185,12 @@ async fn keygen_reconcile(
}
}
- trustee::unmount_secret(kube_client, id)
- .await
- .map(|_| Action::await_change())
- .map_err(|e| finalizer::Error::::CleanupFailed(e.into()))
+ trustee::delete_secret(kube_client, id).await.map_err(|e| {
+ finalizer::Error::::CleanupFailed(
+ anyhow!("failed to delete secret for machine {id}: {e}").into(),
+ )
+ })?;
+ Ok(Action::await_change())
}
}
})
diff --git a/operator/src/test_utils.rs b/operator/src/test_utils.rs
index af4ed28c..2c2527c5 100644
--- a/operator/src/test_utils.rs
+++ b/operator/src/test_utils.rs
@@ -2,12 +2,17 @@
//
// SPDX-License-Identifier: MIT
+use crate::trustee;
use compute_pcrs_lib::Pcr;
-use k8s_openapi::{api::core::v1::ConfigMap, jiff::Timestamp};
+use k8s_openapi::apimachinery::pkg::apis::meta::v1::OwnerReference;
+use k8s_openapi::{
+ api::core::v1::{ConfigMap, Secret},
+ jiff::Timestamp,
+};
+use kube::api::ObjectMeta;
use std::collections::BTreeMap;
-
-use crate::trustee;
use trusted_cluster_operator_lib::reference_values::{ImagePcr, ImagePcrs, PCR_CONFIG_FILE};
+use trusted_cluster_operator_lib::{Machine, MachineSpec};
pub fn dummy_pcrs() -> ImagePcrs {
ImagePcrs(BTreeMap::from([(
@@ -41,6 +46,26 @@ pub fn dummy_trustee_map() -> ConfigMap {
}
}
+pub fn dummy_trustee_auth() -> Secret {
+ let key_pair =
+ trustee::generate_ed25519_key_pair().expect("Failed to generate ed25519 key pair");
+ let data = BTreeMap::from([
+ (
+ trustee::TRUSTEE_AUTH_PRIV_KEY.to_string(),
+ k8s_openapi::ByteString(key_pair.private_key_pem),
+ ),
+ (
+ trustee::TRUSTEE_AUTH_PUB_KEY.to_string(),
+ k8s_openapi::ByteString(key_pair.public_key_pem),
+ ),
+ ]);
+
+ Secret {
+ data: Some(data),
+ ..Default::default()
+ }
+}
+
pub fn dummy_pcrs_map() -> ConfigMap {
let data = BTreeMap::from([(
PCR_CONFIG_FILE.to_string(),
@@ -51,3 +76,45 @@ pub fn dummy_pcrs_map() -> ConfigMap {
..Default::default()
}
}
+
+pub fn dummy_machine(id: &str) -> Machine {
+ Machine {
+ metadata: ObjectMeta {
+ name: Some(id.to_string()),
+ ..Default::default()
+ },
+ spec: MachineSpec { id: id.to_string() },
+ status: None,
+ }
+}
+
+pub fn dummy_secret() -> Secret {
+ let data = BTreeMap::from([(
+ "root".to_string(),
+ k8s_openapi::ByteString(b"secret-data".to_vec()),
+ )]);
+ Secret {
+ data: Some(data),
+ ..Default::default()
+ }
+}
+
+pub fn dummy_ak_secret(name: &str) -> Secret {
+ Secret {
+ metadata: ObjectMeta {
+ name: Some(name.to_string()),
+ owner_references: Some(vec![OwnerReference {
+ kind: "AttestationKey".to_string(),
+ name: name.to_string(),
+ uid: "ak-uid".to_string(),
+ ..Default::default()
+ }]),
+ ..Default::default()
+ },
+ data: Some(BTreeMap::from([(
+ "public_key".to_string(),
+ k8s_openapi::ByteString(b"test-ak-public-key".to_vec()),
+ )])),
+ ..Default::default()
+ }
+}
diff --git a/operator/src/tpm.rego b/operator/src/tpm.rego
index abf6ea6d..f94b74a5 100644
--- a/operator/src/tpm.rego
+++ b/operator/src/tpm.rego
@@ -9,6 +9,7 @@ executables := 3 if {
input.tpm.pcr04 in query_reference_value("tpm_pcr4")
input.tpm.pcr14 in query_reference_value("tpm_pcr14")
+ input.tpm.ak_public in query_reference_value("trusted_aks")
}
# Azure SNP vTPM validation
executables := 3 if {
diff --git a/operator/src/trustee.rs b/operator/src/trustee.rs
index d8d63d16..530483d4 100644
--- a/operator/src/trustee.rs
+++ b/operator/src/trustee.rs
@@ -4,44 +4,55 @@
//
// SPDX-License-Identifier: MIT
-use crate::attestation_key_register::AkContextData;
use anyhow::{Context, Result};
use base64::{Engine as _, engine::general_purpose};
use chrono::{DateTime, Utc};
use clevis_pin_trustee_lib::Key as ClevisKey;
+use futures_util::StreamExt;
use k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec};
use k8s_openapi::api::core::v1::{
- ConfigMap, ConfigMapVolumeSource, Container, ContainerPort, EmptyDirVolumeSource, EnvVar,
- KeyToPath, PodSpec, PodTemplateSpec, ProjectedVolumeSource, Secret, SecretProjection,
- SecretVolumeSource, Service, ServicePort, ServiceSpec, Volume, VolumeMount, VolumeProjection,
+ ConfigMap, ConfigMapVolumeSource, Container, ContainerPort, EnvVar, KeyToPath, PodSpec,
+ PodTemplateSpec, Secret, SecretVolumeSource, Service, ServicePort, ServiceSpec, Volume,
+ VolumeMount,
};
use k8s_openapi::apimachinery::pkg::{
apis::meta::v1::{LabelSelector, OwnerReference},
util::intstr::IntOrString,
};
+
use kube::{
Api, Client, Resource,
- api::{ObjectMeta, Patch, PatchParams},
- runtime::reflector::ObjectRef,
+ api::ObjectMeta,
+ runtime::{
+ controller::{Action, Controller},
+ watcher,
+ },
+};
+use log::{info, warn};
+use operator::{
+ ControllerError, TLS_DIR, controller_error_policy, controller_info, create_or_info_if_exists,
+ read_certificate,
};
-use log::info;
-use operator::{TLS_DIR, create_or_info_if_exists, read_certificate};
-use serde::{Serialize, Serializer};
+
+use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
+use serde::{Deserialize, Serialize, Serializer};
use serde_json::{Value::String as JsonString, json};
use std::collections::BTreeMap;
+use std::sync::Arc;
-use trusted_cluster_operator_lib::endpoints::*;
use trusted_cluster_operator_lib::reference_values::*;
+use trusted_cluster_operator_lib::{Machine, endpoints::*};
-const TRUSTEE_DATA_DIR: &str = "/opt/trustee";
-pub const TRUSTEE_SECRETS_PATH: &str = "/opt/trustee/kbs-repository/default";
+const TRUSTEE_DATA_DIR: &str = "/etc/kbs";
const KBS_CONFIG_FILE: &str = "kbs-config.toml";
-pub(crate) const REFERENCE_VALUES_FILE: &str = "reference-values.json";
pub(crate) const TRUSTEE_DATA_MAP: &str = "trustee-data";
-const ATT_POLICY_MAP: &str = "attestation-policy";
-const TRUSTED_AK_KEYS_VOLUME: &str = "trusted-ak-keys";
-const TRUSTED_AK_KEYS_DIR: &str = "/etc/tpm/trusted_ak_keys";
+pub(crate) const TRUSTEE_RV_MAP: &str = "trustee-rv-data";
+pub(crate) const REFERENCE_VALUES_FILE: &str = "reference-values.json";
+const TRUSTEE_AUTH_SECRET: &str = "trustee-auth";
+const TRUSTEE_AUTH_KEY_DIR: &str = "/opt/trustee/keys";
+pub(crate) const TRUSTEE_AUTH_PUB_KEY: &str = "public.pub";
+pub(crate) const TRUSTEE_AUTH_PRIV_KEY: &str = "private.key";
fn primitive_date_time_to_str(d: &DateTime, s: S) -> Result
where
@@ -54,7 +65,7 @@ where
/// reference_value_provider_service::reference_value::ReferenceValue
/// (cannot import directly because its expiration doesn't serialize
/// right)
-#[derive(Serialize)]
+#[derive(Serialize, Deserialize)]
struct ReferenceValue {
pub version: String,
pub name: String,
@@ -93,24 +104,79 @@ fn recompute_reference_values(image_pcrs: ImagePcrs) -> Vec {
}
pub async fn update_reference_values(client: Client) -> Result<()> {
- let config_maps: Api = Api::default_namespaced(client);
+ let config_maps: Api = Api::default_namespaced(client.clone());
let image_pcrs_map = config_maps.get(PCR_CONFIG_MAP).await?;
let reference_values = recompute_reference_values(get_image_pcrs(image_pcrs_map)?);
let rv_json = serde_json::to_string(&reference_values)?;
- let mut trustee_map = config_maps.get(TRUSTEE_DATA_MAP).await?;
- let err = format!("ConfigMap {TRUSTEE_DATA_MAP} existed, but had no data");
- let trustee_data = trustee_map.data.as_mut().context(err)?;
- trustee_data.insert(REFERENCE_VALUES_FILE.to_string(), rv_json);
-
+ let mut rv_map = config_maps.get(TRUSTEE_RV_MAP).await?;
+ let err = format!("ConfigMap {TRUSTEE_RV_MAP} existed, but had no data");
+ let rv_data = rv_map.data.as_mut().context(err)?;
+ rv_data.insert(REFERENCE_VALUES_FILE.to_string(), rv_json);
config_maps
- .replace(TRUSTEE_DATA_MAP, &Default::default(), &trustee_map)
+ .replace(TRUSTEE_RV_MAP, &Default::default(), &rv_map)
.await?;
+
+ if let Err(e) = sync_reference_values(&client, &reference_values).await {
+ warn!(
+ "Failed to sync reference values to KBS (will retry on next deployment reconcile): {e}"
+ );
+ }
info!("Recomputed reference values");
Ok(())
}
+async fn sync_reference_values(client: &Client, reference_values: &[ReferenceValue]) -> Result<()> {
+ let auth_token = get_auth_key_token(client).await?;
+ let (url, certs) = get_kbs_connection(client).await?;
+ for rv in reference_values {
+ kbs_client::set_sample_rv(
+ url.clone(),
+ rv.name.clone(),
+ rv.value.clone(),
+ Some(auth_token.clone()),
+ certs.clone(),
+ )
+ .await?;
+ }
+ info!("Sent {} reference values to KBS", reference_values.len());
+ Ok(())
+}
+
+async fn sync_reference_values_from_configmap(client: &Client) -> Result<()> {
+ let config_maps: Api = Api::default_namespaced(client.clone());
+ let rv_map = config_maps.get(TRUSTEE_RV_MAP).await?;
+ let data = rv_map.data.context("RV ConfigMap has no data")?;
+ let rv_json = match data.get(REFERENCE_VALUES_FILE) {
+ Some(json) => json,
+ None => {
+ info!("No reference values in ConfigMap yet, skipping sync");
+ return Ok(());
+ }
+ };
+ let reference_values: Vec = serde_json::from_str(rv_json)?;
+ if reference_values.is_empty() {
+ return Ok(());
+ }
+ sync_reference_values(client, &reference_values).await
+}
+
+pub struct Ed25519KeyPair {
+ pub private_key_pem: Vec,
+ pub public_key_pem: Vec,
+}
+
+pub fn generate_ed25519_key_pair() -> Result {
+ let key = openssl::pkey::PKey::generate_ed25519()?;
+ let private_key_pem = key.private_key_to_pem_pkcs8()?;
+ let public_key_pem = key.public_key_to_pem()?;
+ Ok(Ed25519KeyPair {
+ private_key_pem,
+ public_key_pem,
+ })
+}
+
fn generate_luks_key() -> Result> {
// Constraint: 32 bytes b64-encoded, thus 24
let mut pass = [0; 24];
@@ -122,88 +188,214 @@ fn generate_luks_key() -> Result> {
};
serde_json::to_vec(&jwk).map_err(Into::into)
}
+async fn get_auth_key_token(client: &Client) -> Result {
+ let secret_api: Api = Api::default_namespaced(client.clone());
+ let auth_secret = secret_api.get(TRUSTEE_AUTH_SECRET).await?;
+ let auth_data = auth_secret.data.context("Auth secret has no data")?;
+ let auth_key_bytes = auth_data
+ .get("private.key")
+ .context("Auth secret missing private.key")?;
+
+ let claims = json!({
+ "role": "admin",
+ "exp": i32::MAX
+ });
+
+ let encoding_key = EncodingKey::from_ed_pem(auth_key_bytes.0.as_slice())?;
+
+ let token = encode(&Header::new(Algorithm::EdDSA), &claims, &encoding_key)?;
+ Ok(token)
+}
+async fn get_kbs_connection(client: &Client) -> Result<(String, Vec)> {
+ let tec = trusted_cluster_operator_lib::get_trusted_execution_cluster(client.clone()).await?;
+ let secret_api: Api = Api::default_namespaced(client.clone());
+
+ if let Some(secret_name) = &tec.spec.trustee_secret {
+ if let Ok(secret) = secret_api.get(secret_name).await {
+ if let Some(ca_crt) = secret.data.as_ref().and_then(|d| d.get("ca.crt")) {
+ let ca_pem = String::from_utf8(ca_crt.0.clone())
+ .context("ca certificate is not valid UTF-8")?;
+ let trustee_addr = format!(
+ "https://{}",
+ tec.spec
+ .public_trustee_addr
+ .as_ref()
+ .context("TrustedExecutionCluster missing public_trustee_addr HTTPS")?
+ );
+ return Ok((trustee_addr, vec![ca_pem]));
+ }
+ }
+ }
+
+ Ok((
+ format!(
+ "http://{}",
+ tec.spec
+ .public_trustee_addr
+ .as_ref()
+ .context("TrustedExecutionCluster missing public_trustee_addr HTTP")?
+ ),
+ vec![],
+ ))
+}
-fn generate_secret_volume(id: &str) -> (Volume, VolumeMount) {
- (
- Volume {
- name: id.to_string(),
- secret: Some(SecretVolumeSource {
- secret_name: Some(id.to_string()),
- ..Default::default()
- }),
- ..Default::default()
- },
- VolumeMount {
- name: id.to_string(),
- mount_path: format!("{TRUSTEE_SECRETS_PATH}/{id}"),
- ..Default::default()
- },
+pub fn secret_path(id: &str) -> String {
+ format!("default/{id}/root")
+}
+
+pub async fn send_secret(client: Client, id: &str) -> Result<()> {
+ let secret_api: Api = Api::default_namespaced(client.clone());
+ let auth_key_token = get_auth_key_token(&client).await?;
+ let (url, certs) = get_kbs_connection(&client).await?;
+ let secret = secret_api.get(id).await?;
+ let secret_data = secret.data.context("Secret has no data")?;
+ let resource_bytes = secret_data
+ .get("root")
+ .context("Secret missing root key")?
+ .0
+ .clone();
+ let path = secret_path(id);
+ info!("Sending secret {id} to KBS API...");
+ kbs_client::set_resource(&url, Some(auth_key_token), resource_bytes, &path, certs).await?;
+ info!("Secret {id} sent successfully");
+ Ok(())
+}
+
+pub async fn delete_secret(client: Client, id: &str) -> Result<()> {
+ let auth_key_token = get_auth_key_token(&client).await?;
+ let (url, certs) = get_kbs_connection(&client).await?;
+ let path = secret_path(id);
+ info!("Deleting secret {id} to KBS API...");
+ kbs_client::delete_resource(&url, Some(auth_key_token), &path, certs).await?;
+ info!("Secret {id} deleted successfully");
+ Ok(())
+}
+
+pub async fn register_ak(client: Client, ak: &str) -> Result<()> {
+ let auth_key_token = get_auth_key_token(&client).await?;
+ let (url, certs) = get_kbs_connection(&client).await?;
+ info!("Registering AK to KBS API...");
+ kbs_client::set_sample_rv(
+ url.to_string(),
+ "trusted_aks".to_string(),
+ JsonString(ak.to_string()),
+ Some(auth_key_token),
+ certs,
)
+ .await?;
+ info!("AK registered successfully");
+ Ok(())
}
-pub async fn mount_secret(client: Client, id: &str) -> Result<()> {
- let result = do_mount_secret(client, id, true).await;
- info!("Mounted secret {id} to {TRUSTEE_DEPLOYMENT}");
- result
+pub async fn sync_resource_policy(client: Client) -> Result<()> {
+ let auth_token = get_auth_key_token(&client).await?;
+ let (url, certs) = get_kbs_connection(&client).await?;
+ let policy = include_str!("resource.rego");
+ info!("Sending resource policy to KBS API...");
+ kbs_client::set_resource_policy(&url, Some(auth_token), policy.as_bytes().to_vec(), certs)
+ .await?;
+ info!("Resource policy set successfully");
+ Ok(())
}
-pub async fn unmount_secret(client: Client, id: &str) -> Result<()> {
- let result = do_mount_secret(client, id, false).await;
- info!("Unmounted secret {id} from {TRUSTEE_DEPLOYMENT}");
- result
+pub async fn sync_attestation_policy(client: Client) -> Result<()> {
+ let auth_token = get_auth_key_token(&client).await?;
+ let (url, certs) = get_kbs_connection(&client).await?;
+ let policy = include_str!("tpm.rego");
+ info!("Sending attestation policy to KBS API...");
+ kbs_client::set_attestation_policy(
+ &url,
+ Some(auth_token),
+ policy.as_bytes().to_vec(),
+ Some("rego".to_string()),
+ Some("default_cpu".to_string()),
+ certs,
+ )
+ .await?;
+ info!("Attestation policy set successfully");
+ Ok(())
}
-pub async fn do_mount_secret(client: Client, id: &str, add: bool) -> Result<()> {
- let deployments: Api = Api::default_namespaced(client);
- let mut deployment = deployments.get(TRUSTEE_DEPLOYMENT).await?;
+pub async fn sync_all_machine_luks_key(client: Client) -> Result<()> {
+ let machine_api: Api = Api::default_namespaced(client.clone());
+ let machine_list = machine_api.list(&Default::default()).await?;
- let err = format!("Deployment {TRUSTEE_DEPLOYMENT} existed, but had no spec");
- let depl_spec = deployment.spec.as_mut().context(err)?;
- let err = format!("Deployment {TRUSTEE_DEPLOYMENT} existed, but had no pod spec");
- let pod_spec = depl_spec.template.spec.as_mut().context(err)?;
- let err = format!("Deployment {TRUSTEE_DEPLOYMENT} existed, but had no containers");
- let container = pod_spec.containers.get_mut(0).context(err)?;
- let vol_mounts = container.volume_mounts.get_or_insert_default();
+ let machine_ids: Vec = machine_list
+ .items
+ .iter()
+ .map(|machine| machine.spec.id.clone())
+ .collect();
- if add {
- let (volume, volume_mount) = generate_secret_volume(id);
- pod_spec.volumes.get_or_insert_default().push(volume);
- vol_mounts.push(volume_mount);
- } else {
- let vol_result = pod_spec.volumes.as_mut().and_then(|vs| {
- let pos = vs.iter().position(|v| v.name == id);
- pos.map(|p| vs.swap_remove(p))
- });
- if vol_result.is_none() {
- info!("Secret {id} was to be dropped, but volume had already been removed");
+ info!("Syncing {} machine luks key to KBS", machine_ids.len());
+ for id in &machine_ids {
+ if let Err(e) = send_secret(client.clone(), id).await {
+ warn!("Failed to sync secret {id} to KBS: {e}");
}
- let vol_mount_result = container.volume_mounts.as_mut().and_then(|vms| {
- let pos = vms.iter().position(|v| v.name == id);
- pos.map(|p| vms.swap_remove(p))
- });
- if vol_mount_result.is_none() {
- info!("Secret {id} was to be dropped, but volume mount had already been removed");
+ }
+ Ok(())
+}
+
+async fn trustee_deployment_reconcile(
+ deployment: Arc,
+ client: Arc,
+) -> Result {
+ if let Some(status) = &deployment.status {
+ if let Some(is_available) = &status.conditions {
+ if is_available
+ .iter()
+ .any(|c| c.type_ == "Available" && c.status == "True")
+ {
+ let c = Arc::unwrap_or_clone(client.clone());
+ if let Err(e) = sync_resource_policy(c.clone()).await {
+ warn!("Failed to sync resource policy to KBS: {e}");
+ }
+ if let Err(e) = sync_attestation_policy(c.clone()).await {
+ warn!("Failed to sync attestation policy to KBS: {e}");
+ }
+ if let Err(e) = sync_reference_values_from_configmap(&c).await {
+ warn!("Failed to sync reference values to KBS: {e}");
+ }
+ sync_all_machine_luks_key(c.clone())
+ .await
+ .map_err(ControllerError::Anyhow)?;
+ update_attestation_keys(c)
+ .await
+ .map_err(ControllerError::Anyhow)?;
+ }
}
}
+ Ok(Action::await_change())
+}
- deployments
- .replace(TRUSTEE_DEPLOYMENT, &Default::default(), &deployment)
- .await?;
- Ok(())
+pub async fn launch_trustee_sync_controller(client: Client) {
+ let deployments: Api = Api::default_namespaced(client.clone());
+ let watcher_config = watcher::Config {
+ label_selector: Some(format!("app={TRUSTEE_APP_LABEL}")),
+ ..Default::default()
+ };
+ tokio::spawn(
+ Controller::new(deployments, watcher_config)
+ .run(
+ trustee_deployment_reconcile,
+ controller_error_policy,
+ Arc::new(client),
+ )
+ .for_each(controller_info),
+ );
}
-pub async fn update_attestation_keys(ctx: &AkContextData) -> Result<()> {
- let client = &ctx.client;
- let ak_secrets: Vec = ctx
- .secret_store
- .state()
- .into_iter()
+pub async fn update_attestation_keys(client: Client) -> Result<()> {
+ let secrets: Api = Api::default_namespaced(client.clone());
+ let secret_list = secrets.list(&Default::default()).await?;
+
+ let ak_secrets: Vec = secret_list
+ .items
+ .iter()
.filter(|secret| {
// Filter out secrets that are being deleted
if secret.metadata.deletion_timestamp.is_some() {
return false;
}
-
secret
.metadata
.owner_references
@@ -211,125 +403,18 @@ pub async fn update_attestation_keys(ctx: &AkContextData) -> Result<()> {
.map(|owners| owners.iter().any(|owner| owner.kind == "AttestationKey"))
.unwrap_or(false)
})
- .filter_map(|secret| secret.metadata.name.clone())
- .collect();
-
- let ns = client.default_namespace().to_string();
- let Some(deployment) = ctx
- .deployment_store
- .get(&ObjectRef::new(TRUSTEE_DEPLOYMENT).within(&ns))
- .map(std::sync::Arc::unwrap_or_clone)
- else {
- // Trustee deployment is not (yet or no longer) present — nothing to patch.
- info!("{TRUSTEE_DEPLOYMENT} not found in cache, skipping attestation key volume update");
- return Ok(());
- };
- let deployments: Api = Api::default_namespaced(client.clone());
- let err = format!("Deployment {TRUSTEE_DEPLOYMENT} existed, but had no spec");
- let depl_spec = deployment.spec.as_ref().context(err)?;
- let err = format!("Deployment {TRUSTEE_DEPLOYMENT} existed, but had no pod spec");
- let pod_spec = depl_spec.template.spec.as_ref().context(err)?;
-
- // Get existing volumes and volumeMounts, filtering out the attestation key volume
- let mut volumes: Vec = pod_spec
- .volumes
- .as_ref()
- .map(|v| {
- v.iter()
- .filter(|vol| vol.name != TRUSTED_AK_KEYS_VOLUME)
- .cloned()
- .collect()
- })
- .unwrap_or_default();
-
- let err = format!("Deployment {TRUSTEE_DEPLOYMENT} existed, but had no containers");
- let container = pod_spec.containers.first().context(err)?;
- let mut vol_mounts: Vec = container
- .volume_mounts
- .as_ref()
- .map(|vm| {
- vm.iter()
- .filter(|mount| mount.name != TRUSTED_AK_KEYS_VOLUME)
- .cloned()
- .collect()
+ .filter_map(|secret| {
+ secret
+ .data
+ .as_ref()
+ .and_then(|d| String::from_utf8(d.get("public_key").unwrap().0.clone()).ok())
})
- .unwrap_or_default();
-
- if ak_secrets.is_empty() {
- info!(
- "No AttestationKey secrets found, removing projected volume from {TRUSTEE_DEPLOYMENT}"
- );
- } else {
- // Build the projected volume with all AttestationKey secrets
- let projections: Vec = ak_secrets
- .iter()
- .map(|secret_name| VolumeProjection {
- secret: Some(SecretProjection {
- name: secret_name.to_string(),
- items: Some(vec![KeyToPath {
- key: "public_key".to_string(),
- path: format!("{secret_name}.pub"),
- ..Default::default()
- }]),
- ..Default::default()
- }),
- ..Default::default()
- })
- .collect();
-
- let projected_volume = Volume {
- name: TRUSTED_AK_KEYS_VOLUME.to_string(),
- projected: Some(ProjectedVolumeSource {
- sources: Some(projections),
- ..Default::default()
- }),
- ..Default::default()
- };
-
- volumes.push(projected_volume);
-
- vol_mounts.push(VolumeMount {
- name: TRUSTED_AK_KEYS_VOLUME.to_string(),
- mount_path: TRUSTED_AK_KEYS_DIR.to_string(),
- ..Default::default()
- });
- }
-
- // Check if volumes or volumeMounts have changed
- let volumes_changed = pod_spec.volumes.as_ref() != Some(&volumes);
- let vol_mounts_changed = container.volume_mounts.as_ref() != Some(&vol_mounts);
-
- if volumes_changed || vol_mounts_changed {
- // Patch the deployment with updated volumes and volumeMounts
- let patch = json!({
- "apiVersion": "apps/v1",
- "kind": "Deployment",
- "metadata": {
- "name": TRUSTEE_DEPLOYMENT
- },
- "spec": {
- "template": {
- "spec": {
- "volumes": volumes,
- "containers": [{
- "name": "kbs",
- "volumeMounts": vol_mounts
- }]
- }
- }
- }
- });
+ .collect();
- deployments
- .patch(
- TRUSTEE_DEPLOYMENT,
- &PatchParams::apply("trusted-cluster-operator").force(),
- &Patch::Apply(&patch),
- )
- .await?;
- info!("Successfully patched {TRUSTEE_DEPLOYMENT} with attestation key volumes");
- } else {
- info!("No changes to attestation key volumes, skipping deployment update");
+ for ak in ak_secrets {
+ if let Err(e) = register_ak(client.clone(), &ak).await {
+ warn!("Failed to register AK {ak} to KBS: {e}");
+ }
}
Ok(())
@@ -356,27 +441,32 @@ pub async fn generate_secret(
Ok(())
}
-pub async fn generate_attestation_policy(
+pub async fn generate_trustee_auth_keys_secret(
client: Client,
owner_reference: OwnerReference,
) -> Result<()> {
- let policy_rego = include_str!("tpm.rego");
+ let key_pair = generate_ed25519_key_pair()?;
let data = BTreeMap::from([
- ("default_cpu.rego".to_string(), policy_rego.to_string()),
- // Must create GPU policy or Trustee will attempt to write one to the read-only mount
- ("default_gpu.rego".to_string(), String::new()),
+ (
+ TRUSTEE_AUTH_PRIV_KEY.to_string(),
+ k8s_openapi::ByteString(key_pair.private_key_pem),
+ ),
+ (
+ TRUSTEE_AUTH_PUB_KEY.to_string(),
+ k8s_openapi::ByteString(key_pair.public_key_pem),
+ ),
]);
- let config_map = ConfigMap {
+ let secret = Secret {
metadata: ObjectMeta {
- name: Some(ATT_POLICY_MAP.to_string()),
+ name: Some(TRUSTEE_AUTH_SECRET.to_string()),
owner_references: Some(vec![owner_reference]),
..Default::default()
},
data: Some(data),
..Default::default()
};
- create_or_info_if_exists!(client, ConfigMap, config_map);
+ create_or_info_if_exists!(client, Secret, secret);
Ok(())
}
@@ -395,6 +485,9 @@ fn generate_kbs_config(has_certificate: bool) -> Result {
let tls_cert = toml::Value::String(format!("{TLS_DIR}/tls.crt"));
http_server.insert("certificate".to_string(), tls_cert);
} else {
+ warn!(
+ "Trustee deployment has no TLS certificate, starting KBS with insecure HTTP (not recommended for production)"
+ );
http_server.insert("insecure_http".to_string(), toml::Value::Boolean(true));
}
@@ -408,13 +501,8 @@ pub async fn generate_trustee_data(
) -> Result<()> {
let has_certificate = read_certificate(client.clone(), secret).await?.is_some();
let kbs_config = generate_kbs_config(has_certificate)?;
- let policy_rego = include_str!("resource.rego");
- let data = BTreeMap::from([
- ("kbs-config.toml".to_string(), kbs_config),
- ("policy.rego".to_string(), policy_rego.to_string()),
- (REFERENCE_VALUES_FILE.to_string(), "[]".to_string()),
- ]);
+ let data = BTreeMap::from([("kbs-config.toml".to_string(), kbs_config)]);
let config_map = ConfigMap {
metadata: ObjectMeta {
@@ -429,6 +517,21 @@ pub async fn generate_trustee_data(
Ok(())
}
+pub async fn generate_rv_data(client: Client, owner_reference: OwnerReference) -> Result<()> {
+ let data = BTreeMap::from([(REFERENCE_VALUES_FILE.to_string(), "[]".to_string())]);
+ let config_map = ConfigMap {
+ metadata: ObjectMeta {
+ name: Some(TRUSTEE_RV_MAP.to_string()),
+ owner_references: Some(vec![owner_reference]),
+ ..Default::default()
+ },
+ data: Some(data),
+ ..Default::default()
+ };
+ create_or_info_if_exists!(client, ConfigMap, config_map);
+ Ok(())
+}
+
pub async fn generate_kbs_service(
client: Client,
owner_reference: OwnerReference,
@@ -459,19 +562,8 @@ pub async fn generate_kbs_service(
Ok(())
}
-fn generate_kbs_volume_templates() -> [(&'static str, &'static str, Volume); 3] {
+fn generate_kbs_volume_templates() -> [(&'static str, &'static str, Volume); 2] {
[
- (
- ATT_POLICY_MAP,
- "/opt/trustee/policies/opa",
- Volume {
- config_map: Some(ConfigMapVolumeSource {
- name: ATT_POLICY_MAP.to_string(),
- ..Default::default()
- }),
- ..Default::default()
- },
- ),
(
TRUSTEE_DATA_MAP,
TRUSTEE_DATA_DIR,
@@ -484,11 +576,16 @@ fn generate_kbs_volume_templates() -> [(&'static str, &'static str, Volume); 3]
},
),
(
- "resource-dir",
- TRUSTEE_SECRETS_PATH,
+ TRUSTEE_AUTH_SECRET,
+ TRUSTEE_AUTH_KEY_DIR,
Volume {
- empty_dir: Some(EmptyDirVolumeSource {
- medium: Some("Memory".to_string()),
+ secret: Some(SecretVolumeSource {
+ secret_name: Some(TRUSTEE_AUTH_SECRET.to_string()),
+ items: Some(vec![KeyToPath {
+ key: "public.pub".to_string(),
+ path: "public.pub".to_string(),
+ ..Default::default()
+ }]),
..Default::default()
}),
..Default::default()
@@ -553,7 +650,10 @@ pub async fn generate_kbs_deployment(
image: &str,
secret: &Option,
) -> Result<()> {
- let selector = Some(BTreeMap::from([("app".to_string(), "kbs".to_string())]));
+ let selector = Some(BTreeMap::from([(
+ "app".to_string(),
+ TRUSTEE_APP_LABEL.to_string(),
+ )]));
let tls_volumes = read_certificate(client.clone(), secret).await?;
let pod_spec = generate_kbs_pod_spec(image, tls_volumes);
@@ -561,6 +661,7 @@ pub async fn generate_kbs_deployment(
let deployment = Deployment {
metadata: ObjectMeta {
name: Some(TRUSTEE_DEPLOYMENT.to_string()),
+ labels: selector.clone(),
owner_references: Some(vec![owner_reference]),
..Default::default()
},
@@ -590,7 +691,6 @@ mod tests {
use super::*;
use crate::test_utils::*;
use http::{Method, Request, StatusCode};
- use kube::client::Body;
use trusted_cluster_operator_test_utils::mock_client::*;
use trusted_cluster_operator_test_utils::test_error_method;
@@ -641,22 +741,27 @@ mod tests {
#[tokio::test]
async fn test_update_rvs_success() {
+ let _ = jsonwebtoken_openssl::install_default();
let clos = async |req: Request<_>, ctr| match (ctr, req.method()) {
(0, &Method::GET) => {
assert!(req.uri().path().contains(PCR_CONFIG_MAP));
Ok(serde_json::to_string(&dummy_pcrs_map()).unwrap())
}
(1, &Method::GET) | (2, &Method::PUT) => {
- assert!(req.uri().path().contains(TRUSTEE_DATA_MAP));
+ assert!(req.uri().path().contains(TRUSTEE_RV_MAP));
Ok(serde_json::to_string(&dummy_trustee_map()).unwrap())
}
+ (3, &Method::GET) => {
+ assert!(req.uri().path().contains(TRUSTEE_AUTH_SECRET));
+ Ok(serde_json::to_string(&dummy_trustee_auth()).unwrap())
+ }
+ (4, &Method::GET) => Ok(serde_json::to_string(&dummy_cluster()).unwrap()),
_ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
};
- count_check!(3, clos, |client| {
+ count_check!(5, clos, |client| {
assert!(update_reference_values(client).await.is_ok());
});
}
-
#[tokio::test]
async fn test_update_rvs_no_pcr_map() {
let clos = async |req: Request<_>, _| match (req.uri().path(), req.method()) {
@@ -669,12 +774,12 @@ mod tests {
}
#[tokio::test]
- async fn test_update_rvs_no_trustee_map() {
+ async fn test_update_rvs_no_rvs_map() {
let clos = async |req: Request<_>, ctr| match (ctr, req.uri().path()) {
(0, p) if p.contains(PCR_CONFIG_MAP) => {
Ok(serde_json::to_string(&dummy_pcrs_map()).unwrap())
}
- (1, p) if p.contains(TRUSTEE_DATA_MAP) => Err(StatusCode::NOT_FOUND),
+ (1, p) if p.contains(TRUSTEE_RV_MAP) => Err(StatusCode::NOT_FOUND),
_ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
};
count_check!(2, clos, |client| {
@@ -683,12 +788,12 @@ mod tests {
}
#[tokio::test]
- async fn test_update_rvs_no_trustee_data() {
+ async fn test_update_rvs_no_rvs_data() {
let clos = async |req: Request<_>, ctr| match (ctr, req.uri().path()) {
(0, p) if p.contains(PCR_CONFIG_MAP) => {
Ok(serde_json::to_string(&dummy_pcrs_map()).unwrap())
}
- (1, p) if p.contains(TRUSTEE_DATA_MAP) => {
+ (1, p) if p.contains(TRUSTEE_RV_MAP) => {
Ok(serde_json::to_string(&ConfigMap::default()).unwrap())
}
_ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
@@ -705,133 +810,21 @@ mod tests {
assert_eq!(jwk.key.len(), 32);
}
- fn dummy_deployment() -> Deployment {
- Deployment {
- spec: Some(DeploymentSpec {
- replicas: Some(1),
- template: PodTemplateSpec {
- spec: Some(PodSpec {
- containers: vec![Container::default()],
- ..Default::default()
- }),
- ..Default::default()
- },
- ..Default::default()
- }),
- ..Default::default()
- }
- }
-
- #[tokio::test]
- async fn test_mount_secret_success() {
- let clos = async |req: Request<_>, ctr| match (ctr, req.method()) {
- (0, &Method::GET) | (1, &Method::PUT) => {
- Ok(serde_json::to_string(&dummy_deployment()).unwrap())
- }
- _ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
- };
- count_check!(2, clos, |client| {
- assert!(mount_secret(client, "id").await.is_ok());
- });
- }
-
- #[tokio::test]
- async fn test_mount_secret_no_depl() {
- let clos = async |_, _| Err(StatusCode::NOT_FOUND);
- count_check!(1, clos, |client| {
- assert!(mount_secret(client, "id").await.is_err());
- });
- }
-
- #[tokio::test]
- async fn test_mount_secret_no_spec() {
- let clos = async |_, _| {
- let mut depl = dummy_deployment();
- depl.spec = None;
- Ok(serde_json::to_string(&depl).unwrap())
- };
- count_check!(1, clos, |client| {
- let err = mount_secret(client, "id").await.err().unwrap();
- assert!(err.to_string().contains("but had no spec"));
- });
- }
-
- #[tokio::test]
- async fn test_mount_secret_no_pod_spec() {
- let clos = async |_, _| {
- let mut depl = dummy_deployment();
- let spec = depl.spec.as_mut().unwrap();
- spec.template.spec = None;
- Ok(serde_json::to_string(&depl).unwrap())
- };
- count_check!(1, clos, |client| {
- let err = mount_secret(client, "id").await.err().unwrap();
- assert!(err.to_string().contains("but had no pod spec"));
- });
- }
-
- #[tokio::test]
- async fn test_mount_secret_no_containers() {
- let clos = async |_, _| {
- let mut depl = dummy_deployment();
- let spec = depl.spec.as_mut().unwrap();
- let pod_spec = spec.template.spec.as_mut().unwrap();
- pod_spec.containers = vec![];
- Ok(serde_json::to_string(&depl).unwrap())
- };
- count_check!(1, clos, |client| {
- let err = mount_secret(client, "id").await.err().unwrap();
- assert!(err.to_string().contains("but had no containers"));
- });
- }
-
- #[tokio::test]
- async fn test_unmount_secret() {
- let clos = async |req: Request, ctr| match (ctr, req.method()) {
- (0, &Method::GET) => {
- let mut depl = dummy_deployment();
- let spec = depl.spec.as_mut().unwrap();
- let pod_spec = spec.template.spec.as_mut().unwrap();
- pod_spec.volumes = Some(vec![Volume {
- name: "id".to_string(),
- ..Default::default()
- }]);
- let container = pod_spec.containers.get_mut(0).unwrap();
- container.volume_mounts = Some(vec![VolumeMount {
- name: "id".to_string(),
- ..Default::default()
- }]);
- Ok(serde_json::to_string(&depl).unwrap())
- }
- (1, &Method::PUT) => {
- let bytes = req.into_body().collect_bytes().await.unwrap().to_vec();
- let body = String::from_utf8_lossy(&bytes);
- assert!(!body.contains("id"));
- Ok(serde_json::to_string(&dummy_deployment()).unwrap())
- }
- _ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
- };
- count_check!(2, clos, |client| {
- assert!(unmount_secret(client, "id").await.is_ok());
- });
- }
-
- #[tokio::test]
- async fn test_generate_att_policy_success() {
- let clos = |client| generate_attestation_policy(client, Default::default());
- test_create_success::<_, _, ConfigMap>(clos).await;
- }
-
- #[tokio::test]
- async fn test_generate_att_policy_already_exists() {
- let clos = |client| generate_attestation_policy(client, Default::default());
- test_create_already_exists(clos).await;
+ #[test]
+ fn test_generate_ed25519_key_pair() {
+ let pair = generate_ed25519_key_pair().unwrap();
+ let priv_pem = String::from_utf8(pair.private_key_pem).unwrap();
+ let pub_pem = String::from_utf8(pair.public_key_pem).unwrap();
+ assert!(priv_pem.starts_with("-----BEGIN PRIVATE KEY-----"));
+ assert!(pub_pem.starts_with("-----BEGIN PUBLIC KEY-----"));
}
- #[tokio::test]
- async fn test_generate_att_policy_error() {
- let clos = |client| generate_attestation_policy(client, Default::default());
- test_error_method!(clos, Method::POST);
+ #[test]
+ fn test_generate_ed25519_key_pair_unique() {
+ let pair1 = generate_ed25519_key_pair().unwrap();
+ let pair2 = generate_ed25519_key_pair().unwrap();
+ assert_ne!(pair1.private_key_pem, pair2.private_key_pem);
+ assert_ne!(pair1.public_key_pem, pair2.public_key_pem);
}
#[tokio::test]
@@ -893,4 +886,178 @@ mod tests {
let clos = |client| generate_kbs_deployment(client, Default::default(), "image", &None);
test_error_method!(clos, Method::POST);
}
+
+ #[tokio::test]
+ async fn test_generate_rv_data_success() {
+ let clos = |client| generate_rv_data(client, Default::default());
+ test_create_success::<_, _, ConfigMap>(clos).await;
+ }
+
+ #[tokio::test]
+ async fn test_generate_rv_data_already_exists() {
+ let clos = |client| generate_rv_data(client, Default::default());
+ test_create_already_exists(clos).await;
+ }
+
+ #[tokio::test]
+ async fn test_generate_rv_data_error() {
+ let clos = |client| generate_rv_data(client, Default::default());
+ test_error_method!(clos, Method::POST);
+ }
+
+ #[test]
+ fn test_recompute_reference_values_includes_svn() {
+ let result = recompute_reference_values(dummy_pcrs());
+ let svn = result.iter().find(|rv| rv.name == "tpm_svn").unwrap();
+ let vals = svn.value.as_array().unwrap();
+ assert_eq!(vals.len(), 1);
+ assert_eq!(vals[0].as_str().unwrap(), "1");
+ }
+
+ #[test]
+ fn test_recompute_reference_values_version() {
+ let result = recompute_reference_values(dummy_pcrs());
+ for rv in &result {
+ assert_eq!(rv.version, "0.1.0");
+ }
+ }
+
+ #[tokio::test]
+ async fn test_sync_all_machine_luks_key_empty() {
+ let clos = async |req: Request<_>, ctr| match (ctr, req.method()) {
+ (0, &Method::GET) => {
+ let list = kube::api::ObjectList {
+ items: Vec::::new(),
+ types: Default::default(),
+ metadata: Default::default(),
+ };
+ Ok(serde_json::to_string(&list).unwrap())
+ }
+ _ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
+ };
+ count_check!(1, clos, |client| {
+ assert!(sync_all_machine_luks_key(client).await.is_ok());
+ });
+ }
+
+ #[tokio::test]
+ async fn test_sync_all_machine_luks_key_send_success() {
+ let _ = jsonwebtoken_openssl::install_default();
+ let clos = async |req: Request<_>, ctr| match (ctr, req.method()) {
+ (0, &Method::GET) => {
+ let list = kube::api::ObjectList {
+ items: vec![dummy_machine("m1"), dummy_machine("m2")],
+ types: Default::default(),
+ metadata: Default::default(),
+ };
+ Ok(serde_json::to_string(&list).unwrap())
+ }
+ (1, &Method::GET) => {
+ assert!(req.uri().path().contains(TRUSTEE_AUTH_SECRET));
+ Ok(serde_json::to_string(&dummy_trustee_auth()).unwrap())
+ }
+ (2, &Method::GET) => Ok(serde_json::to_string(&dummy_cluster()).unwrap()),
+ (3, &Method::GET) => Ok(serde_json::to_string(&dummy_secret()).unwrap()),
+ (4, &Method::POST) => Ok(serde_json::to_string(&()).unwrap()),
+ _ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
+ };
+ count_check!(4, clos, |client| {
+ assert!(sync_all_machine_luks_key(client).await.is_ok());
+ });
+ }
+
+ #[tokio::test]
+ async fn test_sync_all_machine_luks_key_send_fails_gracefully() {
+ let clos = async |req: Request<_>, ctr| match (ctr, req.method()) {
+ (0, &Method::GET) => {
+ let list = kube::api::ObjectList {
+ items: vec![dummy_machine("m1"), dummy_machine("m2")],
+ types: Default::default(),
+ metadata: Default::default(),
+ };
+ Ok(serde_json::to_string(&list).unwrap())
+ }
+ (_, &Method::GET) => Err(StatusCode::NOT_FOUND),
+ _ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
+ };
+ count_check!(3, clos, |client| {
+ assert!(sync_all_machine_luks_key(client).await.is_ok());
+ });
+ }
+
+ #[tokio::test]
+ async fn test_update_attestation_keys_empty() {
+ let clos = async |_, _| {
+ let list = kube::api::ObjectList {
+ items: Vec::::new(),
+ types: Default::default(),
+ metadata: Default::default(),
+ };
+ Ok(serde_json::to_string(&list).unwrap())
+ };
+ count_check!(1, clos, |client| {
+ assert!(update_attestation_keys(client).await.is_ok());
+ });
+ }
+
+ #[tokio::test]
+ async fn test_update_attestation_keys_register_fails_gracefully() {
+ let clos = async |req: Request<_>, ctr| match (ctr, req.method()) {
+ (0, &Method::GET) => {
+ let list = kube::api::ObjectList {
+ items: vec![dummy_ak_secret("ak1"), dummy_ak_secret("ak2")],
+ types: Default::default(),
+ metadata: Default::default(),
+ };
+ Ok(serde_json::to_string(&list).unwrap())
+ }
+ _ => Err(StatusCode::NOT_FOUND),
+ };
+ count_check!(3, clos, |client| {
+ assert!(update_attestation_keys(client).await.is_ok());
+ });
+ }
+
+ #[tokio::test]
+ async fn test_update_attestation_keys_register_success() {
+ let clos = async |req: Request<_>, ctr| match (ctr, req.method()) {
+ (0, &Method::GET) => {
+ let list = kube::api::ObjectList {
+ items: vec![dummy_ak_secret("ak1")],
+ types: Default::default(),
+ metadata: Default::default(),
+ };
+ Ok(serde_json::to_string(&list).unwrap())
+ }
+ (1, &Method::GET) => {
+ assert!(req.uri().path().contains(TRUSTEE_AUTH_SECRET));
+ Ok(serde_json::to_string(&dummy_trustee_auth()).unwrap())
+ }
+ (2, &Method::GET) => Ok(serde_json::to_string(&dummy_cluster()).unwrap()),
+ (3, &Method::POST) => Ok(serde_json::to_string(&()).unwrap()),
+ _ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
+ };
+ count_check!(3, clos, |client| {
+ assert!(update_attestation_keys(client).await.is_ok());
+ });
+ }
+
+ #[tokio::test]
+ async fn test_update_attestation_keys_filters_deleting() {
+ use k8s_openapi::apimachinery::pkg::apis::meta::v1::Time;
+ let clos = async |_, _| {
+ let mut deleting = dummy_ak_secret("ak-deleting");
+ deleting.metadata.deletion_timestamp = Some(Time(jiff::Timestamp::now()));
+ let no_owner = Secret::default();
+ let list = kube::api::ObjectList {
+ items: vec![deleting, no_owner],
+ types: Default::default(),
+ metadata: Default::default(),
+ };
+ Ok(serde_json::to_string(&list).unwrap())
+ };
+ count_check!(1, clos, |client| {
+ assert!(update_attestation_keys(client).await.is_ok());
+ });
+ }
}
diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs
index 53d75d37..e428b520 100644
--- a/test_utils/src/lib.rs
+++ b/test_utils/src/lib.rs
@@ -592,6 +592,49 @@ impl TestContext {
Ok(())
}
+ pub async fn wait_for_deployment_ready(
+ &self,
+ deployments_api: &Api,
+ deployment_name: &str,
+ timeout_secs: u64,
+ ) -> Result<()> {
+ test_info!(
+ &self.test_name,
+ "Waiting for deployment {} to be ready",
+ deployment_name
+ );
+ let poller = Poller::new()
+ .with_timeout(Duration::from_secs(timeout_secs))
+ .with_interval(Duration::from_secs(5))
+ .with_error_message(format!(
+ "{deployment_name} deployment does not have 1 available replica after {timeout_secs} seconds"
+ ));
+
+ let test_name_owned = self.test_name.clone();
+ poller
+ .poll_async(move || {
+ let api = deployments_api.clone();
+ let name = deployment_name.to_string();
+ let tn = test_name_owned.clone();
+ async move {
+ let deployment = api.get(&name).await?;
+
+ if let Some(status) = &deployment.status
+ && let Some(available_replicas) = status.available_replicas
+ && available_replicas == 1
+ {
+ test_info!(&tn, "{} deployment has 1 available replica", name);
+ return Ok(());
+ }
+
+ Err(anyhow!(
+ "{name} deployment does not have 1 available replica yet"
+ ))
+ }
+ })
+ .await
+ }
+
async fn create_certificate(
&self,
service_name: &str,
diff --git a/tests/Cargo.toml b/tests/Cargo.toml
index c0de449a..1d1946fa 100644
--- a/tests/Cargo.toml
+++ b/tests/Cargo.toml
@@ -15,6 +15,7 @@ virtualization = []
[dependencies]
anyhow.workspace = true
cfg-if = "1.0.4"
+chrono.workspace = true
compute-pcrs-lib.workspace = true
k8s-openapi.workspace = true
kube = { workspace = true }
diff --git a/tests/trusted_execution_cluster.rs b/tests/trusted_execution_cluster.rs
index f57c6d73..e09173ec 100644
--- a/tests/trusted_execution_cluster.rs
+++ b/tests/trusted_execution_cluster.rs
@@ -4,27 +4,30 @@
// SPDX-License-Identifier: MIT
use anyhow::Context;
+use chrono::Utc;
use compute_pcrs_lib::{Part, Pcr};
use k8s_openapi::api::apps::v1::Deployment;
-use k8s_openapi::api::core::v1::{ConfigMap, Secret};
+use k8s_openapi::api::core::v1::{ConfigMap, Pod, Secret};
+use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta;
use k8s_openapi::apimachinery::pkg::apis::meta::v1::{Condition, OwnerReference};
-use kube::api::ObjectMeta;
+use kube::api::{ListParams, LogParams, Patch, PatchParams};
use kube::runtime::wait::await_condition;
use kube::{Api, api::DeleteParams};
+use serde_json::json;
use std::time::Duration;
use tokio::time::timeout;
use trusted_cluster_operator_lib::conditions::NOT_COMMITTED_REASON_PENDING;
-use trusted_cluster_operator_lib::endpoints::{REGISTER_SERVER_DEPLOYMENT, TRUSTEE_DEPLOYMENT};
+use trusted_cluster_operator_lib::endpoints::REGISTER_SERVER_DEPLOYMENT;
+use trusted_cluster_operator_lib::endpoints::TRUSTEE_DEPLOYMENT;
use trusted_cluster_operator_lib::reference_values::ImagePcrs;
use trusted_cluster_operator_lib::{
ApprovedImage, AttestationKey, Machine, TrustedExecutionCluster, generate_owner_reference,
};
use trusted_cluster_operator_test_utils::*;
-
const EXPECTED_PCR4: &str = "ff2b357be4a4bc66be796d4e7b2f1f27077dc89b96220aae60b443bcf4672525";
const TEC_NAME: &str = "trusted-execution-cluster";
const APPROVED_IMAGE_NAME: &str = "coreos";
-const TRUSTEE_CONFIG_MAP: &str = "trustee-data";
+const TRUSTEE_RV_MAP: &str = "trustee-rv-data";
const RV_JSON_KEY: &str = "reference-values.json";
fn ak_approved(ak: Option<&AttestationKey>) -> bool {
@@ -231,8 +234,8 @@ async fn test_image_disallow() -> anyhow::Result<()> {
let json = data.and_then(|data| data.get(RV_JSON_KEY));
json.map(|json| !json.contains(EXPECTED_PCR4)).unwrap_or(false)
};
- let rv_removed = await_condition(configmap_api, TRUSTEE_CONFIG_MAP, chk_removed);
- let ctx = format!("waiting for ConfigMap {TRUSTEE_CONFIG_MAP} to not contain PCR value");
+ let rv_removed = await_condition(configmap_api, TRUSTEE_RV_MAP, chk_removed);
+ let ctx = format!("waiting for ConfigMap {TRUSTEE_RV_MAP} to not contain PCR value");
timeout(scaled_duration(180), rv_removed).await.context(ctx)??;
test_ctx.cleanup().await?;
@@ -369,6 +372,437 @@ async fn test_nonexistent_approved_image() -> anyhow::Result<()> {
let ctx = "waiting for ApprovedImage coreos1 to be PodPending";
timeout(scaled_duration(30), done).await.context(ctx)??;
+ test_ctx.cleanup().await?;
+ Ok(())
+}
+}
+
+named_test! {
+async fn test_luks_key_sync() -> anyhow::Result<()> {
+ let test_ctx = setup!().await?;
+ let client = test_ctx.client();
+ let namespace = test_ctx.namespace();
+ let tec_name = "trusted-execution-cluster";
+
+ let tec_api: Api = Api::namespaced(client.clone(), namespace);
+ let tec = tec_api.get(tec_name).await?;
+ let owner_reference = generate_owner_reference(&tec)?;
+
+ // Create two machines
+ let machine1_uuid = uuid::Uuid::new_v4().to_string();
+ let machine1_name = format!("test-machine-{}", &machine1_uuid[..8]);
+ let machine2_uuid = uuid::Uuid::new_v4().to_string();
+ let machine2_name = format!("test-machine-{}", &machine2_uuid[..8]);
+
+ let machines: Api = Api::namespaced(client.clone(), namespace);
+
+ let machine1 = Machine {
+ metadata: k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
+ name: Some(machine1_name.clone()),
+ namespace: Some(namespace.to_string()),
+ owner_references: Some(vec![owner_reference.clone()]),
+ ..Default::default()
+ },
+ spec: trusted_cluster_operator_lib::MachineSpec {
+ id: machine1_uuid.clone(),
+ },
+ status: None,
+ };
+ machines.create(&Default::default(), &machine1).await?;
+ test_ctx.info(format!("Created Machine 1: {machine1_name}"));
+
+ let machine2 = Machine {
+ metadata: k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
+ name: Some(machine2_name.clone()),
+ namespace: Some(namespace.to_string()),
+ owner_references: Some(vec![owner_reference.clone()]),
+ ..Default::default()
+ },
+ spec: trusted_cluster_operator_lib::MachineSpec {
+ id: machine2_uuid.clone(),
+ },
+ status: None,
+ };
+ machines.create(&Default::default(), &machine2).await?;
+ test_ctx.info(format!("Created Machine 2: {machine2_name}"));
+
+ // Wait for both K8s secrets to be created by the keygen controller
+ let secrets_api: Api = Api::namespaced(client.clone(), namespace);
+ let poller = Poller::new()
+ .with_timeout(Duration::from_secs(60))
+ .with_interval(Duration::from_millis(500))
+ .with_error_message("Machine secrets not created".to_string());
+
+ poller
+ .poll_async(|| {
+ let api = secrets_api.clone();
+ let id1 = machine1_uuid.clone();
+ let id2 = machine2_uuid.clone();
+ async move {
+ api.get(&id1).await?;
+ api.get(&id2).await?;
+ anyhow::Ok(())
+ }
+ })
+ .await?;
+ test_ctx.info("Both machine secrets created");
+
+ // Wait for the operator to send both secrets to the KBS
+ let pods_api: Api = Api::namespaced(client.clone(), namespace);
+ let poller = Poller::new()
+ .with_timeout(Duration::from_secs(60))
+ .with_interval(Duration::from_secs(2))
+ .with_error_message("Secrets not sent to KBS".to_string());
+
+ poller
+ .poll_async(|| {
+ let api = pods_api.clone();
+ let id1 = machine1_uuid.clone();
+ let id2 = machine2_uuid.clone();
+ async move {
+ let lp = ListParams::default().labels("app=trusted-cluster-operator");
+ let operator_pods = api.list(&lp).await?;
+ let pod_name = operator_pods
+ .items
+ .first()
+ .and_then(|p| p.metadata.name.as_ref())
+ .ok_or_else(|| anyhow::anyhow!("Operator pod not found"))?
+ .clone();
+ let logs = api.logs(&pod_name, &LogParams::default()).await?;
+ if logs.contains(&format!("{id1} sent successfully"))
+ && logs.contains(&format!("{id2} sent successfully"))
+ {
+ return Ok(());
+ }
+ Err(anyhow::anyhow!("Not all secrets sent to KBS yet"))
+ }
+ })
+ .await?;
+ test_ctx.info("Both secrets sent to KBS");
+
+
+ let now = Utc::now().to_rfc3339();
+ let patch = json!({
+ "spec": {
+ "template": {
+ "metadata": {
+ "annotations": {
+ "kubectl.kubernetes.io/restartedAt": now
+ }
+ }
+ }
+ }
+ });
+
+ test_ctx.info(format!("Triggering rollout restart for deployment: {TRUSTEE_DEPLOYMENT}"));
+ let deployments: Api = Api::namespaced(client.clone(), namespace);
+ // Apply the patch
+ deployments
+ .patch(
+ TRUSTEE_DEPLOYMENT,
+ &PatchParams::default(),
+ &Patch::Strategic(patch),
+ )
+ .await?;
+
+ test_ctx.wait_for_deployment_ready(&deployments, TRUSTEE_DEPLOYMENT, 120).await?;
+
+ // Wait for the new pod to be ready
+ test_ctx.info("Trustee deployment is ready after restart");
+
+ // Verify both secrets are re-synced to KBS after the trustee restart
+ let poller = Poller::new()
+ .with_timeout(Duration::from_secs(60))
+ .with_interval(Duration::from_secs(2))
+ .with_error_message("Secrets not re-synced to KBS after restart".to_string());
+
+ poller
+ .poll_async(|| {
+ let api = pods_api.clone();
+ async move {
+ let lp = ListParams::default().labels("app=trusted-cluster-operator");
+ let operator_pods = api.list(&lp).await?;
+ let pod_name = operator_pods
+ .items
+ .first()
+ .and_then(|p| p.metadata.name.as_ref())
+ .ok_or_else(|| anyhow::anyhow!("Operator pod not found"))?
+ .clone();
+ let logs = api.logs(&pod_name, &LogParams::default()).await?;
+ if logs.contains("Syncing 2 machine luks key to KBS") {
+ return Ok(());
+ }
+ Err(anyhow::anyhow!("Secrets not yet re-synced to KBS after restart."))
+ }
+ })
+ .await?;
+ test_ctx.info("Both secrets re-synced to KBS after trustee restart");
+
+ // Delete machine1 and verify its secret is removed from both K8s and KBS
+ machines
+ .delete(&machine1_name, &Default::default())
+ .await?;
+ test_ctx.info(format!("Deleted Machine 1: {machine1_name}"));
+
+ let poller = Poller::new()
+ .with_timeout(Duration::from_secs(60))
+ .with_interval(Duration::from_secs(2))
+ .with_error_message("Machine1 secret not deleted from KBS".to_string());
+
+ poller
+ .poll_async(|| {
+ let api = pods_api.clone();
+ let id1 = machine1_uuid.clone();
+ async move {
+ let lp = ListParams::default().labels("app=trusted-cluster-operator");
+ let operator_pods = api.list(&lp).await?;
+ let pod_name = operator_pods
+ .items
+ .first()
+ .and_then(|p| p.metadata.name.as_ref())
+ .ok_or_else(|| anyhow::anyhow!("Operator pod not found"))?
+ .clone();
+ let logs = api.logs(&pod_name, &LogParams::default()).await?;
+ if logs.contains(&format!("Secret {id1} deleted successfully")) {
+ return Ok(());
+ }
+ Err(anyhow::anyhow!("Machine1 secret not yet deleted from KBS"))
+ }
+ })
+ .await?;
+ poller
+ .poll_async(|| {
+ let secrets_api = secrets_api.clone();
+ let id1 = machine1_uuid.clone();
+ async move {
+ match secrets_api.get(&id1).await {
+ Ok(_) => Err(anyhow::anyhow!("Machine1 secret still exists")),
+ Err(kube::Error::Api(ae)) if ae.code == 404 => Ok(()),
+ Err(e) => Err(e.into()),
+ }
+ }
+ })
+ .await?;
+ test_ctx.info("Machine1 secret deleted from KBS");
+
+ // Verify the K8s Secret for machine1 is also deleted
+ wait_for_resource_deleted(&secrets_api, &machine1_uuid, 60).await?;
+ test_ctx.info("Machine1 K8s secret deleted");
+
+ test_ctx.cleanup().await?;
+ Ok(())
+}
+}
+
+named_test! {
+async fn test_attestation_key_sync() -> anyhow::Result<()> {
+ let test_ctx = setup!().await?;
+ let client = test_ctx.client();
+ let namespace = test_ctx.namespace();
+ let tec_name = "trusted-execution-cluster";
+
+ let tec_api: Api = Api::namespaced(client.clone(), namespace);
+ let tec = tec_api.get(tec_name).await?;
+ let owner_reference = generate_owner_reference(&tec)?;
+
+ // Create two machines
+ let machine1_uuid = uuid::Uuid::new_v4().to_string();
+ let machine1_name = format!("test-machine-{}", &machine1_uuid[..8]);
+ let machine2_uuid = uuid::Uuid::new_v4().to_string();
+ let machine2_name = format!("test-machine-{}", &machine2_uuid[..8]);
+
+ let machines: Api = Api::namespaced(client.clone(), namespace);
+ let machine1 = Machine {
+ metadata: k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
+ name: Some(machine1_name.clone()),
+ namespace: Some(namespace.to_string()),
+ owner_references: Some(vec![owner_reference.clone()]),
+ ..Default::default()
+ },
+ spec: trusted_cluster_operator_lib::MachineSpec {
+ id: machine1_uuid.clone(),
+ },
+ status: None,
+ };
+ machines.create(&Default::default(), &machine1).await?;
+ test_ctx.info(format!("Created Machine 1: {machine1_name}"));
+
+ let machine2 = Machine {
+ metadata: k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
+ name: Some(machine2_name.clone()),
+ namespace: Some(namespace.to_string()),
+ owner_references: Some(vec![owner_reference.clone()]),
+ ..Default::default()
+ },
+ spec: trusted_cluster_operator_lib::MachineSpec {
+ id: machine2_uuid.clone(),
+ },
+ status: None,
+ };
+ machines.create(&Default::default(), &machine2).await?;
+ test_ctx.info(format!("Created Machine 2: {machine2_name}"));
+
+ // Create two AttestationKeys with matching UUIDs
+ let ak1_name = format!("test-ak-{}", &machine1_uuid[..8]);
+ let ak1_public_key = uuid::Uuid::new_v4().to_string();
+ let ak2_name = format!("test-ak-{}", &machine2_uuid[..8]);
+ let ak2_public_key = uuid::Uuid::new_v4().to_string();
+
+ let attestation_keys: Api = Api::namespaced(client.clone(), namespace);
+
+ let ak1 = AttestationKey {
+ metadata: k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
+ name: Some(ak1_name.clone()),
+ namespace: Some(namespace.to_string()),
+ owner_references: Some(vec![owner_reference.clone()]),
+ ..Default::default()
+ },
+ spec: trusted_cluster_operator_lib::AttestationKeySpec {
+ public_key: ak1_public_key,
+ uuid: Some(machine1_uuid.clone()),
+ },
+ status: None,
+ };
+ attestation_keys.create(&Default::default(), &ak1).await?;
+ test_ctx.info(format!("Created AttestationKey 1: {ak1_name}"));
+
+ let ak2 = AttestationKey {
+ metadata: k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
+ name: Some(ak2_name.clone()),
+ namespace: Some(namespace.to_string()),
+ owner_references: Some(vec![owner_reference.clone()]),
+ ..Default::default()
+ },
+ spec: trusted_cluster_operator_lib::AttestationKeySpec {
+ public_key: ak2_public_key,
+ uuid: Some(machine2_uuid.clone()),
+ },
+ status: None,
+ };
+ attestation_keys.create(&Default::default(), &ak2).await?;
+ test_ctx.info(format!("Created AttestationKey 2: {ak2_name}"));
+
+ // Wait for both AKs to be approved and have secrets created
+ let secrets_api: Api = Api::namespaced(client.clone(), namespace);
+ let poller = Poller::new()
+ .with_timeout(Duration::from_secs(60))
+ .with_interval(Duration::from_millis(500))
+ .with_error_message("AttestationKeys not approved with secrets".to_string());
+
+ poller
+ .poll_async(|| {
+ let ak_api = attestation_keys.clone();
+ let secrets = secrets_api.clone();
+ let ak1_clone = ak1_name.clone();
+ let ak2_clone = ak2_name.clone();
+ async move {
+ for ak_name in [&ak1_clone, &ak2_clone] {
+ let ak = ak_api.get(ak_name).await?;
+ let is_approved = ak
+ .status
+ .as_ref()
+ .and_then(|s| s.conditions.as_ref())
+ .map(|conditions| {
+ conditions.iter().any(|c| c.type_ == "Approved" && c.status == "True")
+ })
+ .unwrap_or(false);
+ if !is_approved {
+ return Err(anyhow::anyhow!("AttestationKey {ak_name} not approved yet"));
+ }
+ secrets.get(ak_name).await?;
+ }
+ Ok(())
+ }
+ })
+ .await?;
+ test_ctx.info("Both AttestationKeys approved and secrets created");
+
+ // Wait for both AKs to be registered with KBS
+ let pods_api: Api = Api::namespaced(client.clone(), namespace);
+ let poller = Poller::new()
+ .with_timeout(Duration::from_secs(60))
+ .with_interval(Duration::from_secs(2))
+ .with_error_message("AKs not registered with KBS".to_string());
+
+ poller
+ .poll_async(|| {
+ let api = pods_api.clone();
+ async move {
+ let lp = ListParams::default().labels("app=trusted-cluster-operator");
+ let operator_pods = api.list(&lp).await?;
+ let pod_name = operator_pods
+ .items
+ .first()
+ .and_then(|p| p.metadata.name.as_ref())
+ .ok_or_else(|| anyhow::anyhow!("Operator pod not found"))?
+ .clone();
+ let logs = api.logs(&pod_name, &LogParams::default()).await?;
+ let count = logs.matches("AK registered successfully").count();
+ if count >= 3 {
+ return Ok(());
+ }
+ Err(anyhow::anyhow!("Only {count} AK registrations found, need at least 3"))
+ }
+ })
+ .await?;
+ test_ctx.info("Both AKs registered with KBS");
+
+ // Restart the trustee deployment
+ let now = Utc::now().to_rfc3339();
+ let patch = json!({
+ "spec": {
+ "template": {
+ "metadata": {
+ "annotations": {
+ "kubectl.kubernetes.io/restartedAt": now
+ }
+ }
+ }
+ }
+ });
+
+ test_ctx.info(format!("Triggering rollout restart for deployment: {TRUSTEE_DEPLOYMENT}"));
+ let deployments: Api = Api::namespaced(client.clone(), namespace);
+ deployments
+ .patch(
+ TRUSTEE_DEPLOYMENT,
+ &PatchParams::default(),
+ &Patch::Strategic(patch),
+ )
+ .await?;
+
+ test_ctx.wait_for_deployment_ready(&deployments, TRUSTEE_DEPLOYMENT, 120).await?;
+ test_ctx.info("Trustee deployment is ready after restart");
+
+ // Verify both AKs are re-registered to KBS after the trustee restart
+ let poller = Poller::new()
+ .with_timeout(Duration::from_secs(60))
+ .with_interval(Duration::from_secs(2))
+ .with_error_message("AKs not re-registered with KBS after restart".to_string());
+
+ poller
+ .poll_async(|| {
+ let api = pods_api.clone();
+ async move {
+ let lp = ListParams::default().labels("app=trusted-cluster-operator");
+ let operator_pods = api.list(&lp).await?;
+ let pod_name = operator_pods
+ .items
+ .first()
+ .and_then(|p| p.metadata.name.as_ref())
+ .ok_or_else(|| anyhow::anyhow!("Operator pod not found"))?
+ .clone();
+ let logs = api.logs(&pod_name, &LogParams::default()).await?;
+ let count = logs.matches("AK registered successfully").count();
+ if count >= 5 {
+ return Ok(());
+ }
+ Err(anyhow::anyhow!("Only {count} AK registrations after restart, need at least 5"))
+ }
+ })
+ .await?;
+ test_ctx.info("Both AKs re-registered with KBS after trustee restart");
+
test_ctx.cleanup().await?;
Ok(())
}
@@ -397,9 +831,9 @@ async fn test_approved_image_readoption() -> anyhow::Result<()> {
test_ctx.info(format!("Deleting TrustedExecutionCluster {TEC_NAME}"));
clusters.delete(TEC_NAME, &Default::default()).await?;
- wait_for_resource_deleted(&configmaps, TRUSTEE_CONFIG_MAP, scaled_timeout(60)).await?;
+ wait_for_resource_deleted(&configmaps, TRUSTEE_RV_MAP, scaled_timeout(60)).await?;
wait_for_resource_deleted(&images, APPROVED_IMAGE_NAME, scaled_timeout(60)).await?;
- test_ctx.info(format!("Configmap {TRUSTEE_CONFIG_MAP} was removed"));
+ test_ctx.info(format!("Configmap {TRUSTEE_RV_MAP} was removed"));
let image = ApprovedImage {
spec: image_spec,
@@ -428,8 +862,8 @@ async fn test_approved_image_readoption() -> anyhow::Result<()> {
let json = data.and_then(|data| data.get(RV_JSON_KEY));
json.map(|json| json.contains(EXPECTED_PCR4)).unwrap_or(false)
};
- let rv_added = await_condition(configmaps, TRUSTEE_CONFIG_MAP, chk_added);
- let ctx = format!("waiting for ConfigMap {TRUSTEE_CONFIG_MAP} to contain PCR value");
+ let rv_added = await_condition(configmaps, TRUSTEE_RV_MAP, chk_added);
+ let ctx = format!("waiting for ConfigMap {TRUSTEE_RV_MAP} to contain PCR value");
timeout(scaled_duration(180), rv_added).await.context(ctx)??;
test_ctx.info("Reference values regenerated");