diff --git a/.cargo/xtask.toml b/.cargo/xtask.toml index 76d7b342d94..56038b99047 100644 --- a/.cargo/xtask.toml +++ b/.cargo/xtask.toml @@ -28,6 +28,8 @@ [libraries."libxmlsec1.so.1"] # libipcc should only be depended on by binaries that communicate with ipcc. +# sush-proxy does not, but sprockets-tls links libipcc unconditionally. +# TODO: feature-gate ipcc in sprockets, then drop sush-proxy here. [libraries."libipcc.so.1"] binary_allow_list = [ "installinator", @@ -36,6 +38,7 @@ binary_allow_list = [ "omicron-dev", "sled-agent", "sled-agent-sim", + "sush-proxy", ] # libnvme is a global zone only library and therefore we must be sure that only diff --git a/Cargo.lock b/Cargo.lock index 616ced6f1d4..57d2dbcce7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,7 +74,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "api_identity" @@ -476,11 +476,11 @@ dependencies = [ [[package]] name = "attest-data" version = "0.5.0" -source = "git+https://github.com/oxidecomputer/dice-util?rev=6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd#6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd" +source = "git+https://github.com/oxidecomputer/dice-util?rev=4a39ef08d81e5177edee0bddb1146032aa21074d#4a39ef08d81e5177edee0bddb1146032aa21074d" dependencies = [ "const-oid 0.9.6", "der 0.7.10", - "getrandom 0.3.4", + "getrandom 0.4.3", "hex", "hubpack", "rats-corim 0.1.0 (git+https://github.com/oxidecomputer/rats-corim)", @@ -503,7 +503,7 @@ dependencies = [ "hex", "hubpack", "knuffel", - "miette", + "miette 5.10.0", "rats-corim 0.1.0 (git+https://github.com/oxidecomputer/rats-corim)", "serde_json", ] @@ -674,6 +674,21 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "before" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/rumors?rev=b2675dbef99b784efb817db01920ee59dac708c3#b2675dbef99b784efb817db01920ee59dac708c3" +dependencies = [ + "bitvec", + "borsh", + "bytes", + "dashu-int", + "dsi-bitstream", + "static_assertions", + "suanpan", + "thiserror 2.0.18", +] + [[package]] name = "bhyve_api" version = "0.0.0" @@ -968,6 +983,30 @@ dependencies = [ "wicketd-commission-types", ] +[[package]] +name = "borsh" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" +dependencies = [ + "borsh-derive", + "bytes", + "cfg_aliases 0.2.1", +] + +[[package]] +name = "borsh-derive" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f347189c62a579b8cd5f80714efa178f52e461dc2e6d701d264f5ff22e566c" +dependencies = [ + "once_cell", + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "bs58" version = "0.5.1" @@ -1038,6 +1077,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bytesize" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7354288c522e7e980fafd2075d63d1285794c3a6a16cdd492f189ea406e5f18b" + [[package]] name = "bzip2" version = "0.4.4" @@ -1069,9 +1114,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.2" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" dependencies = [ "serde_core", ] @@ -2172,7 +2217,7 @@ dependencies = [ "futures-core", "mio", "parking_lot 0.12.5", - "rustix 1.1.3", + "rustix 1.1.4", "signal-hook", "signal-hook-mio", "winapi", @@ -2307,7 +2352,7 @@ checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271" dependencies = [ "cpubits", "ctutils", - "getrandom 0.4.1", + "getrandom 0.4.3", "hybrid-array", "num-traits", "rand_core 0.10.0", @@ -2333,7 +2378,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "getrandom 0.4.1", + "getrandom 0.4.3", "hybrid-array", "rand_core 0.10.0", ] @@ -2606,6 +2651,25 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dashu-base" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64b04cdfc4c8533100fe00304eb9687173bda47b1f1dac8af12ba13712ed49d" + +[[package]] +name = "dashu-int" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6ee98721d5d223e5b64b642dd9588b79d9ef415554b13720308b77d628c3be6" +dependencies = [ + "cfg-if", + "dashu-base", + "num-modular", + "rustversion", + "static_assertions", +] + [[package]] name = "data-encoding" version = "2.10.0" @@ -2944,7 +3008,7 @@ dependencies = [ [[package]] name = "dice-mfg-msgs" version = "0.3.0" -source = "git+https://github.com/oxidecomputer/dice-util?rev=6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd#6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd" +source = "git+https://github.com/oxidecomputer/dice-util?rev=4a39ef08d81e5177edee0bddb1146032aa21074d#4a39ef08d81e5177edee0bddb1146032aa21074d" dependencies = [ "const-oid 0.9.6", "corncobs", @@ -2954,13 +3018,13 @@ dependencies = [ "serde-big-array", "thiserror 2.0.18", "x509-cert", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] name = "dice-util-barcode" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/dice-util?rev=6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd#6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd" +source = "git+https://github.com/oxidecomputer/dice-util?rev=4a39ef08d81e5177edee0bddb1146032aa21074d#4a39ef08d81e5177edee0bddb1146032aa21074d" dependencies = [ "thiserror 2.0.18", ] @@ -2990,21 +3054,27 @@ dependencies = [ [[package]] name = "dice-verifier" version = "0.3.0-pre0" -source = "git+https://github.com/oxidecomputer/dice-util?rev=6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd#6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd" +source = "git+https://github.com/oxidecomputer/dice-util?rev=4a39ef08d81e5177edee0bddb1146032aa21074d#4a39ef08d81e5177edee0bddb1146032aa21074d" dependencies = [ - "attest-data 0.5.0 (git+https://github.com/oxidecomputer/dice-util?rev=6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd)", + "anyhow", + "async-trait", + "attest-data 0.5.0 (git+https://github.com/oxidecomputer/dice-util?rev=4a39ef08d81e5177edee0bddb1146032aa21074d)", + "camino", "const-oid 0.9.6", "ed25519-dalek 2.2.0", "env_logger", "hex", "hubpack", - "libipcc 0.1.0 (git+https://github.com/oxidecomputer/ipcc-rs?rev=dbaad520e1f5ae32c10db16ce176f9c24de95652)", + "libipcc 0.1.0 (git+https://github.com/oxidecomputer/ipcc-rs?rev=7cdf2ab9c8d9e9267a8b366aa780c6c26f9a5ecf)", "log", "p384 0.13.1", + "pki-playground 0.2.0 (git+https://github.com/oxidecomputer/pki-playground?rev=bcd3bf2dfe36468c494eac17463a4e10be366b35)", "rats-corim 0.1.0 (git+https://github.com/oxidecomputer/rats-corim)", "sha3 0.10.8", + "slog", "tempfile", "thiserror 2.0.18", + "tokio", "x509-cert", ] @@ -3283,7 +3353,7 @@ dependencies = [ "serde", "serde_json", "thiserror 2.0.18", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -3492,6 +3562,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dsi-bitstream" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f3e8a7ab0f1148cf7a792807d330f1e417dda3f4f122526e609a83925b4823" +dependencies = [ + "num-primitive", +] + [[package]] name = "dsl_auto_type" version = "0.2.0" @@ -3785,20 +3864,21 @@ dependencies = [ [[package]] name = "env_filter" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" dependencies = [ "log", + "regex", ] [[package]] name = "env_logger" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" dependencies = [ - "anstream 0.6.21", + "anstream 1.0.0", "anstyle", "env_filter", "log", @@ -3938,7 +4018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 1.1.3", + "rustix 1.1.4", "windows-sys 0.59.0", ] @@ -4352,7 +4432,7 @@ version = "0.1.0" source = "git+https://github.com/oxidecomputer/management-gateway-service?rev=6c0aca2545a73fd75536e149d29faa7108be5862#6c0aca2545a73fd75536e149d29faa7108be5862" dependencies = [ "serde", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -4370,7 +4450,7 @@ dependencies = [ "strum 0.27.2", "strum_macros 0.27.2", "uuid", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -4408,7 +4488,7 @@ dependencies = [ "usdt 0.5.0", "uuid", "version_check", - "zerocopy 0.8.40", + "zerocopy 0.8.56", "zip 0.6.6", ] @@ -4512,23 +4592,21 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 6.0.0", "rand_core 0.10.0", - "wasip2", - "wasip3", "wasm-bindgen", ] @@ -4741,7 +4819,7 @@ checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -4814,6 +4892,11 @@ name = "hashbrown" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "headers" @@ -5159,6 +5242,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.10.1" @@ -5516,12 +5605,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "iddqd" version = "0.4.2" @@ -5649,7 +5732,7 @@ dependencies = [ "oxlog", "oxnet", "regress 0.10.5", - "rustix 1.1.3", + "rustix 1.1.4", "schemars 0.8.22", "serde", "serde_json", @@ -5749,7 +5832,7 @@ dependencies = [ "ingot-types", "macaddr", "serde", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -5774,7 +5857,7 @@ checksum = "0c38331af4cff022b2ea46b220467d5962973853a47bd6851c54c111b71baa42" dependencies = [ "ingot-macros", "macaddr", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -6071,9 +6154,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" dependencies = [ "serde", ] @@ -6338,7 +6421,7 @@ dependencies = [ "base64 0.21.7", "chumsky", "knuffel-derive", - "miette", + "miette 5.10.0", "thiserror 1.0.69", "unicode-width 0.1.14", ] @@ -6356,6 +6439,33 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "knus" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6a53d1efe403777ea7ad6dcefb498c2c09f53591c361ca175e2adb050bf95e" +dependencies = [ + "base64 0.22.1", + "chumsky", + "knus-derive", + "miette 7.6.0", + "thiserror 2.0.18", + "unicode-width 0.2.0", +] + +[[package]] +name = "knus-derive" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268277f3967db51921dd2eb97256e533d718584e77b7ab1b0b72d1b6103f5a60" +dependencies = [ + "heck 0.5.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "kstat-macro" version = "0.1.0" @@ -6412,12 +6522,6 @@ dependencies = [ "spin", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libbz2-rs-sys" version = "0.2.2" @@ -6484,16 +6588,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "libipcc" -version = "0.1.0" -source = "git+https://github.com/oxidecomputer/ipcc-rs?rev=dbaad520e1f5ae32c10db16ce176f9c24de95652#dbaad520e1f5ae32c10db16ce176f9c24de95652" -dependencies = [ - "cfg-if", - "libc", - "thiserror 1.0.69", -] - [[package]] name = "libloading" version = "0.8.9" @@ -6666,9 +6760,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -6750,9 +6844,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "lpc55_areas" @@ -6787,7 +6881,7 @@ dependencies = [ "sha2 0.10.9", "thiserror 2.0.18", "x509-cert", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -6799,6 +6893,15 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "lru" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2f2f9b4ba7e6b24d95e7e899329d35be83bcded72c8540cdd5368932d1d90a" +dependencies = [ + "hashbrown 0.17.0", +] + [[package]] name = "lru-cache" version = "0.1.2" @@ -6978,18 +7081,37 @@ dependencies = [ "backtrace", "backtrace-ext", "is-terminal", - "miette-derive", + "miette-derive 5.10.0", "once_cell", "owo-colors 3.5.0", "supports-color 2.1.0", - "supports-hyperlinks", - "supports-unicode", + "supports-hyperlinks 2.1.0", + "supports-unicode 2.1.0", "terminal_size 0.1.17", "textwrap 0.15.2", "thiserror 1.0.69", "unicode-width 0.1.14", ] +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "backtrace", + "backtrace-ext", + "cfg-if", + "miette-derive 7.6.0", + "owo-colors 4.3.0", + "supports-color 3.0.2", + "supports-hyperlinks 3.2.0", + "supports-unicode 3.0.0", + "terminal_size 0.4.3", + "textwrap 0.16.2", + "unicode-width 0.1.14", +] + [[package]] name = "miette-derive" version = "5.10.0" @@ -7001,6 +7123,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "mime" version = "0.3.17" @@ -8565,6 +8698,18 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-modular" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc41a1374056e9672221567958a66c16be12d0e2c1b408761e14d901c237d5e0" + +[[package]] +name = "num-primitive" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0178502a58a2514f927965f80216c0ea2533b5364061544ea8e09b79343d4183" + [[package]] name = "num-rational" version = "0.4.2" @@ -9670,6 +9815,8 @@ dependencies = [ "fmd-adm", "fmd-adm-sys", "futures", + "gateway-client", + "gateway-types", "glob", "guppy", "hex", @@ -9750,6 +9897,9 @@ dependencies = [ "sprockets-tls", "strum 0.27.2", "subprocess", + "sush-api", + "sush-common", + "sush-server", "tar", "thiserror 2.0.18", "tofino", @@ -9833,14 +9983,17 @@ version = "0.1.0" dependencies = [ "ahash", "aho-corasick", - "anstream 0.6.21", "anyhow", "aws-lc-rs", "aws-lc-sys", "base16ct 0.2.0", "base64 0.22.1", + "base64ct", "bitflags 1.3.2", "bitflags 2.11.0", + "bitvec", + "borsh", + "borsh-derive", "bstr", "buf-list", "byte-wrapper", @@ -9849,6 +10002,7 @@ dependencies = [ "cc", "chacha20 0.10.0", "chrono", + "cipher 0.4.4", "cipher 0.5.2", "clap", "clap_builder", @@ -9857,6 +10011,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "crossterm 0.28.1", + "crypto-bigint 0.5.5", "crypto-common 0.1.7", "crypto-common 0.2.2", "ctutils", @@ -9864,6 +10019,7 @@ dependencies = [ "daft", "data-encoding", "der 0.7.10", + "dice-verifier 0.3.0-pre0 (git+https://github.com/oxidecomputer/dice-util?rev=4a39ef08d81e5177edee0bddb1146032aa21074d)", "digest 0.10.7", "digest 0.11.3", "dof 0.3.0", @@ -9887,10 +10043,12 @@ dependencies = [ "gateway-messages", "generic-array 0.14.7", "getrandom 0.2.17", - "getrandom 0.4.1", + "getrandom 0.3.4", + "getrandom 0.4.3", "group 0.13.0", "hashbrown 0.15.5", "hashbrown 0.16.1", + "hashbrown 0.17.0", "heck 0.4.1", "hex", "hickory-proto 0.25.2", @@ -9910,10 +10068,12 @@ dependencies = [ "lalrpop-util", "lazy_static", "libc", + "linux-raw-sys 0.12.1", "linux-raw-sys 0.4.15", "log", "managed", "memchr", + "miniz_oxide", "mio", "newtype-uuid", "nix 0.31.2", @@ -9925,6 +10085,7 @@ dependencies = [ "once_cell", "openapiv3", "peg-runtime", + "pem-rfc7468 0.7.0", "percent-encoding", "petgraph 0.6.5", "petgraph 0.8.3", @@ -9933,10 +10094,9 @@ dependencies = [ "ppv-lite86", "predicates", "proc-macro2", + "qorb", "quote", - "rand 0.8.6", "rand 0.9.2", - "rand_chacha 0.3.1", "rand_chacha 0.9.0", "regex", "regex-automata", @@ -9945,7 +10105,7 @@ dependencies = [ "reqwest 0.13.2", "rsa 0.9.10", "rustix 0.38.44", - "rustix 1.1.3", + "rustix 1.1.4", "rustls", "schemars 0.8.22", "scopeguard", @@ -9964,12 +10124,14 @@ dependencies = [ "smallvec 1.15.1", "spin", "spki 0.7.3", + "sprockets-tls", "string_cache", "strum 0.26.3", "strum 0.27.2", "subtle", "syn 1.0.109", "syn 2.0.117", + "textwrap 0.16.2", "time", "time-macros", "tokio", @@ -9995,7 +10157,7 @@ dependencies = [ "vergen-lib 9.1.0", "winnow 1.0.3", "x509-cert", - "zerocopy 0.8.40", + "zerocopy 0.8.56", "zeroize", "zip 0.6.6", "zip 4.6.1", @@ -10149,7 +10311,7 @@ dependencies = [ "serde", "tabwriter", "version_check", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -10315,7 +10477,7 @@ dependencies = [ "serde", "tabwriter", "uuid", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -10710,6 +10872,18 @@ dependencies = [ "uuid", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder 0.13.6", + "sha2 0.10.9", +] + [[package]] name = "p256" version = "0.14.0" @@ -10749,6 +10923,20 @@ dependencies = [ "sha2 0.11.0", ] +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct 0.2.0", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder 0.13.6", + "rand_core 0.6.4", + "sha2 0.10.9", +] + [[package]] name = "p521" version = "0.14.0" @@ -11298,7 +11486,37 @@ dependencies = [ "hex", "ipnet", "knuffel", - "miette", + "miette 5.10.0", + "p384 0.13.1", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", + "rand 0.8.6", + "rsa 0.9.10", + "sha1 0.10.6", + "sha2 0.10.9", + "sha3 0.10.8", + "signature 2.2.0", + "spki 0.7.3", + "x509-cert", + "zeroize", +] + +[[package]] +name = "pki-playground" +version = "0.2.0" +source = "git+https://github.com/oxidecomputer/pki-playground?rev=bcd3bf2dfe36468c494eac17463a4e10be366b35#bcd3bf2dfe36468c494eac17463a4e10be366b35" +dependencies = [ + "camino", + "clap", + "const-oid 0.9.6", + "der 0.7.10", + "digest 0.10.7", + "ed25519-dalek 2.2.0", + "flagset", + "hex", + "ipnet", + "knus", + "miette 7.6.0", "p384 0.13.1", "pem-rfc7468 0.7.0", "pkcs8 0.10.2", @@ -11503,7 +11721,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -11983,6 +12201,16 @@ dependencies = [ "psl-types", ] +[[package]] +name = "pwd" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c71c0c79b9701efe4e1e4b563b2016dd4ee789eb99badcb09d61ac4b92e4a2" +dependencies = [ + "libc", + "thiserror 1.0.69", +] + [[package]] name = "qorb" version = "0.4.1" @@ -12048,7 +12276,7 @@ checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.4.1", + "getrandom 0.4.3", "lru-slab", "rand 0.10.1", "rand_pcg", @@ -12092,6 +12320,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "r2d2" version = "0.8.10" @@ -12164,7 +12398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20 0.10.0", - "getrandom 0.4.1", + "getrandom 0.4.3", "rand_core 0.10.0", ] @@ -12281,7 +12515,7 @@ dependencies = [ "indoc 2.0.7", "instability", "itertools 0.13.0", - "lru", + "lru 0.12.5", "paste", "strum 0.26.3", "unicode-segmentation", @@ -12762,6 +12996,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rlimit" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7043b63bd0cd1aaa628e476b80e6d4023a3b50eb32789f2728908107bd0c793a" +dependencies = [ + "libc", +] + [[package]] name = "ron" version = "0.8.1" @@ -12867,21 +13110,45 @@ dependencies = [ ] [[package]] -name = "russh" -version = "0.62.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b67b5a0d8068c89dcbe9d95df986af7a851d1f3c604525274c37468e60464f" +name = "rumors" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/rumors?rev=b2675dbef99b784efb817db01920ee59dac708c3#b2675dbef99b784efb817db01920ee59dac708c3" dependencies = [ - "aes", - "aws-lc-rs", - "bitflags 2.11.0", - "block-padding", - "byteorder", + "async-stream", + "before", + "blake3", + "borsh", "bytes", - "cbc", - "cipher 0.5.2", - "crypto-bigint 0.7.5", - "ctr", + "futures", + "futures-util", + "hex", + "itertools 0.14.0", + "rand 0.8.6", + "seq-macro", + "smallvec 1.15.1", + "static_assertions", + "thiserror 2.0.18", + "tinyvec", + "tokio", + "tokio-stream", +] + +[[package]] +name = "russh" +version = "0.62.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b67b5a0d8068c89dcbe9d95df986af7a851d1f3c604525274c37468e60464f" +dependencies = [ + "aes", + "aws-lc-rs", + "bitflags 2.11.0", + "block-padding", + "byteorder", + "bytes", + "cbc", + "cipher 0.5.2", + "crypto-bigint 0.7.5", + "ctr", "curve25519-dalek 5.0.0", "data-encoding", "delegate", @@ -12894,7 +13161,7 @@ dependencies = [ "flate2", "futures", "generic-array 1.4.3", - "getrandom 0.4.1", + "getrandom 0.4.3", "ghash", "hex-literal 1.1.0", "hmac 0.13.0", @@ -12906,9 +13173,9 @@ dependencies = [ "ml-kem", "module-lattice", "num-bigint", - "p256", + "p256 0.14.0", "p384 0.14.0", - "p521", + "p521 0.14.0", "pageant", "pbkdf2", "pkcs1 0.8.0-rc.4", @@ -12928,8 +13195,8 @@ dependencies = [ "sha3 0.12.0", "signature 3.0.0", "spki 0.8.0", - "ssh-encoding", - "ssh-key", + "ssh-encoding 0.3.0", + "ssh-key 0.7.0-rc.11", "subtle", "thiserror 2.0.18", "tokio", @@ -12946,7 +13213,7 @@ checksum = "3aec6cb630dbe85d72ffd7bcd95f07e1bd69f9f270ee8adfa1afe443a6331438" dependencies = [ "log", "nix 0.31.2", - "ssh-encoding", + "ssh-encoding 0.3.0", "windows-sys 0.61.2", ] @@ -13018,14 +13285,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys 0.11.0", + "linux-raw-sys 0.12.1", "windows-sys 0.52.0", ] @@ -14781,7 +15048,7 @@ dependencies = [ "tokio", "toml 0.8.23", "uuid", - "zerocopy 0.8.40", + "zerocopy 0.8.56", ] [[package]] @@ -14822,15 +15089,15 @@ checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620" [[package]] name = "sprockets-tls" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/sprockets.git?rev=68a4b3bf819722f9f57a3f0c99e1393ed01ba392#68a4b3bf819722f9f57a3f0c99e1393ed01ba392" +source = "git+https://github.com/oxidecomputer/sprockets.git?rev=b8782742d1bd91546887fbc1757a7d024a5469ef#b8782742d1bd91546887fbc1757a7d024a5469ef" dependencies = [ "anyhow", - "attest-data 0.5.0 (git+https://github.com/oxidecomputer/dice-util?rev=6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd)", + "attest-data 0.5.0 (git+https://github.com/oxidecomputer/dice-util?rev=4a39ef08d81e5177edee0bddb1146032aa21074d)", "camino", "cfg-if", "clap", "dice-mfg-msgs", - "dice-verifier 0.3.0-pre0 (git+https://github.com/oxidecomputer/dice-util?rev=6e0ef48f72ff85ba50fc8286c8e89dc5f9c822dd)", + "dice-verifier 0.3.0-pre0 (git+https://github.com/oxidecomputer/dice-util?rev=4a39ef08d81e5177edee0bddb1146032aa21074d)", "ed25519-dalek 2.2.0", "hubpack", "libipcc 0.1.0 (git+https://github.com/oxidecomputer/ipcc-rs?rev=524eb8f125003dff50b9703900c6b323f00f9e1b)", @@ -14855,10 +15122,10 @@ dependencies = [ [[package]] name = "sprockets-tls-test-utils" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/sprockets.git?rev=68a4b3bf819722f9f57a3f0c99e1393ed01ba392#68a4b3bf819722f9f57a3f0c99e1393ed01ba392" +source = "git+https://github.com/oxidecomputer/sprockets.git?rev=b8782742d1bd91546887fbc1757a7d024a5469ef#b8782742d1bd91546887fbc1757a7d024a5469ef" dependencies = [ "camino", - "pki-playground", + "pki-playground 0.2.0 (git+https://github.com/oxidecomputer/pki-playground?rev=7600756029ce046a02c6234aa84ce230cc5eaa04)", ] [[package]] @@ -14893,6 +15160,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher 0.4.4", + "ssh-encoding 0.2.0", +] + [[package]] name = "ssh-cipher" version = "0.3.0" @@ -14907,10 +15184,21 @@ dependencies = [ "ctutils", "des", "poly1305 0.9.0", - "ssh-encoding", + "ssh-encoding 0.3.0", "zeroize", ] +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "pem-rfc7468 0.7.0", + "sha2 0.10.9", +] + [[package]] name = "ssh-encoding" version = "0.3.0" @@ -14926,6 +15214,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ssh-key" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" +dependencies = [ + "ed25519-dalek 2.2.0", + "p256 0.13.2", + "p384 0.13.1", + "p521 0.13.3", + "rand_core 0.6.4", + "rsa 0.9.10", + "sec1 0.7.3", + "serde", + "sha2 0.10.9", + "signature 2.2.0", + "ssh-cipher 0.2.0", + "ssh-encoding 0.2.0", + "subtle", + "zeroize", +] + [[package]] name = "ssh-key" version = "0.7.0-rc.11" @@ -14938,17 +15248,17 @@ dependencies = [ "ed25519-dalek 3.0.0", "hex", "hmac 0.13.0", - "p256", + "p256 0.14.0", "p384 0.14.0", - "p521", + "p521 0.14.0", "rand_core 0.10.0", "rsa 0.10.0-rc.18", "sec1 0.8.1", "sha1 0.11.0", "sha2 0.11.0", "signature 3.0.0", - "ssh-cipher", - "ssh-encoding", + "ssh-cipher 0.3.0", + "ssh-encoding 0.3.0", "zeroize", ] @@ -15129,6 +15439,14 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "suanpan" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/rumors?rev=b2675dbef99b784efb817db01920ee59dac708c3#b2675dbef99b784efb817db01920ee59dac708c3" +dependencies = [ + "dashu-int", +] + [[package]] name = "subprocess" version = "0.2.15" @@ -15228,6 +15546,12 @@ dependencies = [ "is-terminal", ] +[[package]] +name = "supports-hyperlinks" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91" + [[package]] name = "supports-unicode" version = "2.1.0" @@ -15237,6 +15561,145 @@ dependencies = [ "is-terminal", ] +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + +[[package]] +name = "sush-api" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/sush?rev=c987748c331dd8f78873f77fc5a39c11520d9fc9#c987748c331dd8f78873f77fc5a39c11520d9fc9" +dependencies = [ + "borsh", + "chrono", + "dropshot", + "http-range-header", + "hyper", + "schemars 0.8.22", + "serde", + "sled-hardware-types", + "sush-common", + "thiserror 1.0.69", +] + +[[package]] +name = "sush-common" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/sush?rev=c987748c331dd8f78873f77fc5a39c11520d9fc9#c987748c331dd8f78873f77fc5a39c11520d9fc9" +dependencies = [ + "blake3", + "borsh", + "bytes", + "bytesize", + "chrono", + "crypto-bigint 0.5.5", + "ed25519-dalek 2.2.0", + "http", + "http-range-header", + "p256 0.13.2", + "pem-rfc7468 0.7.0", + "rand 0.8.6", + "rand_core 0.6.4", + "rlimit", + "rustix 1.1.4", + "schemars 0.8.22", + "serde", + "serde_json", + "sha2 0.10.9", + "signature 2.2.0", + "sled-hardware-types", + "slog", + "ssh-key 0.6.7", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-tungstenite 0.28.0", + "x509-cert", +] + +[[package]] +name = "sush-proxy" +version = "0.1.0" +dependencies = [ + "anyhow", + "camino", + "clap", + "futures", + "gateway-client", + "gateway-types", + "omicron-common", + "omicron-ddm-admin-client", + "omicron-workspace-hack", + "oxide-tokio-rt", + "reqwest 0.13.2", + "sled-hardware-types", + "slog", + "slog-async", + "slog-dtrace", + "slog-term", + "sprockets-tls", + "sush-common", + "sush-server", + "tokio", + "tokio-util", +] + +[[package]] +name = "sush-server" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/sush?rev=c987748c331dd8f78873f77fc5a39c11520d9fc9#c987748c331dd8f78873f77fc5a39c11520d9fc9" +dependencies = [ + "async-trait", + "blake3", + "borsh", + "bytes", + "bytesize", + "camino", + "chrono", + "clap", + "dropshot", + "ed25519-dalek 2.2.0", + "futures", + "http", + "http-body-util", + "http-range-header", + "hyper", + "hyper-util", + "libc", + "lru 0.18.2", + "memmap2", + "p256 0.13.2", + "percent-encoding", + "pwd", + "qorb", + "rand_core 0.6.4", + "rumors", + "rustix 1.1.4", + "rustls", + "schemars 0.8.22", + "serde", + "serde_json", + "sha2 0.10.9", + "sled-hardware-types", + "slog", + "slog-term", + "sprockets-tls", + "ssh-key 0.6.7", + "sush-api", + "sush-common", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-rustls", + "tokio-stream", + "tokio-tungstenite 0.28.0", + "tokio-util", + "uuid", + "x509-cert", +] + [[package]] name = "swrite" version = "0.1.0" @@ -15409,14 +15872,14 @@ checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" [[package]] name = "tempfile" -version = "3.25.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.1", + "getrandom 0.3.4", "once_cell", - "rustix 1.1.3", + "rustix 1.1.4", "windows-sys 0.52.0", ] @@ -15465,7 +15928,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 1.1.3", + "rustix 1.1.4", "windows-sys 0.60.2", ] @@ -15682,9 +16145,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -15889,6 +16352,18 @@ dependencies = [ "tungstenite 0.23.0", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.28.0", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -16673,6 +17148,23 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "sha1 0.10.6", + "thiserror 2.0.18", + "utf-8", +] + [[package]] name = "twox-hash" version = "2.1.2" @@ -17052,7 +17544,9 @@ version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ - "getrandom 0.4.1", + "borsh", + "borsh-derive", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -17240,15 +17734,6 @@ dependencies = [ "wit-bindgen", ] -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasite" version = "0.1.0" @@ -17323,28 +17808,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.14.0", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.4.2" @@ -17371,18 +17834,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver 1.0.28", -] - [[package]] name = "web-sys" version = "0.3.91" @@ -18260,88 +18711,6 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap 2.14.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap 2.14.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.14.0", - "log", - "semver 1.0.28", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] [[package]] name = "wnaf" @@ -18388,7 +18757,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.1.3", + "rustix 1.1.4", ] [[package]] @@ -18508,11 +18877,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.40" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ - "zerocopy-derive 0.8.40", + "zerocopy-derive 0.8.56", ] [[package]] @@ -18539,9 +18908,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.40" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 6ce5f5377d1..f2ddb374098 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,6 +169,7 @@ members = [ "sled-storage/zfs-test-harness", "sp-sim", "support-bundle-collection", + "sush-proxy", "test-utils", "trust-quorum", "trust-quorum/gfss", @@ -364,6 +365,7 @@ default-members = [ "sled-storage/zfs-test-harness", "sp-sim", "support-bundle-collection", + "sush-proxy", "trust-quorum", "trust-quorum/gfss", "trust-quorum/protocol", @@ -838,8 +840,8 @@ slog-term = "2.9.1" smf = "0.2" socket2 = { version = "0.5", features = ["all"] } sp-sim = { path = "sp-sim" } -sprockets-tls = { git = "https://github.com/oxidecomputer/sprockets.git", rev = "68a4b3bf819722f9f57a3f0c99e1393ed01ba392" } -sprockets-tls-test-utils = { git = "https://github.com/oxidecomputer/sprockets.git", rev = "68a4b3bf819722f9f57a3f0c99e1393ed01ba392" } +sprockets-tls = { git = "https://github.com/oxidecomputer/sprockets.git", rev = "b8782742d1bd91546887fbc1757a7d024a5469ef", default-features = false } +sprockets-tls-test-utils = { git = "https://github.com/oxidecomputer/sprockets.git", rev = "b8782742d1bd91546887fbc1757a7d024a5469ef" } sqlformat = "0.3.5" sqlparser = { version = "0.61.0", features = [ "visitor" ] } static_assertions = "1.1.0" @@ -853,6 +855,9 @@ subtle = "2.6.1" supports-color = "3.0.2" support-bundle-collection = { path = "support-bundle-collection" } support-bundle-viewer = "0.1.2" +sush-api = { git = "https://github.com/oxidecomputer/sush", rev = "c987748c331dd8f78873f77fc5a39c11520d9fc9" } +sush-common = { git = "https://github.com/oxidecomputer/sush", rev = "c987748c331dd8f78873f77fc5a39c11520d9fc9" } +sush-server = { git = "https://github.com/oxidecomputer/sush", rev = "c987748c331dd8f78873f77fc5a39c11520d9fc9", features = ["embedded"] } swrite = "0.1.0" sync-ptr = "0.1.4" libsw = { version = "3.5.0", features = ["tokio"] } @@ -1156,3 +1161,8 @@ path = "workspace-hack" [patch."https://github.com/oxidecomputer/omicron"] omicron-uuid-kinds = { path = "uuid-kinds" } omicron-common = { path = "common" } +# sush depends on this repo's sled-hardware-types as a Git dependency, for +# BaseboardId. Without this directive the build gets two distinct copies of the +# crate, and the BaseboardId that sush's API expects is a *different type* from +# the one sled-agent derives from HardwareManager::baseboard(). +sled-hardware-types = { path = "sled-hardware/types" } diff --git a/clients/ddm-admin-client/src/lib.rs b/clients/ddm-admin-client/src/lib.rs index c9450ecefaa..05ed10fdc25 100644 --- a/clients/ddm-admin-client/src/lib.rs +++ b/clients/ddm-admin-client/src/lib.rs @@ -14,6 +14,9 @@ use either::Either; use omicron_common::address::BOOTSTRAP_PREFIX; use omicron_common::address::BOOTSTRAP_SLED_SUBNET_PREFIX_LENGTH; use omicron_common::address::DDMD_PORT; +use omicron_common::address::Ipv6Subnet; +use omicron_common::address::SLED_PREFIX_LENGTH; +use omicron_common::address::get_sled_address; use oxnet::Ipv6Net; use sled_hardware_types::underlay::BootstrapInterface; use slog::Logger; @@ -103,6 +106,34 @@ impl Client { self.inner.enable_stats(request).await.map(|resp| resp.into_inner()) } + /// Returns the sled addresses behind advertised underlay prefixes. + /// These are candidates that callers must probe: not every underlay + /// prefix is a sled subnet (RFD 63). + pub async fn derive_sled_addrs_from_prefixes( + &self, + ) -> Result + use<>, DdmError> { + let subnets = self.derive_underlay_subnets_from_prefixes().await?; + Ok(subnets.map(get_sled_address)) + } + + /// Returns advertised underlay subnets. These are candidates that + /// callers must probe: sleds also advertise internal DNS subnets, + /// and the architecture reserves a services prefix (RFD 63). + pub async fn derive_underlay_subnets_from_prefixes( + &self, + ) -> Result< + impl Iterator> + use<>, + DdmError, + > { + let prefixes = self.inner.get_prefixes().await?.into_inner(); + Ok(prefixes.into_values().flatten().filter_map(|prefix| { + let addr = prefix.destination.addr(); + (prefix.destination.width() == SLED_PREFIX_LENGTH + && addr.segments()[0] != BOOTSTRAP_PREFIX) + .then(|| Ipv6Subnet::new(addr)) + })) + } + /// Returns the addresses of connected sleds. /// /// Note: These sleds have not yet been verified. diff --git a/common/src/address.rs b/common/src/address.rs index 953ccd40df2..bb815401587 100644 --- a/common/src/address.rs +++ b/common/src/address.rs @@ -232,6 +232,9 @@ pub const BOOTSTRAP_AGENT_RACK_INIT_PORT: u16 = 12346; pub const BOOTSTORE_PORT: u16 = 12347; pub const REPO_DEPOT_PORT: u16 = 12348; pub const TRUST_QUORUM_PORT: u16 = 12349; +pub const SUSH_API_PORT: u16 = 12350; +pub const SUSH_GOSSIP_PORT: u16 = 12351; +pub const SUSH_PROXY_PORT: u16 = 12352; pub const BOOTSTRAP_AGENT_LOCKSTEP_PORT: u16 = 8080; diff --git a/dev-tools/ls-apis/api-manifest.toml b/dev-tools/ls-apis/api-manifest.toml index 44445adf01a..0837578e497 100644 --- a/dev-tools/ls-apis/api-manifest.toml +++ b/dev-tools/ls-apis/api-manifest.toml @@ -58,6 +58,7 @@ packages = [ "lldpd", "mgd", "omicron-gateway", + "sush-proxy", "tfportd", "wicketd", ] @@ -774,6 +775,30 @@ permalinks = [ "https://github.com/oxidecomputer/omicron/blob/6cef874/sled-agent/src/instance.rs#L2283", ] +[[intra_deployment_unit_only_edges]] +server = "sush-proxy" +client = "ddm-admin-client" +note = """ +sush-proxy discovers sleds through its own switch zone's ddmd, always +via Client::localhost (sush-proxy/src/lib.rs). +""" +permalinks = [ + "https://github.com/oxidecomputer/omicron/blob/sush-integration/sush-proxy/src/lib.rs", +] + +[[intra_deployment_unit_only_edges]] +server = "sush-proxy" +client = "gateway-client" +note = """ +sush-proxy reads the cubby map from its own switch zone's MGS. +sled-agent sets the mgs-address SMF property to [::1] (services.rs), +and the manifest passes it via --mgs-address. +""" +permalinks = [ + "https://github.com/oxidecomputer/omicron/blob/sush-integration/sled-agent/src/services.rs", + "https://github.com/oxidecomputer/omicron/blob/sush-integration/smf/sush-proxy/manifest.xml", +] + [[intra_deployment_unit_only_edges]] server = "propolis-server" client = "sled-agent-client" diff --git a/dev-tools/ls-apis/tests/api_dependencies.out b/dev-tools/ls-apis/tests/api_dependencies.out index d7c9c9671a6..2d51d7d5fca 100644 --- a/dev-tools/ls-apis/tests/api_dependencies.out +++ b/dev-tools/ls-apis/tests/api_dependencies.out @@ -27,6 +27,7 @@ Maghemite DDM Admin (client: ddm-admin-client) consumed by: mgd (maghemite/mgd) via 1 path consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path consumed by: sled-agent-rack-setup (omicron/sled-agent/rack-setup) via 1 path [embedded in omicron-sled-agent; rack-init only] + consumed by: sush-proxy (omicron/sush-proxy) via 1 path consumed by: wicketd (omicron/wicketd) via 1 path DNS Server (client: dns-service-client) @@ -49,7 +50,8 @@ Management Gateway Service (client: gateway-client) consumed by: lldpd (lldp/lldpd) via 1 path consumed by: mgd (maghemite/mgd) via 1 path consumed by: omicron-nexus (omicron/nexus) via 6 paths - consumed by: omicron-sled-agent (omicron/sled-agent) via 2 paths + consumed by: omicron-sled-agent (omicron/sled-agent) via 3 paths + consumed by: sush-proxy (omicron/sush-proxy) via 1 path consumed by: wicketd (omicron/wicketd) via 3 paths Wicketd Installinator (client: installinator-client) diff --git a/package-manifest.toml b/package-manifest.toml index edb753576ce..5bb3c7fd3e9 100644 --- a/package-manifest.toml +++ b/package-manifest.toml @@ -525,6 +525,16 @@ source.paths = [{ from = "smf/wicketd", to = "/var/svc/manifest/site/wicketd" }] output.type = "zone" output.intermediate_only = true +[package.sush-proxy] +service_name = "sush-proxy" +only_for_targets.image = "standard" +source.type = "local" +source.rust.binary_names = ["sush-proxy"] +source.rust.release = true +source.paths = [{ from = "smf/sush-proxy", to = "/var/svc/manifest/site/sush-proxy" }] +output.type = "zone" +output.intermediate_only = true + [package.wicket] service_name = "wicket" only_for_targets.image = "standard" @@ -856,6 +866,7 @@ source.packages = [ "pumpkind.tar.gz", "wicketd.tar.gz", "wicket.tar.gz", + "sush-proxy.tar.gz", "mg-ddm.tar.gz", "mgd.tar.gz", "switch_zone_setup.tar.gz", @@ -883,6 +894,7 @@ source.packages = [ "lldp.tar.gz", "wicketd.tar.gz", "wicket.tar.gz", + "sush-proxy.tar.gz", "mg-ddm.tar.gz", "mgd.tar.gz", "switch_zone_setup.tar.gz", @@ -910,6 +922,7 @@ source.packages = [ "lldp.tar.gz", "wicketd.tar.gz", "wicket.tar.gz", + "sush-proxy.tar.gz", "mg-ddm.tar.gz", "mgd.tar.gz", "switch_zone_setup.tar.gz", diff --git a/sled-agent/Cargo.toml b/sled-agent/Cargo.toml index 4eff4a407f3..f4ab9ee2238 100644 --- a/sled-agent/Cargo.toml +++ b/sled-agent/Cargo.toml @@ -39,6 +39,8 @@ dropshot.workspace = true flate2.workspace = true flume.workspace = true futures.workspace = true +gateway-client.workspace = true +gateway-types.workspace = true glob.workspace = true hex.workspace = true http.workspace = true @@ -108,8 +110,11 @@ slog-dtrace.workspace = true slog-error-chain.workspace = true slog-term.workspace = true smf.workspace = true -sprockets-tls.workspace = true +sprockets-tls = { workspace = true, features = ["ipcc"] } strum.workspace = true +sush-api.workspace = true +sush-common.workspace = true +sush-server.workspace = true tar.workspace = true thiserror.workspace = true tofino.workspace = true @@ -126,7 +131,7 @@ tufaceous-brand-metadata.workspace = true usdt.workspace = true uuid.workspace = true walkdir.workspace = true -x509-cert.workspace = true +x509-cert = { workspace = true, features = ["std"] } zeroize.workspace = true zip.workspace = true zone.workspace = true diff --git a/sled-agent/config-reconciler/src/handle.rs b/sled-agent/config-reconciler/src/handle.rs index 6158d217c11..78e57517d31 100644 --- a/sled-agent/config-reconciler/src/handle.rs +++ b/sled-agent/config-reconciler/src/handle.rs @@ -511,6 +511,27 @@ impl AvailableDatasetsReceiver { } } + /// Wait until the set of available datasets may have changed. + /// + /// Returns immediately if it has changed since this receiver last observed + /// it, and never returns for the test variants, whose datasets are fixed. + pub async fn changed(&mut self) { + match &mut self.inner { + AvailableDatasetsReceiverInner::Real(receiver) => { + // An error means the reconciler task is gone, in which case the + // set can never change again. + if receiver.changed().await.is_err() { + std::future::pending().await + } + } + #[cfg(feature = "testing")] + AvailableDatasetsReceiverInner::FakeTempDir { .. } + | AvailableDatasetsReceiverInner::FakeStatic(_) => { + std::future::pending().await + } + } + } + pub fn all_mounted_debug_datasets(&self) -> Vec { match &self.inner { AvailableDatasetsReceiverInner::Real(receiver) => { diff --git a/sled-agent/src/bootstrap/bootstore_setup.rs b/sled-agent/src/bootstrap/bootstore_setup.rs index 020f7506761..f4d9dd54d12 100644 --- a/sled-agent/src/bootstrap/bootstore_setup.rs +++ b/sled-agent/src/bootstrap/bootstore_setup.rs @@ -11,6 +11,7 @@ use super::server::StartError; use bootstore::schemes::v0 as bootstore; use camino::Utf8PathBuf; use omicron_common::address::BOOTSTORE_PORT; +use omicron_common::address::SUSH_GOSSIP_PORT; use omicron_common::address::TRUST_QUORUM_PORT; use omicron_ddm_admin_client::Client as DdmAdminClient; use sled_hardware_types::Baseboard; @@ -22,6 +23,7 @@ use std::collections::BTreeSet; use std::net::Ipv6Addr; use std::net::SocketAddrV6; use std::time::Duration; +use tokio::sync::watch; const BOOTSTORE_FSM_STATE_FILE: &str = "bootstore-fsm-state.json"; const BOOTSTORE_NETWORK_CONFIG_FILE: &str = "bootstore-network-config.json"; @@ -75,10 +77,11 @@ fn bootstore_network_config_paths( Ok(paths) } -pub async fn poll_ddmd_for_bootstore_and_tq_peer_update( +pub async fn poll_ddmd_for_peer_updates( log: Logger, bootstore_node_handle: bootstore::NodeHandle, trust_quorum_handle: trust_quorum::NodeTaskHandle, + sush_gossip_tx: watch::Sender>, ) { let mut current_bootstore_peers: BTreeSet = BTreeSet::new(); let mut current_tq_peers: BTreeSet = BTreeSet::new(); @@ -137,6 +140,18 @@ pub async fn poll_ddmd_for_bootstore_and_tq_peer_update( return; } } + // Inform the sush gossip manager of all known peer addresses + let sush_peers: BTreeSet<_> = addrs + .iter() + .map(|ip| SocketAddrV6::new(*ip, SUSH_GOSSIP_PORT, 0, 0)) + .collect(); + sush_gossip_tx.send_if_modified(|current| { + let changed = *current != sush_peers; + if changed { + *current = sush_peers; + } + changed + }); } Err(err) => { warn!( diff --git a/sled-agent/src/config.rs b/sled-agent/src/config.rs index 5f4d2d83be3..bec78b7000c 100644 --- a/sled-agent/src/config.rs +++ b/sled-agent/src/config.rs @@ -120,6 +120,55 @@ pub struct Config { /// root certificates and whether to use local certificate chain or /// one over IPCC pub sprockets: SprocketsConfig, + + /// Settings for the Support Shell server (RFD 620). If this is absent, no + /// Support Shell server runs on this sled. + #[serde(default)] + pub sush: Option, +} + +/// Configuration for the Support Shell (`sush`) server that runs in the global +/// zone. See RFD 620. +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct SushConfig { + /// PEM files holding the trusted root certificates for job requests, + /// one certificate per file. A job whose signature does not chain to + /// one of these is refused, so an empty list means no job can ever run. + pub roots: Vec, + + /// Where to record job output before an encrypted dataset is available. + /// This is on the ramdisk, so it costs global zone memory and does not + /// survive a reboot. + #[serde(default = "SushConfig::default_ramdisk_dir")] + pub ramdisk_dir: Utf8PathBuf, + + /// Maximum output a single job may record while it is being written to + /// `ramdisk_dir`. Deliberately small: this is memory. + #[serde(default = "SushConfig::default_ramdisk_max_output_mb")] + pub ramdisk_max_output_mb: u32, + + /// Maximum output a single job may record once it is being written to an + /// encrypted dataset. + #[serde(default = "SushConfig::default_max_output_mb")] + pub max_output_mb: u32, +} + +impl SushConfig { + // TODO: check that this is the right default. `/var/run/oxide` is tmpfs + // and is created during bootstrap, but it exists to hold ZFS key files + // (see `illumos_utils::zfs::KEYPATH_ROOT`), not bulk job output. + fn default_ramdisk_dir() -> Utf8PathBuf { + "/var/run/oxide/sush".into() + } + + fn default_ramdisk_max_output_mb() -> u32 { + 64 + } + + fn default_max_output_mb() -> u32 { + 10 * 1024 + } } #[derive(Debug, thiserror::Error)] @@ -210,4 +259,32 @@ mod test { } assert!(configs_seen > 0, "No sled-agent configs found"); } + + #[test] + fn test_sush_config() { + // Only `roots` is required. The rest have defaults. + let sush: SushConfig = + toml::from_str(r#"roots = ["/pkg/sush-root.pem"]"#).unwrap(); + assert_eq!(sush.roots, vec![Utf8PathBuf::from("/pkg/sush-root.pem")]); + assert_eq!(sush.ramdisk_dir, SushConfig::default_ramdisk_dir()); + assert_eq!( + sush.ramdisk_max_output_mb, + SushConfig::default_ramdisk_max_output_mb() + ); + assert_eq!(sush.max_output_mb, SushConfig::default_max_output_mb()); + + let sush: SushConfig = toml::from_str( + r#" + roots = [] + ramdisk_dir = "/var/run/sush" + ramdisk_max_output_mb = 8 + max_output_mb = 128 + "#, + ) + .unwrap(); + assert!(sush.roots.is_empty()); + assert_eq!(sush.ramdisk_dir, "/var/run/sush"); + assert_eq!(sush.ramdisk_max_output_mb, 8); + assert_eq!(sush.max_output_mb, 128); + } } diff --git a/sled-agent/src/lib.rs b/sled-agent/src/lib.rs index a30026d0134..f41bc1d4949 100644 --- a/sled-agent/src/lib.rs +++ b/sled-agent/src/lib.rs @@ -35,6 +35,7 @@ pub mod server; pub mod services; mod sled_agent; mod support_bundle; +pub mod sush; mod swap_device; mod vmm_reservoir; mod zone_bundle; diff --git a/sled-agent/src/long_running_tasks.rs b/sled-agent/src/long_running_tasks.rs index bb34420cdb2..a151f567161 100644 --- a/sled-agent/src/long_running_tasks.rs +++ b/sled-agent/src/long_running_tasks.rs @@ -14,7 +14,7 @@ use crate::artifact_store::{ArtifactStore, SledAgentArtifactStoreWrapper}; use crate::bootstrap::bootstore_setup::{ - new_bootstore_config, poll_ddmd_for_bootstore_and_tq_peer_update, + new_bootstore_config, poll_ddmd_for_peer_updates, }; use crate::bootstrap::secret_retriever::{ ConfigurableSecretRetriever, ConfigurableSecretRetrieverHandle, @@ -24,6 +24,7 @@ use crate::config::Config; use crate::hardware_monitor::{HardwareMonitor, HardwareMonitorHandle}; use crate::services::ServiceManager; use crate::sled_agent::SledAgent; +use crate::sush::{GossipInputs, SushHandles, spawn_sush_tasks}; use crate::zone_bundle::ZoneBundler; use bootstore::schemes::v0 as bootstore; use key_manager::{KeyManager, StorageKeyRequester}; @@ -41,9 +42,10 @@ use sled_storage::config::MountConfig; use sled_storage::disk::RawSyntheticDisk; use slog::{Logger, info}; use sprockets_tls::keys::SprocketsConfig; -use std::net::Ipv6Addr; +use std::collections::BTreeSet; +use std::net::{Ipv6Addr, SocketAddrV6}; use std::sync::Arc; -use tokio::sync::oneshot; +use tokio::sync::{oneshot, watch}; use trust_quorum; /// A mechanism for interacting with all long running tasks that can be shared @@ -91,6 +93,9 @@ pub struct LongRunningTaskHandles { /// Handle to access the set of reference measurements pub measurements: Arc, + + /// Handles to the Support Shell server, if it is running + pub sush: Option, } pub struct LongRunningTaskResult { @@ -196,17 +201,45 @@ pub async fn spawn_all_longrunning_tasks( ) .await; + let (sush_gossip_peers_tx, sush_gossip_peers_rx) = + watch::channel(BTreeSet::new()); let bootstore = spawn_bootstore_tasks( log, &config_reconciler, &hardware_manager, global_zone_bootstrap_ip, trust_quorum.clone(), + sush_gossip_peers_tx, ) .await; let health_monitor = spawn_health_monitor_tasks(log).await; + // sush must work when the control plane doesn't, so it starts here rather + // than waiting for rack membership. Its API is served once we know our + // underlay address (see `crate::server::Server::start`). + let sush = match &config.sush { + Some(sush_config) => { + spawn_sush_tasks( + log, + sush_config, + hardware_manager.baseboard().into(), + GossipInputs { + sprockets: config.sprockets.clone(), + measurements: measurements.clone(), + bootstrap_ip: global_zone_bootstrap_ip, + peers: sush_gossip_peers_rx, + }, + config_reconciler.available_datasets_rx(), + ) + .await + } + None => { + info!(log, "sush not configured"); + None + } + }; + LongRunningTaskResult { long_running_task_handles: LongRunningTaskHandles { config_reconciler, @@ -221,6 +254,7 @@ pub async fn spawn_all_longrunning_tasks( secret_retriever: secret_retriever_config, artifact_store, measurements, + sush, }, config_reconciler_spawn_token, sled_agent_started_tx, @@ -324,6 +358,7 @@ async fn spawn_bootstore_tasks( hardware_manager: &HardwareManager, global_zone_bootstrap_ip: Ipv6Addr, tq_handle: trust_quorum::NodeTaskHandle, + sush_gossip_tx: watch::Sender>, ) -> bootstore::NodeHandle { let config = new_bootstore_config( &config_reconciler @@ -346,7 +381,7 @@ async fn spawn_bootstore_tasks( let log = log.new(o!("component" => "bootstore_ddmd_poller")); let node_handle2 = node_handle.clone(); tokio::spawn(async move { - poll_ddmd_for_bootstore_and_tq_peer_update(log, node_handle2, tq_handle) + poll_ddmd_for_peer_updates(log, node_handle2, tq_handle, sush_gossip_tx) .await }); diff --git a/sled-agent/src/server.rs b/sled-agent/src/server.rs index c23e7033150..b309c0e3041 100644 --- a/sled-agent/src/server.rs +++ b/sled-agent/src/server.rs @@ -10,6 +10,7 @@ use super::sled_agent::SledAgent; use crate::long_running_tasks::LongRunningTaskHandles; use crate::nexus::make_nexus_client; use crate::services::ServiceManager; +use crate::sush::SushHandles; use internal_dns_resolver::Resolver; use omicron_uuid_kinds::SledUuid; use sled_agent_config_reconciler::ConfigReconcilerSpawnToken; @@ -18,12 +19,16 @@ use slog::Logger; use slog_error_chain::InlineErrorChain; use std::net::SocketAddr; use std::sync::Arc; +use sush_server::JobManager; /// Packages up a [`SledAgent`], running the sled agent API under a Dropshot /// server wired up to the sled agent pub struct Server { /// Dropshot server for the API. http_server: dropshot::HttpServer, + + /// The Support Shell's task handles and API server, if it is running. + sush: Option<(SushHandles, dropshot::HttpServer>)>, } impl Server { @@ -57,6 +62,7 @@ impl Server { let nexus_client = make_nexus_client(&log, resolver); + let sush_handles = long_running_tasks_handles.sush.clone(); let sled_agent = SledAgent::new( &config, log.clone(), @@ -86,7 +92,19 @@ impl Server { ))) .start() .map_err(|error| format!("initializing server: {}", error))?; - Ok(Server { http_server }) + + // Now that we know our underlay address, sush can serve its API. + let sush = sush_handles.and_then(|handles| { + match handles.start_api(*sled_address.ip()) { + Ok(server) => Some((handles, server)), + Err(error) => { + warn!(log, "failed to start sush server"; "error" => error); + None + } + } + }); + + Ok(Server { http_server, sush }) } pub(crate) fn sled_agent(&self) -> &SledAgent { @@ -103,6 +121,13 @@ impl Server { } pub async fn close(self) -> Result<(), String> { - self.http_server.close().await + let sush = match self.sush { + Some((handles, server)) => { + handles.shutdown(); + server.close().await + } + None => Ok(()), + }; + self.http_server.close().await.and(sush) } } diff --git a/sled-agent/src/services.rs b/sled-agent/src/services.rs index f88b6d975c0..5b033804d99 100644 --- a/sled-agent/src/services.rs +++ b/sled-agent/src/services.rs @@ -27,6 +27,9 @@ use crate::config::SidecarRevision; use crate::ddm_reconciler::DdmReconciler; use crate::metrics::MetricsRequestQueue; use crate::profile::*; +use crate::sush::{ + SUSH_PROXY_CERT_CHAIN_PATH, SUSH_PROXY_KEY_PATH, mint_proxy_identity, +}; use camino::{Utf8Path, Utf8PathBuf}; use clickhouse_admin_types::CLICKHOUSE_KEEPER_CONFIG_DIR; use clickhouse_admin_types::CLICKHOUSE_KEEPER_CONFIG_FILE; @@ -67,6 +70,7 @@ use omicron_common::address::MGS_PORT; use omicron_common::address::NTP_ADMIN_PORT; use omicron_common::address::RACK_PREFIX_LENGTH; use omicron_common::address::SLED_PREFIX_LENGTH; +use omicron_common::address::SUSH_PROXY_PORT; use omicron_common::address::TFPORTD_PORT; use omicron_common::address::WICKETD_COMMISSION_PORT; use omicron_common::address::WICKETD_NEXUS_PROXY_PORT; @@ -476,6 +480,27 @@ enum SwitchService { MgDdm { mode: String }, Mgd, SpSim, + SushProxy { tls: SushProxyTls, baseboard: Baseboard }, +} + +/// How the sush proxy authenticates itself to clients. +#[derive(Clone, Copy, Debug, PartialEq)] +enum SushProxyTls { + /// The sled's platform identity: an ephemeral key whose certificate + /// the RoT signs once at zone startup. + Platform, + /// None. For development images, which have no RoT to mint an + /// identity with. + Insecure, +} + +impl SushProxyTls { + fn as_str(&self) -> &'static str { + match self { + SushProxyTls::Platform => "platform", + SushProxyTls::Insecure => "insecure", + } + } } impl illumos_utils::smf_helper::Service for SwitchService { @@ -491,6 +516,7 @@ impl illumos_utils::smf_helper::Service for SwitchService { SwitchService::MgDdm { .. } => "mg-ddm", SwitchService::Mgd => "mgd", SwitchService::SpSim => "sp-sim", + SwitchService::SushProxy { .. } => "sush-proxy", } } fn smf_name(&self) -> String { @@ -2574,6 +2600,7 @@ impl ServiceManager { let mut mgd_service = ServiceBuilder::new("oxide/mgd"); let mut mg_ddm_service = ServiceBuilder::new("oxide/mg-ddm"); let mut uplink_service = ServiceBuilder::new("oxide/uplink"); + let mut sush_proxy_service = ServiceBuilder::new("oxide/sush-proxy"); let mut switch_zone_setup_config = PropertyGroupBuilder::new("config") .add_property( @@ -2649,6 +2676,60 @@ impl ServiceManager { SwitchService::SpSim => { info!(self.inner.log, "Setting up Simulated SP service"); } + SwitchService::SushProxy { tls, baseboard } => { + info!(self.inner.log, "Setting up sush-proxy service"); + if let SushProxyTls::Platform = tls { + if let Err(err) = mint_proxy_identity( + &self.inner.log, + &installed_zone.root(), + ) + .await + { + error!( + self.inner.log, + "failed to mint the sush proxy TLS identity"; + "error" => format!("{err:#}"), + ); + } + } + let config = PropertyGroupBuilder::new("config") + // Bind `::` so the proxy serves all interfaces, + // particularly the tech ports. + .add_property( + "address", + "astring", + &format!("[::]:{SUSH_PROXY_PORT}"), + ) + .add_property( + "mgs-address", + "astring", + &format!("[::1]:{MGS_PORT}"), + ) + .add_property("tls", "astring", tls.as_str()) + .add_property( + "home", + "astring", + &format!( + "{}:{}", + baseboard.model(), + baseboard.identifier() + ), + ) + .add_property( + "priv-key", + "astring", + SUSH_PROXY_KEY_PATH, + ) + .add_property( + "cert-chain", + "astring", + SUSH_PROXY_CERT_CHAIN_PATH, + ); + sush_proxy_service = sush_proxy_service.add_instance( + ServiceInstanceBuilder::new("default") + .add_property_group(config), + ); + } SwitchService::Wicketd { baseboard } => { info!(self.inner.log, "Setting up wicketd service"); // If we're launching the switch zone, we'll have a @@ -3167,7 +3248,8 @@ impl ServiceManager { .add_service(pumpkind_service) .add_service(mgd_service) .add_service(mg_ddm_service) - .add_service(uplink_service); + .add_service(uplink_service) + .add_service(sush_proxy_service); // If we have the rack subnet, also set up /etc/resolv.conf. if let Some(info) = info { @@ -3298,6 +3380,10 @@ impl ServiceManager { SwitchService::Wicketd { baseboard: baseboard.clone() }, SwitchService::Mgd, SwitchService::MgDdm { mode: "transit".to_string() }, + SwitchService::SushProxy { + tls: SushProxyTls::Platform, + baseboard: baseboard.clone(), + }, ] } @@ -3318,6 +3404,10 @@ impl ServiceManager { asic, }, SwitchService::SpSim, + SwitchService::SushProxy { + tls: SushProxyTls::Insecure, + baseboard: baseboard.clone(), + }, ] } @@ -3349,6 +3439,10 @@ impl ServiceManager { asic, }, SwitchService::SpSim, + SwitchService::SushProxy { + tls: SushProxyTls::Insecure, + baseboard: baseboard.clone(), + }, ] } }; @@ -3720,6 +3814,9 @@ impl ServiceManager { SwitchService::SpSim => { // nothing to configure } + SwitchService::SushProxy { .. } => { + // nothing to configure + } SwitchService::Mgd => { info!(self.inner.log, "configuring mgd service"); smfh.delpropvalue_default_instance( diff --git a/sled-agent/src/sush.rs b/sled-agent/src/sush.rs new file mode 100644 index 00000000000..31fa71e83ee --- /dev/null +++ b/sled-agent/src/sush.rs @@ -0,0 +1,498 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! The Support Shell (`sush`) server that runs in the global zone. +//! +//! See RFD 620. This server runs jobs targeted at this sled on behalf of Oxide +//! support, and is deliberately independent of the rest of the control plane: +//! it is one of the tools you reach for when the control plane is the thing +//! that is broken. It therefore starts during bootstrap, before this sled knows +//! whether it is part of a rack at all. +//! +//! Startup is in two parts, because the pieces become available at different +//! times: +//! +//! * [`spawn_sush_tasks`] builds the job manager as soon as we know our +//! baseboard, and is called from `long_running_tasks`. It serves the +//! API on the bootstrap network, so the switch zone proxy can reach +//! this sled before RSS runs. +//! * [`SushHandles::start_api`] serves the API again on the underlay, +//! and is called from `server` once this sled has been told its +//! underlay address. +//! +//! Gossip runs over sprockets on the bootstrap network, so jobs and +//! sessions are shared across sleds. A universe is a gossip network +//! identity; peers that meet merge into one by a dominance rule. +//! Session state is not yet persisted, so a restart forgets the +//! current session and re-seeds its universe. + +use crate::config::SushConfig; +use anyhow::Context; +use camino::Utf8Path; +use dropshot::{ConfigDropshot, HandlerTaskMode, HttpServer, ServerBuilder}; +use gateway_client::Client as MgsClient; +use gateway_types::component::SpType; +use omicron_common::address::{ + MGS_PORT, SUSH_API_PORT, SUSH_GOSSIP_PORT, get_switch_zone_address, +}; +use omicron_ddm_admin_client::Client as DdmClient; +use sha3::{Digest as _, Sha3_256}; +use sled_agent_config_reconciler::AvailableDatasetsReceiver; +use sled_agent_measurements::MeasurementsHandle; +use sled_hardware_types::BaseboardId; +use slog::{Logger, debug, error, info, o, warn}; +use slog_error_chain::InlineErrorChain; +use sprockets_tls::ipcc::Ipcc; +use sprockets_tls::keys::SprocketsConfig; +use std::collections::BTreeSet; +use std::io; +use std::iter::once; +use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::sync::Arc; +use std::time::Duration; +use tokio::fs::{OpenOptions, create_dir_all}; +use tokio::io::AsyncWriteExt as _; +use tokio::spawn; +use tokio::sync::watch; +use tokio::task::spawn_blocking; +use tokio::time::sleep; +use tokio_util::sync::CancellationToken; +use x509_cert::Certificate; +use x509_cert::der::oid::db::rfc8410::ID_ED_25519; +use x509_cert::der::{Decode as _, Reader as _, SliceReader}; +use x509_cert::spki::AlgorithmIdentifierOwned; +use x509_cert::time::Validity; + +use sush_common::keys::{EphemeralKey, KeyType, pem_cert_chain}; +use sush_common::targets::{Cubbies, MAX_CUBBY}; +use sush_server::executor::PathIsolation; +use sush_server::gossip::{GossipConfig, isolated, spawn_gossip}; +use sush_server::link::CorpusSource; +use sush_server::output::{JobOutputDir, OutputDirs}; +use sush_server::server::ApiServer; +use sush_server::{JobManager, seed_gossip}; + +/// Subdirectory of an encrypted dataset that job output is recorded in. +const SUSH_OUTPUT_SUBDIR: &str = "sush"; + +/// Path inside the switch zone to the sush proxy's TLS private key. +pub const SUSH_PROXY_KEY_PATH: &str = "/etc/sush-proxy/key.pem"; + +/// Path inside the switch zone to the sush proxy's TLS certificate chain. +pub const SUSH_PROXY_CERT_CHAIN_PATH: &str = "/etc/sush-proxy/chain.pem"; + +/// How long a minted proxy identity claims to be valid. Nothing checks +/// expiry today, and the identity is re-minted at every zone startup. +const SUSH_PROXY_CERT_VALIDITY: Duration = + Duration::from_secs(365 * 24 * 60 * 60); + +/// How often to refresh the cubby map from MGS. +const MGS_POLL_INTERVAL: Duration = Duration::from_secs(30); + +/// How long to wait for an MGS candidate to answer. +const MGS_PROBE_TIMEOUT: Duration = Duration::from_secs(5); + +/// Maximum size of a request body the API will accept. The largest thing a +/// client sends is a signed job request or a certificate, both small. +const REQUEST_MAX_BODY_BYTES: usize = 0xFFFF; + +/// Handles to the Support Shell server's tasks. +#[derive(Clone)] +pub struct SushHandles { + log: Logger, + manager: Arc, + shutdown: CancellationToken, +} + +impl SushHandles { + /// Start serving the Support Shell API at `ip`. + pub fn start_api( + &self, + ip: Ipv6Addr, + ) -> Result>, String> { + let bind_address = + SocketAddr::V6(SocketAddrV6::new(ip, SUSH_API_PORT, 0, 0)); + let api = sush_api::sush_api_mod::api_description::() + .map_err(|err| format!("failed to describe sush API: {err}"))?; + let server = ServerBuilder::new( + api, + Arc::clone(&self.manager), + self.log.new(o!("component" => "dropshot (sush)")), + ) + .config(ConfigDropshot { + bind_address, + default_request_body_max_bytes: REQUEST_MAX_BODY_BYTES, + // An interactive job holds a websocket open for as long as it runs, + // so a handler must outlive the request that created it. + default_handler_task_mode: HandlerTaskMode::Detached, + log_headers: vec![], + compression: Default::default(), + }) + .start() + .map_err(|err| err.to_string())?; + info!( + self.log, "started sush server"; + "address" => %bind_address, + ); + Ok(server) + } + + /// Stop the Support Shell server's tasks. + pub fn shutdown(&self) { + self.shutdown.cancel(); + } +} + +/// What gossip needs from the sled: its sprockets identity, its reference +/// measurements, the bootstrap address to listen on, and where to find +/// its peers. +pub struct GossipInputs { + pub sprockets: SprocketsConfig, + pub measurements: Arc, + pub bootstrap_ip: Ipv6Addr, + pub peers: watch::Receiver>, +} + +/// Start the Support Shell server's tasks, or return `None` if we can't. +pub async fn spawn_sush_tasks( + log: &Logger, + config: &SushConfig, + own_baseboard: BaseboardId, + gossip: GossipInputs, + available_datasets_rx: AvailableDatasetsReceiver, +) -> Option { + let log = log.new(o!("component" => "sush")); + + // Job output starts out on the ramdisk, because an encrypted dataset can't + // be mounted until trust quorum has been established, and we intend to be + // useful before that. + if let Err(err) = create_dir_all(&config.ramdisk_dir).await { + error!( + log, + "not starting sush server: could not create job output directory"; + "directory" => %config.ramdisk_dir, + "error" => InlineErrorChain::new(&err), + ); + return None; + } + let (output_dirs_tx, output_dirs_rx) = watch::channel(OutputDirs::new( + config.ramdisk_dir.as_std_path(), + mb_to_bytes(config.ramdisk_max_output_mb), + )); + + if config.roots.is_empty() { + warn!(log, "sush has no root certificates, so no job will ever run"); + } + let shutdown = CancellationToken::new(); + + // Gossip runs on the bootstrap network with the sled's sprockets + // identity, and the attestation corpus is re-read per handshake because + // updates change it. A sled that cannot gossip still serves local jobs. + let GossipInputs { sprockets, measurements, bootstrap_ip, peers } = gossip; + let corpus: CorpusSource = Arc::new({ + let log = log.clone(); + move || match measurements.current_measurements() { + Ok(corpus) => corpus, + Err(e) => { + error!(log, "measurement error"; e); + vec![] + } + } + }); + let listen_addr = SocketAddrV6::new(bootstrap_ip, SUSH_GOSSIP_PORT, 0, 0); + let universe = match spawn_gossip( + &log, + GossipConfig::default(), + sprockets, + corpus, + listen_addr, + peers, + seed_gossip(), + shutdown.clone(), + ) + .await + { + Ok((_, universe)) => universe, + Err(err) => { + warn!( + log, + "gossip disabled, this sled serves local jobs only"; + "error" => InlineErrorChain::new(&err), + ); + isolated(seed_gossip()) + } + }; + + let (tx_cubbies, rx_cubbies) = watch::channel(Cubbies::new()); + let mut manager = match JobManager::new( + log.clone(), + PathIsolation::Enable, + JobOutputDir::new(output_dirs_rx), + own_baseboard, + rx_cubbies, + universe, + &config.roots, + shutdown.clone(), + ) + .await + { + Ok(manager) => manager, + Err(err) => { + error!(log, "not starting sush server"; "error" => InlineErrorChain::new(&err)); + return None; + } + }; + spawn(poll_mgs_for_cubbies( + log.new(o!("component" => "cubby map")), + tx_cubbies, + )); + + // The state manager runs until shutdown and nothing waits on it, so all we + // can usefully do with its handle is notice when it stops. + if let Some(join) = manager.take_join_handle() { + let log = log.clone(); + spawn(async move { + match join.await { + Ok(()) => info!(log, "sush state manager stopped"), + Err(err) => error!( + log, + "sush state manager failed"; + "error" => InlineErrorChain::new(&err), + ), + } + }); + } + + spawn(promote_output_dir( + log.clone(), + available_datasets_rx, + output_dirs_tx, + mb_to_bytes(config.max_output_mb), + )); + + info!(log, "started sush job manager"); + let handles = SushHandles { log, manager: Arc::new(manager), shutdown }; + + // Serve the API on the bootstrap network from the start, so the + // switch zone proxy can reach this sled before RSS runs. The + // underlay API starts separately, once this sled has an address. + match handles.start_api(bootstrap_ip) { + Ok(server) => { + let shutdown = handles.shutdown.clone(); + spawn(async move { + shutdown.cancelled().await; + server.close().await.ok(); + }); + } + Err(err) => warn!( + handles.log, + "sush is not serving on the bootstrap network"; + "error" => err, + ), + } + + Some(handles) +} + +/// Record new job output on an encrypted dataset as soon as one is +/// mounted, with a raised size limit. Output already recorded on the +/// ramdisk stays there, readable until reboot. +/// +/// The debug datasets live on the U.2s under `crypt`, so they are encrypted at +/// rest and are not mounted until the keys are available. Waiting for one to +/// appear is how we wait for trust quorum without having to know anything about +/// trust quorum. +async fn promote_output_dir( + log: Logger, + mut available_datasets_rx: AvailableDatasetsReceiver, + output_dirs_tx: watch::Sender, + max_output_bytes: u64, +) { + loop { + if let Some(dataset) = + available_datasets_rx.all_mounted_debug_datasets().first() + { + let dir = dataset.path.join(SUSH_OUTPUT_SUBDIR); + match create_dir_all(&dir).await { + Ok(()) => { + output_dirs_tx.send_modify(|dirs| { + *dirs = + dirs.moved_to(dir.as_std_path(), max_output_bytes) + }); + info!( + log, "recording job output on encrypted dataset"; + "directory" => %dir, + ); + return; + } + Err(err) => error!( + log, + "could not create job output directory on encrypted dataset, leaving it on the ramdisk"; + "directory" => %dir, + "error" => InlineErrorChain::new(&err), + ), + } + } + available_datasets_rx.changed().await; + } +} + +fn mb_to_bytes(mb: u32) -> u64 { + u64::from(mb) * 1024 * 1024 +} + +/// Keep the cubby map current from MGS's view of the SPs. +/// +/// The underlay subnets DDM advertises are candidates (RFD 63): an +/// MGS is whatever answers at a subnet's switch zone address. Answers +/// merge over what we already know, so a probe outage never erases +/// the map. +async fn poll_mgs_for_cubbies(log: Logger, cubbies: watch::Sender) { + let ddm = match DdmClient::localhost(&log) { + Ok(ddm) => ddm, + Err(err) => { + error!( + log, "not polling MGS, cubby-targeted jobs will not run here"; + "error" => InlineErrorChain::new(&err), + ); + return; + } + }; + let client = reqwest::ClientBuilder::new() + .connect_timeout(MGS_PROBE_TIMEOUT) + .timeout(MGS_PROBE_TIMEOUT) + .build() + .expect("failed to build an HTTP client"); + loop { + match ddm.derive_underlay_subnets_from_prefixes().await { + Ok(subnets) => { + let mut map = Cubbies::new(); + for subnet in subnets { + let addr = SocketAddrV6::new( + get_switch_zone_address(subnet), + MGS_PORT, + 0, + 0, + ); + let mgs = MgsClient::new_with_client( + &format!("http://{addr}"), + client.clone(), + log.clone(), + ); + if mgs.sp_local_switch_id().await.is_err() { + continue; + } + for cubby in 0..=MAX_CUBBY { + match mgs.sp_get(&SpType::Sled, cubby.into()).await { + Ok(state) => { + let state = state.into_inner(); + map.insert( + cubby, + BaseboardId { + part_number: state.model, + serial_number: state.serial_number, + }, + ); + } + Err(err) => { + debug!( + log, "no SP state for cubby"; + "cubby" => cubby, "error" => %err, + ); + } + } + } + } + cubbies.send_modify(|current| current.extend(map)); + } + Err(err) => { + warn!( + log, "unable to fetch prefixes"; + "error" => InlineErrorChain::new(&err), + ); + } + } + sleep(MGS_POLL_INTERVAL).await; + } +} + +/// Mint the switch zone proxy's TLS identity: an ephemeral key whose +/// certificate the RoT signs once, in its signing convention (Ed25519 +/// over the SHA3-256 digest of the TBS certificate). The key and chain +/// are written as PEM under `zone_root` for the proxy to serve with. +pub async fn mint_proxy_identity( + log: &Logger, + zone_root: &Utf8Path, +) -> anyhow::Result<()> { + // IPCC requests are ioctls, i.e., blocking I/O. + let (key_pem, chain_pem) = spawn_blocking(mint_proxy_pems).await??; + let key_path = format!("{zone_root}{SUSH_PROXY_KEY_PATH}"); + let chain_path = format!("{zone_root}{SUSH_PROXY_CERT_CHAIN_PATH}"); + let dir = Utf8Path::new(&key_path).parent().expect("key path has a parent"); + create_dir_all(dir).await.with_context(|| format!("creating {dir}"))?; + write_private(&key_path, key_pem.as_bytes()) + .await + .with_context(|| format!("writing {key_path}"))?; + tokio::fs::write(&chain_path, chain_pem.as_bytes()) + .await + .with_context(|| format!("writing {chain_path}"))?; + info!(log, "minted sush proxy TLS identity"; "key" => key_path); + Ok(()) +} + +/// The proxy's private key and certificate chain, PEM-encoded. +fn mint_proxy_pems() -> anyhow::Result<(String, String)> { + let ipcc = Ipcc::new().context("opening IPCC")?; + let chain_der = + ipcc.rot_get_tq_cert_chain().context("fetching the TQ cert chain")?; + // The RoT returns the chain leaf first, as sprockets assumes too. + let platform = der_cert_chain(&chain_der)?; + let issuer = platform + .first() + .context("the TQ cert chain is empty")? + .tbs_certificate + .subject + .clone(); + let leaf = EphemeralKey::new_delegated( + KeyType::Ed25519, + "CN=sush-proxy".parse().context("parsing the subject")?, + issuer, + Validity::from_now(SUSH_PROXY_CERT_VALIDITY) + .context("computing validity")?, + AlgorithmIdentifierOwned { oid: ID_ED_25519, parameters: None }, + |tbs| ipcc.rot_tq_sign(&Sha3_256::digest(tbs)), + ) + .context("minting the proxy key")?; + let key_pem = leaf.private_key_pem().context("encoding the proxy key")?; + let chain = once(leaf.cert().clone()).chain(platform).collect::>(); + let chain_pem = pem_cert_chain(chain).context("encoding the chain")?; + Ok((key_pem, chain_pem)) +} + +/// Parse a concatenated series of DER certs, as the RoT returns. +fn der_cert_chain(bytes: &[u8]) -> anyhow::Result> { + let mut chain = Vec::new(); + let mut reader = + SliceReader::new(bytes).context("reading the TQ cert chain")?; + while !reader.is_finished() { + chain.push( + Certificate::decode(&mut reader) + .context("parsing the TQ cert chain")?, + ); + } + Ok(chain) +} + +/// Write a file readable only by the owner. +async fn write_private(path: &str, contents: &[u8]) -> io::Result<()> { + let mut file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .mode(0o600) + .open(path) + .await?; + file.write_all(contents).await?; + file.flush().await +} diff --git a/smf/sled-agent/gimlet-standalone/config.toml b/smf/sled-agent/gimlet-standalone/config.toml index 547ea950c11..32bf961d5e9 100644 --- a/smf/sled-agent/gimlet-standalone/config.toml +++ b/smf/sled-agent/gimlet-standalone/config.toml @@ -84,3 +84,7 @@ resolve = { which = "ipcc" } attest = { which = "ipcc" } roots = ["/usr/share/oxide/idcerts/staging.pem", "/usr/share/oxide/idcerts/production.pem"] enforce = "Enforced" + +# Support Shell (RFD 620). TODO: replace sandbox cert with prod. +[sush] +roots = ["/opt/oxide/sled-agent/pkg/sush-sandbox.pem"] diff --git a/smf/sled-agent/gimlet-standalone/sush-sandbox.pem b/smf/sled-agent/gimlet-standalone/sush-sandbox.pem new file mode 100644 index 00000000000..17b6a761ef2 --- /dev/null +++ b/smf/sled-agent/gimlet-standalone/sush-sandbox.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBpTCCAUqgAwIBAgIUcaflT3yMc6uPJUFaRV5NZtUdXi0wCgYIKoZIzj0EAwIw +UDELMAkGA1UEBhMCVVMxHzAdBgNVBAoMFk94aWRlIENvbXB1dGVyIENvbXBhbnkx +IDAeBgNVBAMMF1N1cHBvcnQgU2hlbGwgUHJvdG90eXBlMCIYDzIwMjYwMTI3MTk0 +NzU2WhgPOTk5OTEyMzEyMzU5NTlaMFAxCzAJBgNVBAYTAlVTMR8wHQYDVQQKDBZP +eGlkZSBDb21wdXRlciBDb21wYW55MSAwHgYDVQQDDBdTdXBwb3J0IFNoZWxsIFBy +b3RvdHlwZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP5+eaTZnpF4tghwJolg +m8te/yAfAnkgzOV1FJiH0nauoM8gsyoiVAu9CwlHNnivju6TslFFyxBlW5h3eMnJ +ULwwCgYIKoZIzj0EAwIDSQAwRgIhAIqvomz/EXcZGCti2QwHkdPOMfidDw02/Rwk +t3x+5DsfAiEAlEeWgVPiLcseslP86MJpLvVPFFO3S08IODCNgOUh7tM= +-----END CERTIFICATE----- diff --git a/smf/sled-agent/gimlet/config.toml b/smf/sled-agent/gimlet/config.toml index 92a9cda8392..6e5d1d16aa3 100644 --- a/smf/sled-agent/gimlet/config.toml +++ b/smf/sled-agent/gimlet/config.toml @@ -79,3 +79,7 @@ resolve = { which = "ipcc" } attest = { which = "ipcc" } roots = ["/usr/share/oxide/idcerts/staging.pem", "/usr/share/oxide/idcerts/production.pem"] enforce = "Enforced" + +# Support Shell (RFD 620). TODO: replace sandbox cert with prod. +[sush] +roots = ["/opt/oxide/sled-agent/pkg/sush-sandbox.pem"] diff --git a/smf/sled-agent/gimlet/sush-sandbox.pem b/smf/sled-agent/gimlet/sush-sandbox.pem new file mode 100644 index 00000000000..17b6a761ef2 --- /dev/null +++ b/smf/sled-agent/gimlet/sush-sandbox.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBpTCCAUqgAwIBAgIUcaflT3yMc6uPJUFaRV5NZtUdXi0wCgYIKoZIzj0EAwIw +UDELMAkGA1UEBhMCVVMxHzAdBgNVBAoMFk94aWRlIENvbXB1dGVyIENvbXBhbnkx +IDAeBgNVBAMMF1N1cHBvcnQgU2hlbGwgUHJvdG90eXBlMCIYDzIwMjYwMTI3MTk0 +NzU2WhgPOTk5OTEyMzEyMzU5NTlaMFAxCzAJBgNVBAYTAlVTMR8wHQYDVQQKDBZP +eGlkZSBDb21wdXRlciBDb21wYW55MSAwHgYDVQQDDBdTdXBwb3J0IFNoZWxsIFBy +b3RvdHlwZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP5+eaTZnpF4tghwJolg +m8te/yAfAnkgzOV1FJiH0nauoM8gsyoiVAu9CwlHNnivju6TslFFyxBlW5h3eMnJ +ULwwCgYIKoZIzj0EAwIDSQAwRgIhAIqvomz/EXcZGCti2QwHkdPOMfidDw02/Rwk +t3x+5DsfAiEAlEeWgVPiLcseslP86MJpLvVPFFO3S08IODCNgOUh7tM= +-----END CERTIFICATE----- diff --git a/smf/sled-agent/non-gimlet/config.toml b/smf/sled-agent/non-gimlet/config.toml index 4f46cd56cb7..4bb4e704c04 100644 --- a/smf/sled-agent/non-gimlet/config.toml +++ b/smf/sled-agent/non-gimlet/config.toml @@ -129,3 +129,7 @@ resolve = { which = "local", priv_key = "/opt/oxide/sled-agent/pkg/test-sprocket attest = { which = "local", priv_key = "/opt/oxide/sled-agent/pkg/test-alias-1.key.pem", cert_chain = "/opt/oxide/sled-agent/pkg/test-alias-1.certlist.pem", log = "/opt/oxide/sled-agent/pkg/sprockets-log.bin", test_corpus = [ "/opt/oxide/sled-agent/pkg/testing-measurements/corim-rot.cbor", "/opt/oxide/sled-agent/pkg/testing-measurements/corim-sp.cbor"] } roots = ["/opt/oxide/sled-agent/pkg/test-root-a.cert.pem"] enforce = "Enforced" + +# Support Shell (RFD 620). TODO: replace sandbox cert with prod. +[sush] +roots = ["/opt/oxide/sled-agent/pkg/sush-sandbox.pem"] diff --git a/smf/sled-agent/non-gimlet/sush-sandbox.pem b/smf/sled-agent/non-gimlet/sush-sandbox.pem new file mode 100644 index 00000000000..17b6a761ef2 --- /dev/null +++ b/smf/sled-agent/non-gimlet/sush-sandbox.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBpTCCAUqgAwIBAgIUcaflT3yMc6uPJUFaRV5NZtUdXi0wCgYIKoZIzj0EAwIw +UDELMAkGA1UEBhMCVVMxHzAdBgNVBAoMFk94aWRlIENvbXB1dGVyIENvbXBhbnkx +IDAeBgNVBAMMF1N1cHBvcnQgU2hlbGwgUHJvdG90eXBlMCIYDzIwMjYwMTI3MTk0 +NzU2WhgPOTk5OTEyMzEyMzU5NTlaMFAxCzAJBgNVBAYTAlVTMR8wHQYDVQQKDBZP +eGlkZSBDb21wdXRlciBDb21wYW55MSAwHgYDVQQDDBdTdXBwb3J0IFNoZWxsIFBy +b3RvdHlwZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP5+eaTZnpF4tghwJolg +m8te/yAfAnkgzOV1FJiH0nauoM8gsyoiVAu9CwlHNnivju6TslFFyxBlW5h3eMnJ +ULwwCgYIKoZIzj0EAwIDSQAwRgIhAIqvomz/EXcZGCti2QwHkdPOMfidDw02/Rwk +t3x+5DsfAiEAlEeWgVPiLcseslP86MJpLvVPFFO3S08IODCNgOUh7tM= +-----END CERTIFICATE----- diff --git a/smf/sush-proxy/manifest.xml b/smf/sush-proxy/manifest.xml new file mode 100644 index 00000000000..9778cc57dd1 --- /dev/null +++ b/smf/sush-proxy/manifest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sush-proxy/Cargo.toml b/sush-proxy/Cargo.toml new file mode 100644 index 00000000000..fa64641b505 --- /dev/null +++ b/sush-proxy/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "sush-proxy" +version = "0.1.0" +edition.workspace = true + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +camino.workspace = true +clap.workspace = true +futures.workspace = true +gateway-client.workspace = true +gateway-types.workspace = true +omicron-common.workspace = true +omicron-ddm-admin-client.workspace = true +oxide-tokio-rt.workspace = true +reqwest = { workspace = true, features = ["json", "rustls"] } +sled-hardware-types.workspace = true +slog.workspace = true +slog-async.workspace = true +slog-dtrace.workspace = true +slog-term.workspace = true +sprockets-tls.workspace = true +sush-common.workspace = true +sush-server.workspace = true +tokio.workspace = true +tokio-util.workspace = true +omicron-workspace-hack.workspace = true diff --git a/sush-proxy/src/lib.rs b/sush-proxy/src/lib.rs new file mode 100644 index 00000000000..69a23d733c9 --- /dev/null +++ b/sush-proxy/src/lib.rs @@ -0,0 +1,210 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! The Support Shell proxy for the switch zone (RFD 620). +//! +//! Terminates technician-port connections and routes each request +//! to a sled's `sush` server. Sleds are discovered by probing the +//! addresses behind the bootstrap and underlay prefixes DDM +//! advertises, and the cubby numbering comes from MGS. +//! +//! The verification counterpart of the proxy's TLS identity lives +//! in sush's `client/src/tls.rs`. + +use std::collections::BTreeMap; +use std::net::{SocketAddr, SocketAddrV6}; +use std::time::Duration; + +use anyhow::{Context, Result}; +use camino::Utf8PathBuf; +use futures::future::join_all; +use gateway_client::Client as MgsClient; +use gateway_types::component::SpType; +use omicron_common::address::SUSH_API_PORT; +use omicron_ddm_admin_client::Client as DdmClient; +use sled_hardware_types::BaseboardId; +use sled_hardware_types::underlay::BootstrapInterface; +use slog::{Logger, debug, warn}; +use sprockets_tls::keys::ResolveSetting; +use sush_common::targets::{Cubbies, MAX_CUBBY}; +use sush_server::ProxyServer; +use sush_server::proxy::{Targets, platform_tls}; +use tokio::sync::watch; +use tokio::time::sleep; +use tokio_util::sync::CancellationToken; + +const POLL_INTERVAL: Duration = Duration::from_secs(30); +const PROBE_TIMEOUT: Duration = Duration::from_secs(5); + +/// How the proxy authenticates itself to clients. +#[derive(Clone, Debug)] +pub enum Tls { + /// The sled's platform identity: an ephemeral key minted by + /// sled-agent and signed once by the RoT, served from local files. + Platform { priv_key: Utf8PathBuf, cert_chain: Utf8PathBuf }, + /// None. For development images, which have no RoT to mint an + /// identity with. + Insecure, +} + +pub struct Config { + pub address: SocketAddr, + pub mgs_address: SocketAddrV6, + pub tls: Tls, + pub home: Option, +} + +/// Serve the proxy and its discovery loops. +pub async fn run(log: &Logger, config: Config) -> Result<()> { + let tls = match config.tls { + Tls::Platform { priv_key, cert_chain } => Some( + platform_tls(log, ResolveSetting::Local { priv_key, cert_chain }) + .context("loading the TLS identity")?, + ), + Tls::Insecure => None, + }; + let (tx_targets, rx_targets) = watch::channel(Targets::default()); + let _proxy = ProxyServer::start( + log, + config.address, + tls, + rx_targets, + config.home, + CancellationToken::new(), + ) + .await + .context("starting the proxy")?; + + let ddm = DdmClient::localhost(log).context("reaching ddmd")?; + let mgs = mgs_client(log, config.mgs_address); + tokio::join!(sleds(log, ddm, &tx_targets), cubbies(log, mgs, &tx_targets),); + unreachable!("discovery loops never return"); +} + +fn mgs_client(log: &Logger, address: SocketAddrV6) -> MgsClient { + let client = reqwest::ClientBuilder::new() + .connect_timeout(PROBE_TIMEOUT) + .timeout(PROBE_TIMEOUT) + .build() + .expect("failed to build an HTTP client"); + MgsClient::new_with_client( + &format!("http://{address}"), + client, + log.clone(), + ) +} + +/// Keep `Targets::sleds` current. Every DDM-advertised address that +/// answers `/target` is routable. Sleds answer on their bootstrap +/// addresses from boot and on their underlay addresses once RSS +/// assigns them. We probe both, and underlay wins. Answers merge +/// over what we already know, so a missed probe never evicts a +/// sled. A stale address fails at forwarding time instead. +async fn sleds(log: &Logger, ddm: DdmClient, targets: &watch::Sender) { + let probe = reqwest::ClientBuilder::new() + .connect_timeout(PROBE_TIMEOUT) + .timeout(PROBE_TIMEOUT) + .build() + .expect("failed to build an HTTP client"); + loop { + let mut sleds = BTreeMap::new(); + match ddm + .derive_bootstrap_addrs_from_prefixes(&[ + BootstrapInterface::GlobalZone, + ]) + .await + { + Ok(addrs) => { + let addrs = + addrs.map(|ip| SocketAddrV6::new(ip, SUSH_API_PORT, 0, 0)); + discover(log, &probe, addrs, &mut sleds).await; + } + Err(err) => { + warn!(log, "unable to fetch bootstrap prefixes"; "error" => %err) + } + } + match ddm.derive_sled_addrs_from_prefixes().await { + Ok(addrs) => { + let addrs = addrs + .map(|a| SocketAddrV6::new(*a.ip(), SUSH_API_PORT, 0, 0)); + discover(log, &probe, addrs, &mut sleds).await; + } + Err(err) => warn!(log, "unable to fetch prefixes"; "error" => %err), + } + targets.send_modify(|t| t.sleds.extend(sleds)); + sleep(POLL_INTERVAL).await; + } +} + +/// Probe candidate addresses concurrently and record the sleds that +/// answer. +async fn discover( + log: &Logger, + probe: &reqwest::Client, + addrs: impl Iterator, + sleds: &mut BTreeMap, +) { + let probes = + addrs.map(|addr| async move { (addr, target(probe, addr).await) }); + for (addr, result) in join_all(probes).await { + match result { + Ok(baseboard) => { + sleds.insert(baseboard, SocketAddr::V6(addr)); + } + Err(err) => { + debug!(log, "sled did not answer"; "addr" => %addr, "error" => %err); + } + } + } +} + +/// Ask a sush server which baseboard it serves. +async fn target( + probe: &reqwest::Client, + addr: SocketAddrV6, +) -> Result { + probe + .get(format!("http://{addr}/target")) + .send() + .await? + .error_for_status()? + .json() + .await +} + +/// Keep `Targets::cubbies` current from MGS's view of the SPs. +/// Answers merge over what we already know, so a probe outage never +/// erases the map. +async fn cubbies( + log: &Logger, + mgs: MgsClient, + targets: &watch::Sender, +) { + loop { + let polls = (0..=MAX_CUBBY).map(|cubby| { + let mgs = &mgs; + async move { (cubby, mgs.sp_get(&SpType::Sled, cubby.into()).await) } + }); + let mut cubbies = Cubbies::new(); + for (cubby, result) in join_all(polls).await { + match result { + Ok(state) => { + let state = state.into_inner(); + cubbies.insert( + cubby, + BaseboardId { + part_number: state.model, + serial_number: state.serial_number, + }, + ); + } + Err(err) => { + debug!(log, "no SP state for cubby"; "cubby" => cubby, "error" => %err); + } + } + } + targets.send_modify(|t| t.cubbies.extend(cubbies)); + sleep(POLL_INTERVAL).await; + } +} diff --git a/sush-proxy/src/main.rs b/sush-proxy/src/main.rs new file mode 100644 index 00000000000..4d63859f22c --- /dev/null +++ b/sush-proxy/src/main.rs @@ -0,0 +1,85 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Executable for the sush switch zone proxy. + +use std::net::{SocketAddr, SocketAddrV6}; + +use anyhow::Context; +use camino::Utf8PathBuf; +use clap::Parser; +use slog::{Drain, o}; +use sush_proxy::{Config, Tls, run}; + +#[derive(Debug, Parser)] +#[clap(name = "sush-proxy", about = "sush switch zone proxy")] +struct Args { + /// The address to listen on for sush clients + #[clap(long)] + address: SocketAddr, + + /// The address (expected to be on localhost) for MGS + #[clap(long)] + mgs_address: SocketAddrV6, + + /// How to authenticate to clients + #[clap(long, value_enum)] + tls: TlsArg, + + /// The TLS private key (PEM), for `--tls platform` + #[clap(long, required_if_eq("tls", "platform"))] + priv_key: Option, + + /// The TLS certificate chain (PEM), for `--tls platform` + #[clap(long, required_if_eq("tls", "platform"))] + cert_chain: Option, + + /// The baseboard (part:serial) of the sled hosting the proxy, + /// preferred for requests that name no target + #[clap(long)] + home: Option, +} + +/// How the proxy authenticates itself to clients. +#[derive(Clone, Copy, Debug, clap::ValueEnum)] +enum TlsArg { + /// The sled's platform identity, from a local key and chain + Platform, + /// None. For development images only + Insecure, +} + +fn main() -> anyhow::Result<()> { + oxide_tokio_rt::run(run_proxy()) +} + +async fn run_proxy() -> anyhow::Result<()> { + let Args { address, mgs_address, tls, priv_key, cert_chain, home } = + Args::parse(); + let home = home + .map(|home| { + home.parse() + .map_err(|err| anyhow::anyhow!("bad --home `{home}`: {err}")) + }) + .transpose()?; + let tls = match tls { + TlsArg::Platform => Tls::Platform { + priv_key: priv_key.unwrap(), + cert_chain: cert_chain.unwrap(), + }, + TlsArg::Insecure => Tls::Insecure, + }; + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::FullFormat::new(decorator).build().fuse(); + let drain = slog_async::Async::new(drain).build().fuse(); + let log = slog::Logger::root(drain, o!("component" => "sush-proxy")); + let (drain, registration) = slog_dtrace::with_drain(log); + let log = slog::Logger::root(drain.fuse(), o!()); + if let slog_dtrace::ProbeRegistration::Failed(err) = registration { + anyhow::bail!("failed to register DTrace probes: {err}"); + } + run(&log, Config { address, mgs_address, tls, home }) + .await + .context("running the proxy") +} diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml index 6cf43a31d9d..a2b2ef03ef5 100644 --- a/workspace-hack/Cargo.toml +++ b/workspace-hack/Cargo.toml @@ -20,26 +20,30 @@ workspace = true [dependencies] ahash = { version = "0.8.12" } aho-corasick = { version = "1.1.4" } -anstream = { version = "0.6.21" } -anyhow = { version = "1.0.102", features = ["backtrace"] } +anyhow = { version = "1.0.104", features = ["backtrace"] } aws-lc-rs = { version = "1.16.3", features = ["prebuilt-nasm"] } aws-lc-sys = { version = "0.40.0", default-features = false, features = ["prebuilt-nasm"] } base16ct = { version = "0.2.0", default-features = false, features = ["alloc"] } base64 = { version = "0.22.1" } +base64ct = { version = "1.8.3", default-features = false, features = ["std"] } bitflags-dff4ba8e3ae991db = { package = "bitflags", version = "1.3.2" } bitflags-f595c2ba2a3f28df = { package = "bitflags", version = "2.11.0", default-features = false, features = ["serde", "std"] } +bitvec = { version = "1.0.1" } +borsh = { version = "1.8.0", features = ["bytes", "de_strict_order", "derive"] } bstr = { version = "1.12.1" } buf-list = { version = "1.1.2", default-features = false, features = ["tokio1"] } byte-wrapper = { version = "0.1.0", features = ["schemars08", "serde"] } bytes = { version = "1.11.1", features = ["serde"] } -camino = { version = "1.2.2", default-features = false, features = ["serde1"] } +camino = { version = "1.2.5", default-features = false, features = ["serde1"] } chrono = { version = "0.4.45", features = ["serde"] } +cipher-9fbad63c4bcf4a8f = { package = "cipher", version = "0.4.4", default-features = false, features = ["zeroize"] } clap = { version = "4.6.1", features = ["cargo", "derive", "env", "wrap_help"] } clap_builder = { version = "4.6.0", default-features = false, features = ["cargo", "color", "env", "std", "suggestions", "usage", "wrap_help"] } const-oid = { version = "0.9.6", default-features = false, features = ["db", "std"] } crossbeam-epoch = { version = "0.9.18" } crossbeam-utils = { version = "0.8.21" } crossterm = { version = "0.28.1", features = ["event-stream", "serde"] } +crypto-bigint = { version = "0.5.5", features = ["generic-array", "zeroize"] } crypto-common-6f8ce4dd05d13bba = { package = "crypto-common", version = "0.2.2", default-features = false, features = ["getrandom"] } crypto-common-c65f7effa3be6d31 = { package = "crypto-common", version = "0.1.7", default-features = false, features = ["getrandom", "std"] } ctutils = { version = "0.4.2", default-features = false, features = ["subtle"] } @@ -47,12 +51,13 @@ curve25519-dalek = { version = "4.1.3", features = ["digest", "legacy_compatibil daft = { version = "0.1.7", features = ["derive", "newtype-uuid1", "oxnet01", "uuid1"] } data-encoding = { version = "2.10.0" } der = { version = "0.7.10", default-features = false, features = ["derive", "flagset", "oid", "pem", "std"] } +dice-verifier = { git = "https://github.com/oxidecomputer/dice-util", rev = "4a39ef08d81e5177edee0bddb1146032aa21074d", default-features = false, features = ["ipcc", "mock"] } digest-93f6ce9d446188ac = { package = "digest", version = "0.10.7", features = ["mac", "oid", "std"] } digest-a6292c17cd707f01 = { package = "digest", version = "0.11.3", features = ["alloc", "mac", "oid", "rand_core"] } ed25519-dalek = { version = "2.2.0", features = ["digest", "pem", "rand_core"] } either = { version = "1.15.0", features = ["use_std"] } elliptic-curve = { version = "0.13.8", features = ["ecdh", "hazmat", "pem", "std"] } -env_logger = { version = "0.11.9", default-features = false, features = ["auto-color"] } +env_logger = { version = "0.11.11", default-features = false, features = ["auto-color"] } ff = { version = "0.13.1", default-features = false, features = ["alloc"] } flate2 = { version = "1.1.9", features = ["zlib-rs"] } foldhash = { version = "0.2.0" } @@ -67,9 +72,10 @@ gateway-ereport-messages = { git = "https://github.com/oxidecomputer/management- gateway-messages = { git = "https://github.com/oxidecomputer/management-gateway-service", rev = "6c0aca2545a73fd75536e149d29faa7108be5862", features = ["std"] } generic-array = { version = "0.14.7", default-features = false, features = ["more_lengths", "zeroize"] } getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2.17", default-features = false, features = ["js", "rdrand", "std"] } -getrandom-9fbad63c4bcf4a8f = { package = "getrandom", version = "0.4.1", default-features = false, features = ["std", "sys_rng", "wasm_js"] } +getrandom-9fbad63c4bcf4a8f = { package = "getrandom", version = "0.4.3", default-features = false, features = ["std", "sys_rng", "wasm_js"] } group = { version = "0.13.0", default-features = false, features = ["alloc"] } hashbrown-3575ec1268b04181 = { package = "hashbrown", version = "0.15.5" } +hashbrown-9067fe90e8c1f593 = { package = "hashbrown", version = "0.17.0" } hashbrown-986da7b5efc2b80e = { package = "hashbrown", version = "0.16.1" } hex = { version = "0.4.3", features = ["serde"] } hickory-proto = { version = "0.25.2", features = ["serde", "text-parsing"] } @@ -79,14 +85,14 @@ hyper = { version = "1.10.1", features = ["full"] } iddqd = { version = "0.4.2", features = ["daft", "proptest", "schemars08"] } idna = { version = "1.1.0" } indexmap = { version = "2.14.0", features = ["serde"] } -ipnet = { version = "2.11.0", features = ["serde"] } +ipnet = { version = "2.12.1", features = ["serde"] } ipnetwork = { version = "0.21.1", features = ["schemars", "serde"] } itertools = { version = "0.13.0" } jiff = { version = "0.2.34", features = ["serde"] } lalrpop-util = { version = "0.19.12" } lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] } libc = { version = "0.2.185", features = ["extra_traits"] } -log = { version = "0.4.29", default-features = false, features = ["std"] } +log = { version = "0.4.33", default-features = false, features = ["std"] } managed = { version = "0.8.0", default-features = false, features = ["alloc", "map"] } memchr = { version = "2.8.0" } newtype-uuid = { version = "1.3.2", features = ["proptest1", "schemars08", "serde", "v4"] } @@ -98,6 +104,7 @@ num-traits = { version = "0.2.19", features = ["i128", "libm"] } once_cell = { version = "1.21.3", features = ["critical-section"] } openapiv3 = { version = "2.2.0", default-features = false, features = ["skip_serializing_defaults"] } peg-runtime = { version = "0.8.5", default-features = false, features = ["std"] } +pem-rfc7468 = { version = "0.7.0", default-features = false, features = ["std"] } percent-encoding = { version = "2.3.2" } petgraph-3b31131e45eafb45 = { package = "petgraph", version = "0.6.5", features = ["serde-1"] } petgraph-c38e5c1d305a1b54 = { package = "petgraph", version = "0.8.3", features = ["serde-1"] } @@ -106,17 +113,17 @@ postgres-types = { version = "0.2.12", default-features = false, features = ["wi ppv-lite86 = { version = "0.2.21", default-features = false, features = ["simd", "std"] } predicates = { version = "3.1.4" } proc-macro2 = { version = "1.0.106" } +qorb = { version = "0.4.1" } quote = { version = "1.0.45" } -rand-274715c4dabd11b0 = { package = "rand", version = "0.9.2" } -rand-c38e5c1d305a1b54 = { package = "rand", version = "0.8.6" } -rand_chacha-274715c4dabd11b0 = { package = "rand_chacha", version = "0.9.0", default-features = false, features = ["std"] } -rand_chacha-468e82937335b1c9 = { package = "rand_chacha", version = "0.3.1", default-features = false, features = ["std"] } +rand = { version = "0.9.2" } +rand_chacha = { version = "0.9.0", default-features = false, features = ["std"] } regex = { version = "1.12.3" } regex-automata = { version = "0.4.14", default-features = false, features = ["dfa", "hybrid", "meta", "nfa", "perf", "std", "unicode"] } regex-syntax = { version = "0.8.10" } reqwest-594e8ee84c453af0 = { package = "reqwest", version = "0.13.2", features = ["blocking", "cookies", "json", "query", "stream"] } reqwest-5ef9efb8ec2df382 = { package = "reqwest", version = "0.12.28", features = ["blocking", "json", "stream"] } rsa = { version = "0.9.10", features = ["serde", "sha2"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", features = ["process", "pty", "termios"] } rustls = { version = "0.23.41" } schemars = { version = "0.8.22", features = ["bytes", "chrono", "semver", "url", "uuid1"] } scopeguard = { version = "1.2.0" } @@ -131,14 +138,16 @@ simd-adler32 = { version = "0.3.8", default-features = false, features = ["std"] similar = { version = "2.7.0", features = ["bytes", "inline", "unicode"] } slab = { version = "0.4.12" } slog = { version = "2.8.2", features = ["dynamic-keys", "max_level_trace", "release_max_level_debug", "release_max_level_trace"] } -smallvec = { version = "1.15.1", default-features = false, features = ["const_new"] } +smallvec = { version = "1.15.1", default-features = false, features = ["const_new", "union"] } spin = { version = "0.9.8" } spki = { version = "0.7.3", default-features = false, features = ["pem", "std"] } +sprockets-tls = { git = "https://github.com/oxidecomputer/sprockets.git", rev = "b8782742d1bd91546887fbc1757a7d024a5469ef", default-features = false, features = ["ipcc"] } string_cache = { version = "0.8.9" } strum-2f80eeee3b1b6c7e = { package = "strum", version = "0.26.3", features = ["derive"] } strum-754bda37e0fb3874 = { package = "strum", version = "0.27.2", features = ["derive"] } subtle = { version = "2.6.1", features = ["const-generics"] } syn-f595c2ba2a3f28df = { package = "syn", version = "2.0.117", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } +textwrap = { version = "0.16.2", features = ["terminal_size"] } time = { version = "0.3.47", features = ["formatting", "local-offset", "macros", "parsing"] } tokio = { version = "1.52.1", features = ["full", "test-util"] } tokio-postgres = { version = "0.7.16", features = ["with-chrono-0_4", "with-serde_json-1", "with-uuid-1"] } @@ -158,9 +167,9 @@ url = { version = "2.5.8", features = ["serde"] } usdt = { version = "0.6.0" } usdt-impl-3b31131e45eafb45 = { package = "usdt-impl", version = "0.6.0", default-features = false, features = ["des"] } usdt-impl-d8f496e17d97b5cb = { package = "usdt-impl", version = "0.5.0", default-features = false, features = ["asm", "des"] } -uuid = { version = "1.23.4", features = ["serde", "v4"] } +uuid = { version = "1.23.4", features = ["borsh", "serde", "v4"] } x509-cert = { version = "0.2.5" } -zerocopy = { version = "0.8.40", default-features = false, features = ["derive", "simd"] } +zerocopy = { version = "0.8.56", default-features = false, features = ["derive", "simd"] } zeroize = { version = "1.9.0", features = ["aarch64", "std", "zeroize_derive"] } zip-164d15cefe24d7eb = { package = "zip", version = "4.6.1", default-features = false, features = ["bzip2", "deflate", "jiff-02", "zstd"] } zip-3b31131e45eafb45 = { package = "zip", version = "0.6.6", default-features = false, features = ["bzip2", "deflate"] } @@ -168,27 +177,32 @@ zip-3b31131e45eafb45 = { package = "zip", version = "0.6.6", default-features = [build-dependencies] ahash = { version = "0.8.12" } aho-corasick = { version = "1.1.4" } -anstream = { version = "0.6.21" } -anyhow = { version = "1.0.102", features = ["backtrace"] } +anyhow = { version = "1.0.104", features = ["backtrace"] } aws-lc-rs = { version = "1.16.3", features = ["prebuilt-nasm"] } aws-lc-sys = { version = "0.40.0", default-features = false, features = ["prebuilt-nasm"] } base16ct = { version = "0.2.0", default-features = false, features = ["alloc"] } base64 = { version = "0.22.1" } +base64ct = { version = "1.8.3", default-features = false, features = ["std"] } bitflags-dff4ba8e3ae991db = { package = "bitflags", version = "1.3.2" } bitflags-f595c2ba2a3f28df = { package = "bitflags", version = "2.11.0", default-features = false, features = ["serde", "std"] } +bitvec = { version = "1.0.1" } +borsh = { version = "1.8.0", features = ["bytes", "de_strict_order", "derive"] } +borsh-derive = { version = "1.8.0" } bstr = { version = "1.12.1" } buf-list = { version = "1.1.2", default-features = false, features = ["tokio1"] } byte-wrapper = { version = "0.1.0", features = ["schemars08", "serde"] } bytes = { version = "1.11.1", features = ["serde"] } -camino = { version = "1.2.2", default-features = false, features = ["serde1"] } +camino = { version = "1.2.5", default-features = false, features = ["serde1"] } cc = { version = "1.2.56", default-features = false, features = ["parallel"] } chrono = { version = "0.4.45", features = ["serde"] } +cipher-9fbad63c4bcf4a8f = { package = "cipher", version = "0.4.4", default-features = false, features = ["zeroize"] } clap = { version = "4.6.1", features = ["cargo", "derive", "env", "wrap_help"] } clap_builder = { version = "4.6.0", default-features = false, features = ["cargo", "color", "env", "std", "suggestions", "usage", "wrap_help"] } const-oid = { version = "0.9.6", default-features = false, features = ["db", "std"] } crossbeam-epoch = { version = "0.9.18" } crossbeam-utils = { version = "0.8.21" } crossterm = { version = "0.28.1", features = ["event-stream", "serde"] } +crypto-bigint = { version = "0.5.5", features = ["generic-array", "zeroize"] } crypto-common-6f8ce4dd05d13bba = { package = "crypto-common", version = "0.2.2", default-features = false, features = ["getrandom"] } crypto-common-c65f7effa3be6d31 = { package = "crypto-common", version = "0.1.7", default-features = false, features = ["getrandom", "std"] } ctutils = { version = "0.4.2", default-features = false, features = ["subtle"] } @@ -196,12 +210,13 @@ curve25519-dalek = { version = "4.1.3", features = ["digest", "legacy_compatibil daft = { version = "0.1.7", features = ["derive", "newtype-uuid1", "oxnet01", "uuid1"] } data-encoding = { version = "2.10.0" } der = { version = "0.7.10", default-features = false, features = ["derive", "flagset", "oid", "pem", "std"] } +dice-verifier = { git = "https://github.com/oxidecomputer/dice-util", rev = "4a39ef08d81e5177edee0bddb1146032aa21074d", default-features = false, features = ["ipcc", "mock"] } digest-93f6ce9d446188ac = { package = "digest", version = "0.10.7", features = ["mac", "oid", "std"] } digest-a6292c17cd707f01 = { package = "digest", version = "0.11.3", features = ["alloc", "mac", "oid", "rand_core"] } ed25519-dalek = { version = "2.2.0", features = ["digest", "pem", "rand_core"] } either = { version = "1.15.0", features = ["use_std"] } elliptic-curve = { version = "0.13.8", features = ["ecdh", "hazmat", "pem", "std"] } -env_logger = { version = "0.11.9", default-features = false, features = ["auto-color"] } +env_logger = { version = "0.11.11", default-features = false, features = ["auto-color"] } ff = { version = "0.13.1", default-features = false, features = ["alloc"] } flate2 = { version = "1.1.9", features = ["zlib-rs"] } foldhash = { version = "0.2.0" } @@ -216,9 +231,10 @@ gateway-ereport-messages = { git = "https://github.com/oxidecomputer/management- gateway-messages = { git = "https://github.com/oxidecomputer/management-gateway-service", rev = "6c0aca2545a73fd75536e149d29faa7108be5862", features = ["std"] } generic-array = { version = "0.14.7", default-features = false, features = ["more_lengths", "zeroize"] } getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2.17", default-features = false, features = ["js", "rdrand", "std"] } -getrandom-9fbad63c4bcf4a8f = { package = "getrandom", version = "0.4.1", default-features = false, features = ["std", "sys_rng", "wasm_js"] } +getrandom-9fbad63c4bcf4a8f = { package = "getrandom", version = "0.4.3", default-features = false, features = ["std", "sys_rng", "wasm_js"] } group = { version = "0.13.0", default-features = false, features = ["alloc"] } hashbrown-3575ec1268b04181 = { package = "hashbrown", version = "0.15.5" } +hashbrown-9067fe90e8c1f593 = { package = "hashbrown", version = "0.17.0" } hashbrown-986da7b5efc2b80e = { package = "hashbrown", version = "0.16.1" } heck = { version = "0.4.1", features = ["unicode"] } hex = { version = "0.4.3", features = ["serde"] } @@ -229,14 +245,14 @@ hyper = { version = "1.10.1", features = ["full"] } iddqd = { version = "0.4.2", features = ["daft", "proptest", "schemars08"] } idna = { version = "1.1.0" } indexmap = { version = "2.14.0", features = ["serde"] } -ipnet = { version = "2.11.0", features = ["serde"] } +ipnet = { version = "2.12.1", features = ["serde"] } ipnetwork = { version = "0.21.1", features = ["schemars", "serde"] } itertools = { version = "0.13.0" } jiff = { version = "0.2.34", features = ["serde"] } lalrpop-util = { version = "0.19.12" } lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] } libc = { version = "0.2.185", features = ["extra_traits"] } -log = { version = "0.4.29", default-features = false, features = ["std"] } +log = { version = "0.4.33", default-features = false, features = ["std"] } managed = { version = "0.8.0", default-features = false, features = ["alloc", "map"] } memchr = { version = "2.8.0" } newtype-uuid = { version = "1.3.2", features = ["proptest1", "schemars08", "serde", "v4"] } @@ -248,6 +264,7 @@ num-traits = { version = "0.2.19", features = ["i128", "libm"] } once_cell = { version = "1.21.3", features = ["critical-section"] } openapiv3 = { version = "2.2.0", default-features = false, features = ["skip_serializing_defaults"] } peg-runtime = { version = "0.8.5", default-features = false, features = ["std"] } +pem-rfc7468 = { version = "0.7.0", default-features = false, features = ["std"] } percent-encoding = { version = "2.3.2" } petgraph-3b31131e45eafb45 = { package = "petgraph", version = "0.6.5", features = ["serde-1"] } petgraph-c38e5c1d305a1b54 = { package = "petgraph", version = "0.8.3", features = ["serde-1"] } @@ -256,17 +273,17 @@ postgres-types = { version = "0.2.12", default-features = false, features = ["wi ppv-lite86 = { version = "0.2.21", default-features = false, features = ["simd", "std"] } predicates = { version = "3.1.4" } proc-macro2 = { version = "1.0.106" } +qorb = { version = "0.4.1" } quote = { version = "1.0.45" } -rand-274715c4dabd11b0 = { package = "rand", version = "0.9.2" } -rand-c38e5c1d305a1b54 = { package = "rand", version = "0.8.6" } -rand_chacha-274715c4dabd11b0 = { package = "rand_chacha", version = "0.9.0", default-features = false, features = ["std"] } -rand_chacha-468e82937335b1c9 = { package = "rand_chacha", version = "0.3.1", default-features = false, features = ["std"] } +rand = { version = "0.9.2" } +rand_chacha = { version = "0.9.0", default-features = false, features = ["std"] } regex = { version = "1.12.3" } regex-automata = { version = "0.4.14", default-features = false, features = ["dfa", "hybrid", "meta", "nfa", "perf", "std", "unicode"] } regex-syntax = { version = "0.8.10" } reqwest-594e8ee84c453af0 = { package = "reqwest", version = "0.13.2", features = ["blocking", "cookies", "json", "query", "stream"] } reqwest-5ef9efb8ec2df382 = { package = "reqwest", version = "0.12.28", features = ["blocking", "json", "stream"] } rsa = { version = "0.9.10", features = ["serde", "sha2"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", features = ["process", "pty", "termios"] } rustls = { version = "0.23.41" } schemars = { version = "0.8.22", features = ["bytes", "chrono", "semver", "url", "uuid1"] } scopeguard = { version = "1.2.0" } @@ -282,15 +299,17 @@ simd-adler32 = { version = "0.3.8", default-features = false, features = ["std"] similar = { version = "2.7.0", features = ["bytes", "inline", "unicode"] } slab = { version = "0.4.12" } slog = { version = "2.8.2", features = ["dynamic-keys", "max_level_trace", "release_max_level_debug", "release_max_level_trace"] } -smallvec = { version = "1.15.1", default-features = false, features = ["const_new"] } +smallvec = { version = "1.15.1", default-features = false, features = ["const_new", "union"] } spin = { version = "0.9.8" } spki = { version = "0.7.3", default-features = false, features = ["pem", "std"] } +sprockets-tls = { git = "https://github.com/oxidecomputer/sprockets.git", rev = "b8782742d1bd91546887fbc1757a7d024a5469ef", default-features = false, features = ["ipcc"] } string_cache = { version = "0.8.9" } strum-2f80eeee3b1b6c7e = { package = "strum", version = "0.26.3", features = ["derive"] } strum-754bda37e0fb3874 = { package = "strum", version = "0.27.2", features = ["derive"] } subtle = { version = "2.6.1", features = ["const-generics"] } syn-dff4ba8e3ae991db = { package = "syn", version = "1.0.109", features = ["extra-traits", "full"] } syn-f595c2ba2a3f28df = { package = "syn", version = "2.0.117", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } +textwrap = { version = "0.16.2", features = ["terminal_size"] } time = { version = "0.3.47", features = ["formatting", "local-offset", "macros", "parsing"] } time-macros = { version = "0.2.27", default-features = false, features = ["formatting", "parsing"] } tokio = { version = "1.52.1", features = ["full", "test-util"] } @@ -311,11 +330,11 @@ url = { version = "2.5.8", features = ["serde"] } usdt = { version = "0.6.0" } usdt-impl-3b31131e45eafb45 = { package = "usdt-impl", version = "0.6.0", default-features = false, features = ["des"] } usdt-impl-d8f496e17d97b5cb = { package = "usdt-impl", version = "0.5.0", default-features = false, features = ["asm", "des"] } -uuid = { version = "1.23.4", features = ["serde", "v4"] } +uuid = { version = "1.23.4", features = ["borsh", "serde", "v4"] } vergen = { version = "9.1.0", features = ["cargo", "rustc"] } vergen-lib = { version = "9.1.0", features = ["cargo", "git", "rustc"] } x509-cert = { version = "0.2.5" } -zerocopy = { version = "0.8.40", default-features = false, features = ["derive", "simd"] } +zerocopy = { version = "0.8.56", default-features = false, features = ["derive", "simd"] } zeroize = { version = "1.9.0", features = ["aarch64", "std", "zeroize_derive"] } zip-164d15cefe24d7eb = { package = "zip", version = "4.6.1", default-features = false, features = ["bzip2", "deflate", "jiff-02", "zstd"] } zip-3b31131e45eafb45 = { package = "zip", version = "0.6.6", default-features = false, features = ["bzip2", "deflate"] } @@ -324,111 +343,129 @@ zip-3b31131e45eafb45 = { package = "zip", version = "0.6.6", default-features = cookie = { version = "0.18.1", default-features = false, features = ["percent-encode"] } dof-468e82937335b1c9 = { package = "dof", version = "0.3.0", default-features = false, features = ["des"] } dof-9fbad63c4bcf4a8f = { package = "dof", version = "0.4.0", default-features = false, features = ["des"] } +getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.4", default-features = false, features = ["std"] } hyper-rustls = { version = "0.27.7", features = ["http2"] } hyper-util = { version = "0.1.20", features = ["full"] } -linux-raw-sys = { version = "0.4.15", default-features = false, features = ["elf", "errno", "general", "ioctl", "no_std", "system"] } +linux-raw-sys-5ef9efb8ec2df382 = { package = "linux-raw-sys", version = "0.12.1", default-features = false, features = ["auxvec", "elf", "errno", "general", "ioctl", "no_std", "prctl"] } +linux-raw-sys-9fbad63c4bcf4a8f = { package = "linux-raw-sys", version = "0.4.15", default-features = false, features = ["elf", "errno", "general", "ioctl", "no_std", "system"] } +miniz_oxide = { version = "0.8.9", default-features = false, features = ["simd", "with-alloc"] } mio = { version = "1.2.0", features = ["net", "os-ext"] } nix = { version = "0.31.2", default-features = false, features = ["mman"] } object = { version = "0.37.3", default-features = false, features = ["read", "std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } -rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", default-features = false, features = ["stdio"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } [target.x86_64-unknown-linux-gnu.build-dependencies] cookie = { version = "0.18.1", default-features = false, features = ["percent-encode"] } dof-468e82937335b1c9 = { package = "dof", version = "0.3.0", default-features = false, features = ["des"] } dof-9fbad63c4bcf4a8f = { package = "dof", version = "0.4.0", default-features = false, features = ["des"] } +getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.4", default-features = false, features = ["std"] } hyper-rustls = { version = "0.27.7", features = ["http2"] } hyper-util = { version = "0.1.20", features = ["full"] } -linux-raw-sys = { version = "0.4.15", default-features = false, features = ["elf", "errno", "general", "ioctl", "no_std", "system"] } +linux-raw-sys-5ef9efb8ec2df382 = { package = "linux-raw-sys", version = "0.12.1", default-features = false, features = ["auxvec", "elf", "errno", "general", "ioctl", "no_std", "prctl"] } +linux-raw-sys-9fbad63c4bcf4a8f = { package = "linux-raw-sys", version = "0.4.15", default-features = false, features = ["elf", "errno", "general", "ioctl", "no_std", "system"] } +miniz_oxide = { version = "0.8.9", default-features = false, features = ["simd", "with-alloc"] } mio = { version = "1.2.0", features = ["net", "os-ext"] } nix = { version = "0.31.2", default-features = false, features = ["mman"] } object = { version = "0.37.3", default-features = false, features = ["read", "std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } -rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", default-features = false, features = ["stdio"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } [target.x86_64-apple-darwin.dependencies] cookie = { version = "0.18.1", default-features = false, features = ["percent-encode"] } errno = { version = "0.3.14" } +getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.4", default-features = false, features = ["std"] } hyper-rustls = { version = "0.27.7", features = ["http2"] } hyper-util = { version = "0.1.20", features = ["full"] } +miniz_oxide = { version = "0.8.9", default-features = false, features = ["simd", "with-alloc"] } mio = { version = "1.2.0", features = ["net", "os-ext"] } nix = { version = "0.31.2", default-features = false, features = ["mman"] } object = { version = "0.37.3", default-features = false, features = ["read", "std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } -rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", default-features = false, features = ["stdio"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } [target.x86_64-apple-darwin.build-dependencies] cookie = { version = "0.18.1", default-features = false, features = ["percent-encode"] } errno = { version = "0.3.14" } +getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.4", default-features = false, features = ["std"] } hyper-rustls = { version = "0.27.7", features = ["http2"] } hyper-util = { version = "0.1.20", features = ["full"] } +miniz_oxide = { version = "0.8.9", default-features = false, features = ["simd", "with-alloc"] } mio = { version = "1.2.0", features = ["net", "os-ext"] } nix = { version = "0.31.2", default-features = false, features = ["mman"] } object = { version = "0.37.3", default-features = false, features = ["read", "std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } -rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", default-features = false, features = ["stdio"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } [target.aarch64-apple-darwin.dependencies] cookie = { version = "0.18.1", default-features = false, features = ["percent-encode"] } errno = { version = "0.3.14" } +getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.4", default-features = false, features = ["std"] } hyper-rustls = { version = "0.27.7", features = ["http2"] } hyper-util = { version = "0.1.20", features = ["full"] } +miniz_oxide = { version = "0.8.9", default-features = false, features = ["simd", "with-alloc"] } mio = { version = "1.2.0", features = ["net", "os-ext"] } nix = { version = "0.31.2", default-features = false, features = ["mman"] } object = { version = "0.37.3", default-features = false, features = ["read", "std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } -rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", default-features = false, features = ["stdio"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } [target.aarch64-apple-darwin.build-dependencies] cookie = { version = "0.18.1", default-features = false, features = ["percent-encode"] } errno = { version = "0.3.14" } +getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.4", default-features = false, features = ["std"] } hyper-rustls = { version = "0.27.7", features = ["http2"] } hyper-util = { version = "0.1.20", features = ["full"] } +miniz_oxide = { version = "0.8.9", default-features = false, features = ["simd", "with-alloc"] } mio = { version = "1.2.0", features = ["net", "os-ext"] } nix = { version = "0.31.2", default-features = false, features = ["mman"] } object = { version = "0.37.3", default-features = false, features = ["read", "std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } -rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", default-features = false, features = ["stdio"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } [target.x86_64-unknown-illumos.dependencies] chacha20 = { version = "0.10.0", default-features = false, features = ["legacy", "rng", "zeroize"] } -cipher = { version = "0.5.2", default-features = false, features = ["block-padding", "rand_core", "stream-wrapper"] } +cipher-d8f496e17d97b5cb = { package = "cipher", version = "0.5.2", default-features = false, features = ["block-padding", "rand_core", "stream-wrapper"] } cookie = { version = "0.18.1", default-features = false, features = ["percent-encode"] } dof-468e82937335b1c9 = { package = "dof", version = "0.3.0", default-features = false, features = ["des"] } dof-9fbad63c4bcf4a8f = { package = "dof", version = "0.4.0", default-features = false, features = ["des"] } errno = { version = "0.3.14" } +getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.4", default-features = false, features = ["std"] } hyper-rustls = { version = "0.27.7", features = ["http2"] } hyper-util = { version = "0.1.20", features = ["full"] } inout = { version = "0.2.2", default-features = false, features = ["block-padding"] } +miniz_oxide = { version = "0.8.9", default-features = false, features = ["simd", "with-alloc"] } mio = { version = "1.2.0", features = ["net", "os-ext"] } nix = { version = "0.31.2", default-features = false, features = ["mman"] } object = { version = "0.37.3", default-features = false, features = ["read", "std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } -rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", default-features = false, features = ["stdio"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } winnow = { version = "1.0.3" } [target.x86_64-unknown-illumos.build-dependencies] chacha20 = { version = "0.10.0", default-features = false, features = ["legacy", "rng", "zeroize"] } -cipher = { version = "0.5.2", default-features = false, features = ["block-padding", "rand_core", "stream-wrapper"] } +cipher-d8f496e17d97b5cb = { package = "cipher", version = "0.5.2", default-features = false, features = ["block-padding", "rand_core", "stream-wrapper"] } cookie = { version = "0.18.1", default-features = false, features = ["percent-encode"] } dof-468e82937335b1c9 = { package = "dof", version = "0.3.0", default-features = false, features = ["des"] } dof-9fbad63c4bcf4a8f = { package = "dof", version = "0.4.0", default-features = false, features = ["des"] } errno = { version = "0.3.14" } +getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.4", default-features = false, features = ["std"] } hyper-rustls = { version = "0.27.7", features = ["http2"] } hyper-util = { version = "0.1.20", features = ["full"] } inout = { version = "0.2.2", default-features = false, features = ["block-padding"] } +miniz_oxide = { version = "0.8.9", default-features = false, features = ["simd", "with-alloc"] } mio = { version = "1.2.0", features = ["net", "os-ext"] } nix = { version = "0.31.2", default-features = false, features = ["mman"] } object = { version = "0.37.3", default-features = false, features = ["read", "std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } -rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } +rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.4", default-features = false, features = ["stdio"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } winnow = { version = "1.0.3" }