From deaaf836153323e6ce25f38acf3aba2521c140c5 Mon Sep 17 00:00:00 2001 From: Alex Gartrell Date: Sun, 7 Jun 2026 10:29:03 -0700 Subject: [PATCH 1/2] Implement open-source remote viewing with token auth Fill in the open-source remote-mode stub so `below record --port N` serves store frames over a small HTTP+CBOR protocol and remote clients (live, replay, dump, snapshot) can read them. Authentication is a shared bearer token sourced from a Kubernetes Secret via BELOW_REMOTE_TOKEN or BELOW_REMOTE_TOKEN_FILE; the server checks it with a constant-time comparison. Transport security (TLS) is intentionally delegated to the platform (service mesh / ingress), matching typical k8s deployment. - Client (store crate): ureq-based RemoteStore, signature unchanged so no call sites are affected. - Server (below crate): blocking tiny_http server started from the record path under #[cfg(not(fbcode_build))]; the fbcode serving path is untouched. Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.lock | 1336 +++++++++++++++---- below/Cargo.toml | 2 + below/src/main.rs | 13 + below/src/remote_server.rs | 195 +++ below/store/Cargo.toml | 1 + below/store/src/open_source/remote_store.rs | 100 +- 6 files changed, 1394 insertions(+), 253 deletions(-) create mode 100644 below/src/remote_server.rs diff --git a/Cargo.lock b/Cargo.lock index 62fca28e..43733b14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "ahash" version = "0.8.12" @@ -50,9 +35,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.20" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -65,15 +50,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -100,9 +85,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "ascii" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "async-trait" @@ -112,7 +103,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -121,21 +112,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] - [[package]] name = "base64" version = "0.22.1" @@ -163,18 +139,21 @@ dependencies = [ "libbpf-rs", "libc", "maplit", - "nix", + "nix 0.30.1", "once_cell", "plain", "portpicker", "regex", "scopeguard", + "serde", + "serde_cbor", "serde_json", "signal-hook", "slog", "slog-term", "tar", "tempfile", + "tiny_http", "tokio", ] @@ -184,9 +163,9 @@ version = "0.11.0" dependencies = [ "below-common", "libc", - "nix", + "nix 0.30.1", "openat", - "rand", + "rand 0.10.1", "rand_distr", "serde", "slog", @@ -203,8 +182,10 @@ dependencies = [ "humantime", "once_cell", "regex", + "serde", "slog", "slog-term", + "tempfile", "walkdir", ] @@ -215,7 +196,9 @@ dependencies = [ "anyhow", "below-btrfs", "cgroupfs", + "fb_procfs", "serde", + "tempfile", "toml", ] @@ -244,7 +227,7 @@ dependencies = [ name = "below-ethtool" version = "0.11.0" dependencies = [ - "nix", + "nix 0.30.1", "serde", "thiserror", ] @@ -272,6 +255,7 @@ dependencies = [ "cgroupfs", "enum-iterator", "fb_procfs", + "futures 0.3.32", "hostname", "os_info", "regex", @@ -299,13 +283,20 @@ dependencies = [ "bitflags", "bytes", "humantime", + "itertools", + "lazy_static", "maplit", "memmap2", - "nix", + "nix 0.30.1", + "paste", "serde", "serde_cbor", "slog", + "slog-term", "static_assertions", + "tempfile", + "ureq", + "zstd", "zstd-safe", ] @@ -316,7 +307,7 @@ dependencies = [ "netlink-packet-core", "netlink-packet-route", "netlink-sys", - "nix", + "nix 0.30.1", "serde", "thiserror", ] @@ -331,7 +322,7 @@ dependencies = [ "below-render", "below-store", "chrono", - "crossterm", + "crossterm 0.29.0", "cursive", "enum-iterator", "fb_procfs", @@ -341,6 +332,7 @@ dependencies = [ "once_cell", "serde", "slog", + "tempfile", "toml", ] @@ -355,11 +347,20 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.4" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" dependencies = [ - "serde", + "serde_core", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", ] [[package]] @@ -370,9 +371,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] @@ -447,17 +448,30 @@ name = "cgroupfs" version = "0.11.0" dependencies = [ "below-common", - "nix", + "nix 0.30.1", "openat", + "paste", "serde", + "tempfile", "thiserror", ] +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core 0.10.1", +] + [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -467,11 +481,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + [[package]] name = "clap" -version = "4.5.48" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -479,9 +499,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -489,35 +509,35 @@ dependencies = [ "strsim", "terminal_size", "unicase", - "unicode-width 0.2.1", + "unicode-width 0.2.2", ] [[package]] name = "clap_complete" -version = "4.5.58" +version = "4.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75bf0b32ad2e152de789bb635ea4d3078f6b838ad7974143e99b99f45a04af4a" +checksum = "e0a7a9bfdb35811f9e59832f0f05975114d2251b415fb534108e6f34060fd772" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "colorchoice" @@ -541,15 +561,23 @@ dependencies = [ [[package]] name = "console" -version = "0.15.11" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", - "unicode-width 0.2.1", - "windows-sys 0.59.0", + "unicode-width 0.2.2", + "windows-sys 0.61.1", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", ] [[package]] @@ -558,6 +586,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -600,10 +637,28 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags", "crossterm_winapi", - "futures-core", "mio", "parking_lot", "rustix 0.38.44", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags", + "crossterm_winapi", + "derive_more", + "document-features", + "futures-core", + "mio", + "parking_lot", + "rustix 1.1.4", "serde", "signal-hook", "signal-hook-mio", @@ -628,7 +683,7 @@ dependencies = [ "ahash", "cfg-if", "crossbeam-channel", - "crossterm", + "crossterm 0.28.1", "cursive_core", "lazy_static", "libc", @@ -690,7 +745,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -701,7 +756,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -713,6 +768,58 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "either" version = "1.15.0" @@ -742,7 +849,7 @@ checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -762,7 +869,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -783,7 +890,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -824,11 +931,13 @@ dependencies = [ "below-common", "lazy_static", "libc", - "nix", + "nix 0.30.1", "openat", "parking_lot", "serde", "slog", + "slog-term", + "tempfile", "thiserror", "threadpool", ] @@ -857,11 +966,116 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures 0.1.31", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "libc", + "memchr", + "pin-project-lite", + "slab", +] [[package]] name = "getrandom" @@ -882,15 +1096,23 @@ checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", "wasi 0.14.7+wasi-0.2.4", ] [[package]] -name = "gimli" -version = "0.32.3" +name = "getrandom" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasip2", + "wasip3", +] [[package]] name = "half" @@ -898,6 +1120,15 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + [[package]] name = "hashbrown" version = "0.16.0" @@ -918,15 +1149,21 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hostname" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" dependencies = [ + "cfg-if", "libc", - "match_cfg", - "winapi", + "windows-link", ] +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.3.0" @@ -957,12 +1194,121 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.11.4" @@ -970,37 +1316,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.0", + "serde", + "serde_core", ] [[package]] name = "indicatif" -version = "0.17.11" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" dependencies = [ "console", "futures-core", - "number_prefix", "portable-atomic", "rayon", "tokio", "unicode-segmentation", - "unicode-width 0.2.1", + "unicode-width 0.2.2", + "unit-prefix", "web-time", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "is-terminal" version = "0.4.16" @@ -1059,17 +1396,22 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libbpf-cargo" -version = "0.26.0-beta.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf0f7ff3792d69d11e5a1a8a96343fa604adcc1737b5946e7ae2186d512b907" +checksum = "67e63283df9689af55b85f91e99dc8b213ae89fc3e1d9bb29ab7f75eb8a8a783" dependencies = [ "anyhow", "cargo_metadata", "clap", "libbpf-rs", - "libbpf-sys", "memmap2", "serde", "serde_json", @@ -1080,9 +1422,9 @@ dependencies = [ [[package]] name = "libbpf-rs" -version = "0.26.0-beta.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bacc8ccbc646056b3d290f68db80fff5842188e7ba3b479f91800de59e7c58ba" +checksum = "fd8e8db46a2d5967b5b3dba7aba32a875dc38f222395040607045635ec1f9b63" dependencies = [ "bitflags", "libbpf-sys", @@ -1092,20 +1434,20 @@ dependencies = [ [[package]] name = "libbpf-sys" -version = "1.5.1+v1.5.1" +version = "1.7.0+v1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912fae30b08bcbdb861d4b85bd09c05352c0ac9d7b93765ced5ca23709e7e590" +checksum = "a109478760b2900aa2a6f2087e9d0de1d9c535b1758602af2845d5d2ccfaed7c" dependencies = [ "cc", - "nix", + "nix 0.31.3", "pkg-config", ] [[package]] name = "libc" -version = "0.2.176" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -1132,9 +1474,21 @@ 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 = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" @@ -1158,12 +1512,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "memchr" version = "2.7.6" @@ -1172,9 +1520,9 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.9.8" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" dependencies = [ "libc", ] @@ -1188,25 +1536,16 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.1", ] [[package]] @@ -1232,9 +1571,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" +checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" dependencies = [ "bytes", "libc", @@ -1254,6 +1593,18 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nu-ansi-term" version = "0.50.1" @@ -1351,25 +1702,169 @@ dependencies = [ ] [[package]] -name = "number_prefix" -version = "0.4.0" +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] [[package]] -name = "object" -version = "0.37.3" +name = "objc2-user-notifications" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" dependencies = [ - "memchr", + "objc2", + "objc2-foundation", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -1388,14 +1883,18 @@ dependencies = [ [[package]] name = "os_info" -version = "3.12.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" dependencies = [ + "android_system_properties", "log", - "plist", + "nix 0.31.3", + "objc2", + "objc2-foundation", + "objc2-ui-kit", "serde", - "windows-sys 0.52.0", + "windows-sys 0.61.1", ] [[package]] @@ -1427,6 +1926,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1445,24 +1950,11 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "plist" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" -dependencies = [ - "base64", - "indexmap", - "quick-xml", - "serde", - "time", -] - [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portpicker" @@ -1470,7 +1962,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be97d76faf1bfab666e1375477b23fde79eccf0276e9b63b92a39d676a889ba9" dependencies = [ - "rand", + "rand 0.8.5", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", ] [[package]] @@ -1489,28 +1990,29 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.101" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "unicode-ident", + "proc-macro2", + "syn 2.0.117", ] [[package]] -name = "quick-xml" -version = "0.38.3" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ - "memchr", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -1521,6 +2023,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 = "rand" version = "0.8.5" @@ -1529,7 +2037,18 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -1539,7 +2058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1551,14 +2070,20 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_distr" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +checksum = "4d431c2703ccf129de4d45253c03f49ebb22b97d6ad79ee3ecfc7e3f4862c1d8" dependencies = [ "num-traits", - "rand", + "rand 0.10.1", ] [[package]] @@ -1592,9 +2117,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.3" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1604,9 +2129,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1623,17 +2148,24 @@ checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" name = "resctrlfs" version = "0.11.0" dependencies = [ - "nix", + "below-common", + "maplit", + "nix 0.30.1", "openat", + "paste", "serde", + "tempfile", "thiserror", ] [[package]] -name = "rustc-demangle" -version = "0.1.26" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" @@ -1650,14 +2182,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.11.0", + "linux-raw-sys 0.12.1", "windows-sys 0.61.1", ] @@ -1735,7 +2267,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1753,9 +2285,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -1787,9 +2319,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -1813,11 +2345,14 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slog" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" +checksum = "9b3b8565691b22d2bdfc066426ed48f837fc0c5f2c8cad8d9718f7f99d6995c1" dependencies = [ + "anyhow", "erased-serde", + "rustversion", + "serde_core", ] [[package]] @@ -1842,14 +2377,20 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.1", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1875,20 +2416,31 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tar" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", @@ -1897,14 +2449,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.1.2", + "rustix 1.1.4", "windows-sys 0.61.1", ] @@ -1923,28 +2475,28 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 1.1.2", + "rustix 1.1.4", "windows-sys 0.60.2", ] [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1998,43 +2550,62 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" -version = "1.47.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2", "tokio-macros", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.1", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "toml" -version = "0.9.7" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", @@ -2042,32 +2613,32 @@ dependencies = [ "toml_datetime", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.13", ] [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.3", ] [[package]] name = "toml_writer" -version = "1.0.3" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tracing" @@ -2103,9 +2674,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" @@ -2127,9 +2698,51 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unit-prefix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64", + "log", + "once_cell", + "url", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" @@ -2184,7 +2797,16 @@ version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.46.0", +] + +[[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 0.51.0", ] [[package]] @@ -2210,7 +2832,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -2232,7 +2854,7 @@ checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2246,6 +2868,40 @@ 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", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-time" version = "1.1.0" @@ -2308,7 +2964,7 @@ checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2319,7 +2975,7 @@ checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2517,12 +3173,112 @@ version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +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", + "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", + "indexmap", + "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", + "indexmap", + "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", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + [[package]] name = "xattr" version = "1.6.1" @@ -2530,7 +3286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.1.2", + "rustix 1.1.4", ] [[package]] @@ -2539,6 +3295,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.27" @@ -2556,7 +3335,70 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", ] [[package]] diff --git a/below/Cargo.toml b/below/Cargo.toml index bb11088f..0e592afa 100644 --- a/below/Cargo.toml +++ b/below/Cargo.toml @@ -39,12 +39,14 @@ procfs = { package = "fb_procfs", version = "0.11.0", path = "procfs" } regex = "1.12.3" scopeguard = "1.2.0" serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_cbor = "0.11.2" serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "raw_value", "unbounded_depth"] } signal-hook = "0.3.18" slog = { version = "2.8.2", features = ["max_level_trace", "nested-values"] } slog-term = "2.9.2" store = { package = "below-store", version = "0.11.0", path = "store" } tar = "0.4.46" +tiny_http = "0.12" tempfile = "3.27.0" tokio = { version = "1.52.3", features = ["full", "test-util", "tracing"] } view = { package = "below-view", version = "0.11.0", path = "view" } diff --git a/below/src/main.rs b/below/src/main.rs index 6585f6f7..53dcc622 100644 --- a/below/src/main.rs +++ b/below/src/main.rs @@ -65,6 +65,8 @@ use tempfile::TempDir; use tokio::runtime::Builder as TB; mod exitstat; +#[cfg(not(fbcode_build))] +mod remote_server; #[cfg(test)] mod test; @@ -721,6 +723,17 @@ where below_config.store_dir.clone(), err_sender, ); + // Open-source remote-viewing server. In the fbcode build this is handled by + // `facebook::init` above instead. + #[cfg(not(fbcode_build))] + if let Service::On(port) = _service { + if let Err(e) = + remote_server::start(logger.clone(), below_config.store_dir.clone(), port) + { + error!(logger, "Failed to start remote viewing server: {:#}", e); + return 1; + } + } let res = command(init, below_config, logger.clone(), err_receiver); match res { diff --git a/below/src/remote_server.rs b/below/src/remote_server.rs new file mode 100644 index 00000000..2cfeb281 --- /dev/null +++ b/below/src/remote_server.rs @@ -0,0 +1,195 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Open-source remote-viewing server (the counterpart to `store::RemoteStore`). +//! +//! A small blocking HTTP server: GET `/get_frame?timestamp=&direction=` +//! returns a CBOR-encoded `Option<(u64, DataFrame)>` read from the local store. +//! +//! Authentication is a shared bearer token sourced from a Kubernetes Secret via +//! the `BELOW_REMOTE_TOKEN` env var (or the file named by `BELOW_REMOTE_TOKEN_FILE`). +//! When a token is configured, requests must present a matching +//! `Authorization: Bearer ` header. TLS is intentionally delegated to the +//! platform (service mesh / ingress). + +use std::path::PathBuf; +use std::sync::Arc; +use std::thread; +use std::time::Duration; +use std::time::SystemTime; +use std::time::UNIX_EPOCH; + +use anyhow::Context; +use anyhow::Result; +use slog::error; +use slog::info; +use slog::warn; +use store::DataFrame; +use store::Direction; +use store::read_next_sample; +use tiny_http::Response; +use tiny_http::Server; + +/// Default port. Keep in sync with `DEFAULT_REMOTE_PORT` in the remote client +/// (`store/src/open_source/remote_store.rs`). +const DEFAULT_REMOTE_PORT: u16 = 1969; +/// Number of worker threads serving requests. +const NUM_WORKERS: usize = 4; + +struct ServerState { + logger: slog::Logger, + store_dir: PathBuf, + /// Required bearer token. `None` means unauthenticated access is allowed. + token: Option, +} + +/// Start the remote-viewing server. Binding happens synchronously so a failure +/// (e.g. address in use) is reported to the caller; request handling runs on +/// detached worker threads for the lifetime of the process. +pub fn start(logger: slog::Logger, store_dir: PathBuf, port: Option) -> Result<()> { + let port = port.unwrap_or(DEFAULT_REMOTE_PORT); + let addr = format!("0.0.0.0:{port}"); + let server = Server::http(&addr) + .map_err(|e| anyhow::anyhow!("Failed to bind remote viewing server on {addr}: {e}"))?; + + let token = read_token()?; + if token.is_some() { + info!(logger, "Remote viewing server listening on {addr} (token auth)"); + } else { + warn!( + logger, + "Remote viewing server listening on {addr} WITHOUT authentication. Set \ + BELOW_REMOTE_TOKEN (or BELOW_REMOTE_TOKEN_FILE) to require a token." + ); + } + + let state = Arc::new(ServerState { + logger, + store_dir, + token, + }); + let server = Arc::new(server); + + for _ in 0..NUM_WORKERS { + let server = server.clone(); + let state = state.clone(); + thread::Builder::new() + .name("below_remote_server".to_owned()) + .spawn(move || { + for request in server.incoming_requests() { + handle(&state, request); + } + }) + .context("Failed to spawn remote server worker")?; + } + + Ok(()) +} + +fn handle(state: &ServerState, request: tiny_http::Request) { + let response = match build_response(state, &request) { + Ok(bytes) => Response::from_data(bytes), + Err((code, msg)) => { + if code != 404 { + warn!(state.logger, "Remote request rejected ({code}): {msg}"); + } + Response::from_string(msg).with_status_code(code) + } + }; + if let Err(e) = request.respond(response) { + error!(state.logger, "Failed to send remote response: {e}"); + } +} + +/// Returns the CBOR response body, or an `(http_status, message)` error. +fn build_response( + state: &ServerState, + request: &tiny_http::Request, +) -> std::result::Result, (u16, String)> { + // Authenticate first so unauthorized callers learn nothing about the URL. + if let Some(expected) = &state.token { + let presented = request + .headers() + .iter() + .find(|h| h.field.equiv("Authorization")) + .map(|h| h.value.as_str()) + .and_then(|v| v.strip_prefix("Bearer ")) + .unwrap_or(""); + if !constant_time_eq(presented.as_bytes(), expected.as_bytes()) { + return Err((401, "unauthorized".to_owned())); + } + } + + let url = request.url(); + let query = url + .strip_prefix("/get_frame?") + .ok_or((404, "not found".to_owned()))?; + + let mut timestamp: Option = None; + let mut direction: Option = None; + for pair in query.split('&') { + match pair.split_once('=') { + Some(("timestamp", v)) => { + timestamp = Some(v.parse().map_err(|_| (400, "bad timestamp".to_owned()))?); + } + Some(("direction", "forward")) => direction = Some(Direction::Forward), + Some(("direction", "reverse")) => direction = Some(Direction::Reverse), + Some(("direction", _)) => return Err((400, "bad direction".to_owned())), + _ => {} + } + } + let timestamp = timestamp.ok_or((400, "missing timestamp".to_owned()))?; + let direction = direction.ok_or((400, "missing direction".to_owned()))?; + + let target = UNIX_EPOCH + Duration::from_secs(timestamp); + let found = read_next_sample(&state.store_dir, target, direction, state.logger.clone()) + .map_err(|e| (500, format!("store read failed: {e:#}")))?; + + let frame: Option<(u64, DataFrame)> = found.map(|(st, df)| { + let ts = st + .duration_since(UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0); + (ts, df) + }); + + serde_cbor::to_vec(&frame).map_err(|e| (500, format!("serialize failed: {e}"))) +} + +/// Constant-time byte-slice comparison to avoid leaking the token via timing. +fn constant_time_eq(a: &[u8], b: &[u8]) -> bool { + if a.len() != b.len() { + return false; + } + let mut diff = 0u8; + for (x, y) in a.iter().zip(b.iter()) { + diff |= x ^ y; + } + diff == 0 +} + +/// Read the bearer token from `BELOW_REMOTE_TOKEN` or `BELOW_REMOTE_TOKEN_FILE`. +fn read_token() -> Result> { + if let Ok(token) = std::env::var("BELOW_REMOTE_TOKEN") { + if !token.is_empty() { + return Ok(Some(token)); + } + } + if let Ok(path) = std::env::var("BELOW_REMOTE_TOKEN_FILE") { + let token = std::fs::read_to_string(&path) + .with_context(|| format!("Failed to read BELOW_REMOTE_TOKEN_FILE {path:?}"))?; + return Ok(Some(token.trim().to_owned())); + } + Ok(None) +} diff --git a/below/store/Cargo.toml b/below/store/Cargo.toml index de3a58ce..7bf87965 100644 --- a/below/store/Cargo.toml +++ b/below/store/Cargo.toml @@ -23,6 +23,7 @@ serde = { version = "1.0.219", features = ["derive", "rc"] } serde_cbor = "0.11.2" slog = { version = "2.8.2", features = ["max_level_trace", "nested-values"] } static_assertions = "1.1.0" +ureq = { version = "2.12", default-features = false } zstd-safe = { version = "7.2.4", features = ["std"] } [dev-dependencies] diff --git a/below/store/src/open_source/remote_store.rs b/below/store/src/open_source/remote_store.rs index d040225b..f7096105 100644 --- a/below/store/src/open_source/remote_store.rs +++ b/below/store/src/open_source/remote_store.rs @@ -12,26 +12,114 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Open-source client for below's remote-viewing mode. +//! +//! Speaks a tiny HTTP+CBOR protocol to a remote `below record` server: a GET to +//! `/get_frame?timestamp=&direction=` returns a +//! CBOR-encoded `Option<(u64, DataFrame)>`. +//! +//! Authentication is a shared bearer token, intended to be delivered as a +//! Kubernetes Secret. The token is read from the `BELOW_REMOTE_TOKEN` +//! environment variable, or from the file named by `BELOW_REMOTE_TOKEN_FILE`. +//! When neither is set the client sends no credentials (for use inside a +//! trusted network or behind a TLS-terminating proxy/service mesh). +//! +//! Transport security (TLS) is intentionally delegated to the platform (service +//! mesh or ingress), matching how this is typically deployed in Kubernetes. + +use std::io::Read; +use std::time::Duration; use std::time::SystemTime; +use std::time::UNIX_EPOCH; +use anyhow::Context; use anyhow::Result; +use anyhow::anyhow; use anyhow::bail; use crate::DataFrame; use crate::Direction; -pub struct RemoteStore {} +/// Default port used when none is supplied. +pub const DEFAULT_REMOTE_PORT: u16 = 1969; + +const REQUEST_TIMEOUT: Duration = Duration::from_secs(30); + +pub struct RemoteStore { + base_url: String, + token: Option, + agent: ureq::Agent, +} impl RemoteStore { - pub fn new(_host: String, _port: Option) -> Result { - bail!("Remote client not supported") + pub fn new(host: String, port: Option) -> Result { + let port = port.unwrap_or(DEFAULT_REMOTE_PORT); + let agent = ureq::AgentBuilder::new() + .timeout(REQUEST_TIMEOUT) + .build(); + Ok(RemoteStore { + base_url: format!("http://{host}:{port}"), + token: read_token()?, + agent, + }) } pub fn get_frame( &mut self, - _timestamp: u64, - _direction: Direction, + timestamp: u64, + direction: Direction, ) -> Result> { - bail!("Remote client not supported") + let direction = match direction { + Direction::Forward => "forward", + Direction::Reverse => "reverse", + }; + let url = format!( + "{}/get_frame?timestamp={timestamp}&direction={direction}", + self.base_url + ); + + let mut req = self.agent.get(&url); + if let Some(token) = &self.token { + req = req.set("Authorization", &format!("Bearer {token}")); + } + + let resp = match req.call() { + Ok(resp) => resp, + Err(ureq::Error::Status(401, _)) => { + bail!( + "Remote server rejected credentials (401). Set BELOW_REMOTE_TOKEN (or \ + BELOW_REMOTE_TOKEN_FILE) to match the server's token." + ) + } + Err(ureq::Error::Status(code, resp)) => { + let body = resp.into_string().unwrap_or_default(); + bail!("Remote server returned HTTP {code}: {body}") + } + Err(e) => return Err(anyhow!(e).context("Remote frame request failed")), + }; + + let mut buf = Vec::new(); + resp.into_reader() + .read_to_end(&mut buf) + .context("Failed to read remote response body")?; + let frame: Option<(u64, DataFrame)> = + serde_cbor::from_slice(&buf).context("Failed to deserialize remote frame")?; + + Ok(frame.map(|(ts, df)| (UNIX_EPOCH + Duration::from_secs(ts), df))) + } +} + +/// Read the bearer token from `BELOW_REMOTE_TOKEN` or `BELOW_REMOTE_TOKEN_FILE`. +fn read_token() -> Result> { + if let Ok(token) = std::env::var("BELOW_REMOTE_TOKEN") { + if !token.is_empty() { + return Ok(Some(token)); + } + } + if let Ok(path) = std::env::var("BELOW_REMOTE_TOKEN_FILE") { + let token = std::fs::read_to_string(&path) + .with_context(|| format!("Failed to read BELOW_REMOTE_TOKEN_FILE {path:?}"))?; + return Ok(Some(token.trim().to_owned())); } + Ok(None) } From dd50f4e6839594f4479d6c2336c66aed0799e4bb Mon Sep 17 00:00:00 2001 From: Alex Gartrell Date: Sun, 7 Jun 2026 10:42:09 -0700 Subject: [PATCH 2/2] Add optional TLS to remote viewing Serve remote viewing over HTTPS when `record` is given a cert and key: below record --port N --tls-cert server.pem --tls-key server.key Uses tiny_http's rustls-backed TLS. The client opts into TLS via environment (consistent with the env-based token), so no per-command flags are needed: BELOW_REMOTE_TLS=1 connect over HTTPS, verify against system roots BELOW_REMOTE_CA_FILE= verify against a specific CA bundle BELOW_REMOTE_TLS_INSECURE=1 skip server verification (testing only) The OSS server is now started from the record path (so it can receive the cert/key) rather than from run(); the fbcode serving path is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.lock | 190 +++++++++++++++++++- below/Cargo.toml | 2 +- below/src/main.rs | 44 +++-- below/src/remote_server.rs | 64 +++++-- below/store/Cargo.toml | 4 +- below/store/src/open_source/remote_store.rs | 169 ++++++++++++++++- 6 files changed, 435 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43733b14..9e377938 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,6 +112,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.22.1" @@ -289,6 +295,8 @@ dependencies = [ "memmap2", "nix 0.30.1", "paste", + "rustls 0.23.40", + "rustls-pemfile 2.2.0", "serde", "serde_cbor", "slog", @@ -2158,6 +2166,35 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -2193,6 +2230,71 @@ dependencies = [ "windows-sys 0.61.1", ] +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "log", + "once_cell", + "ring 0.17.14", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring 0.17.14", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2220,6 +2322,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.14", + "untrusted 0.9.0", +] + [[package]] name = "semver" version = "1.0.27" @@ -2385,6 +2497,12 @@ dependencies = [ "windows-sys 0.61.1", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -2403,6 +2521,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -2454,7 +2578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", "windows-sys 0.61.1", @@ -2560,6 +2684,9 @@ dependencies = [ "chunked_transfer", "httpdate", "log", + "rustls 0.20.9", + "rustls-pemfile 0.2.1", + "zeroize", ] [[package]] @@ -2714,16 +2841,31 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "ureq" version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ - "base64", + "base64 0.22.1", "log", "once_cell", + "rustls 0.23.40", + "rustls-pki-types", "url", + "webpki-roots 0.26.11", ] [[package]] @@ -2902,6 +3044,16 @@ dependencies = [ "semver", ] +[[package]] +name = "web-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "web-time" version = "1.1.0" @@ -2912,6 +3064,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.14", + "untrusted 0.9.0", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3359,6 +3539,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zerotrie" version = "0.2.4" diff --git a/below/Cargo.toml b/below/Cargo.toml index 0e592afa..bee25aae 100644 --- a/below/Cargo.toml +++ b/below/Cargo.toml @@ -46,7 +46,7 @@ slog = { version = "2.8.2", features = ["max_level_trace", "nested-values"] } slog-term = "2.9.2" store = { package = "below-store", version = "0.11.0", path = "store" } tar = "0.4.46" -tiny_http = "0.12" +tiny_http = { version = "0.12", features = ["ssl-rustls"] } tempfile = "3.27.0" tokio = { version = "1.52.3", features = ["full", "test-util", "tracing"] } view = { package = "below-view", version = "0.11.0", path = "view" } diff --git a/below/src/main.rs b/below/src/main.rs index 53dcc622..5f58abd2 100644 --- a/below/src/main.rs +++ b/below/src/main.rs @@ -201,6 +201,13 @@ enum Command { /// Override default port for remote viewing server #[clap(long)] port: Option, + /// PEM certificate (chain) file to serve remote viewing over TLS. + /// Requires --tls-key. + #[clap(long, value_name = "PEM", requires = "tls_key")] + tls_cert: Option, + /// PEM private key file for --tls-cert. + #[clap(long, value_name = "PEM", requires = "tls_cert")] + tls_key: Option, /// Threshold for hold long data collection takes to trigger warnings. #[clap(long, default_value = "500")] skew_detection_threshold_ms: u64, @@ -723,17 +730,6 @@ where below_config.store_dir.clone(), err_sender, ); - // Open-source remote-viewing server. In the fbcode build this is handled by - // `facebook::init` above instead. - #[cfg(not(fbcode_build))] - if let Service::On(port) = _service { - if let Err(e) = - remote_server::start(logger.clone(), below_config.store_dir.clone(), port) - { - error!(logger, "Failed to start remote viewing server: {:#}", e); - return 1; - } - } let res = command(init, below_config, logger.clone(), err_receiver); match res { @@ -826,6 +822,8 @@ fn real_main(init: init::InitToken) { store_size_limit, collect_io_stat, port, + tls_cert, + tls_key, skew_detection_threshold_ms, disable_disk_stat, disable_exitstats, @@ -833,11 +831,14 @@ fn real_main(init: init::InitToken) { writer_buffer_size, } => { logutil::set_current_log_target(logutil::TargetLog::Term); + let port = *port; + let tls_cert = tls_cert.clone(); + let tls_key = tls_key.clone(); run( init, debug, below_config, - Service::On(*port), + Service::On(port), |init, below_config, logger, errs| { record( init, @@ -854,6 +855,9 @@ fn real_main(init: init::InitToken) { *disable_exitstats, compress_opts, *writer_buffer_size, + port, + tls_cert, + tls_key, ) }, ) @@ -1194,9 +1198,25 @@ fn record( disable_exitstats: bool, compress_opts: &CompressOpts, writer_buffer_size: usize, + port: Option, + tls_cert: Option, + tls_key: Option, ) -> Result<()> { debug!(logger, "Starting up!"); + // Start the open-source remote-viewing server. In the fbcode build, serving + // is handled by `facebook::init` in `run()` instead. + #[cfg(not(fbcode_build))] + remote_server::start( + logger.clone(), + below_config.store_dir.clone(), + port, + tls_cert, + tls_key, + )?; + #[cfg(fbcode_build)] + let _ = (port, tls_cert, tls_key); + if !disable_exitstats { bump_memlock_rlimit()?; } diff --git a/below/src/remote_server.rs b/below/src/remote_server.rs index 2cfeb281..a88ae1cb 100644 --- a/below/src/remote_server.rs +++ b/below/src/remote_server.rs @@ -27,11 +27,11 @@ use std::path::PathBuf; use std::sync::Arc; use std::thread; use std::time::Duration; -use std::time::SystemTime; use std::time::UNIX_EPOCH; use anyhow::Context; use anyhow::Result; +use anyhow::bail; use slog::error; use slog::info; use slog::warn; @@ -55,23 +55,61 @@ struct ServerState { } /// Start the remote-viewing server. Binding happens synchronously so a failure -/// (e.g. address in use) is reported to the caller; request handling runs on -/// detached worker threads for the lifetime of the process. -pub fn start(logger: slog::Logger, store_dir: PathBuf, port: Option) -> Result<()> { +/// (e.g. address in use, bad certificate) is reported to the caller; request +/// handling runs on detached worker threads for the lifetime of the process. +/// +/// When `tls_cert` and `tls_key` (PEM files) are both supplied, the server +/// listens over HTTPS; otherwise plain HTTP. +pub fn start( + logger: slog::Logger, + store_dir: PathBuf, + port: Option, + tls_cert: Option, + tls_key: Option, +) -> Result<()> { let port = port.unwrap_or(DEFAULT_REMOTE_PORT); let addr = format!("0.0.0.0:{port}"); - let server = Server::http(&addr) - .map_err(|e| anyhow::anyhow!("Failed to bind remote viewing server on {addr}: {e}"))?; + + let (server, scheme) = match (tls_cert, tls_key) { + (Some(cert), Some(key)) => { + let certificate = std::fs::read(&cert) + .with_context(|| format!("Failed to read --tls-cert {cert:?}"))?; + let private_key = std::fs::read(&key) + .with_context(|| format!("Failed to read --tls-key {key:?}"))?; + let config = tiny_http::SslConfig { + certificate, + private_key, + }; + let server = Server::https(&addr, config) + .map_err(|e| anyhow::anyhow!("Failed to bind HTTPS server on {addr}: {e}"))?; + (server, "https") + } + (None, None) => { + let server = Server::http(&addr) + .map_err(|e| anyhow::anyhow!("Failed to bind HTTP server on {addr}: {e}"))?; + (server, "http") + } + _ => bail!("both --tls-cert and --tls-key must be provided to enable TLS"), + }; let token = read_token()?; - if token.is_some() { - info!(logger, "Remote viewing server listening on {addr} (token auth)"); - } else { - warn!( + match (scheme, token.is_some()) { + ("https", true) => info!(logger, "Remote viewing server listening on https://{addr} (TLS, token auth)"), + ("https", false) => warn!( + logger, + "Remote viewing server listening on https://{addr} (TLS) WITHOUT authentication. \ + Set BELOW_REMOTE_TOKEN (or BELOW_REMOTE_TOKEN_FILE) to require a token." + ), + (_, true) => warn!( + logger, + "Remote viewing server listening on http://{addr} (token auth, NO transport \ + encryption). Provide --tls-cert/--tls-key or terminate TLS in a proxy/mesh." + ), + (_, false) => warn!( logger, - "Remote viewing server listening on {addr} WITHOUT authentication. Set \ - BELOW_REMOTE_TOKEN (or BELOW_REMOTE_TOKEN_FILE) to require a token." - ); + "Remote viewing server listening on http://{addr} WITHOUT TLS or authentication. \ + Set --tls-cert/--tls-key and BELOW_REMOTE_TOKEN to secure it." + ), } let state = Arc::new(ServerState { diff --git a/below/store/Cargo.toml b/below/store/Cargo.toml index 7bf87965..19e53ab4 100644 --- a/below/store/Cargo.toml +++ b/below/store/Cargo.toml @@ -21,9 +21,11 @@ model = { package = "below-model", version = "0.11.0", path = "../model" } nix = { version = "0.30.1", features = ["dir", "event", "hostname", "inotify", "ioctl", "mman", "mount", "net", "poll", "ptrace", "reboot", "resource", "sched", "signal", "term", "time", "user", "zerocopy"] } serde = { version = "1.0.219", features = ["derive", "rc"] } serde_cbor = "0.11.2" +rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } +rustls-pemfile = "2" slog = { version = "2.8.2", features = ["max_level_trace", "nested-values"] } static_assertions = "1.1.0" -ureq = { version = "2.12", default-features = false } +ureq = { version = "2.12", default-features = false, features = ["tls"] } zstd-safe = { version = "7.2.4", features = ["std"] } [dev-dependencies] diff --git a/below/store/src/open_source/remote_store.rs b/below/store/src/open_source/remote_store.rs index f7096105..85eaebf5 100644 --- a/below/store/src/open_source/remote_store.rs +++ b/below/store/src/open_source/remote_store.rs @@ -21,13 +21,22 @@ //! Authentication is a shared bearer token, intended to be delivered as a //! Kubernetes Secret. The token is read from the `BELOW_REMOTE_TOKEN` //! environment variable, or from the file named by `BELOW_REMOTE_TOKEN_FILE`. -//! When neither is set the client sends no credentials (for use inside a -//! trusted network or behind a TLS-terminating proxy/service mesh). //! -//! Transport security (TLS) is intentionally delegated to the platform (service -//! mesh or ingress), matching how this is typically deployed in Kubernetes. +//! TLS is opt-in via environment variables (so no per-command flags are +//! required): +//! * `BELOW_REMOTE_TLS=1` -- connect over HTTPS, verifying the server +//! against the system trust store. +//! * `BELOW_REMOTE_CA_FILE=`-- connect over HTTPS, verifying against this +//! CA bundle (for self-signed / internal CAs). +//! * `BELOW_REMOTE_TLS_INSECURE=1` -- connect over HTTPS without verifying the +//! server certificate (testing only). +use std::fs::File; +use std::io::BufReader; use std::io::Read; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; use std::time::Duration; use std::time::SystemTime; use std::time::UNIX_EPOCH; @@ -36,6 +45,17 @@ use anyhow::Context; use anyhow::Result; use anyhow::anyhow; use anyhow::bail; +use rustls::ClientConfig; +use rustls::DigitallySignedStruct; +use rustls::RootCertStore; +use rustls::SignatureScheme; +use rustls::client::danger::HandshakeSignatureValid; +use rustls::client::danger::ServerCertVerified; +use rustls::client::danger::ServerCertVerifier; +use rustls::crypto::CryptoProvider; +use rustls::pki_types::CertificateDer; +use rustls::pki_types::ServerName; +use rustls::pki_types::UnixTime; use crate::DataFrame; use crate::Direction; @@ -45,6 +65,16 @@ pub const DEFAULT_REMOTE_PORT: u16 = 1969; const REQUEST_TIMEOUT: Duration = Duration::from_secs(30); +/// How the client should verify the server when TLS is enabled. +enum TlsMode { + /// Verify against the system trust store. + Default, + /// Verify against a specific CA bundle (PEM). + Ca(PathBuf), + /// Do not verify the server certificate (testing only). + Insecure, +} + pub struct RemoteStore { base_url: String, token: Option, @@ -54,13 +84,26 @@ pub struct RemoteStore { impl RemoteStore { pub fn new(host: String, port: Option) -> Result { let port = port.unwrap_or(DEFAULT_REMOTE_PORT); - let agent = ureq::AgentBuilder::new() - .timeout(REQUEST_TIMEOUT) - .build(); + let tls = read_tls_mode(); + let scheme = if tls.is_some() { "https" } else { "http" }; + + let mut builder = ureq::AgentBuilder::new().timeout(REQUEST_TIMEOUT); + match &tls { + Some(TlsMode::Ca(path)) => { + builder = builder.tls_config(Arc::new(build_ca_config(path)?)); + } + Some(TlsMode::Insecure) => { + builder = builder.tls_config(Arc::new(build_insecure_config())); + } + // Default TLS uses ureq's built-in rustls config (system roots); + // plain HTTP needs no TLS config. + Some(TlsMode::Default) | None => {} + } + Ok(RemoteStore { - base_url: format!("http://{host}:{port}"), + base_url: format!("{scheme}://{host}:{port}"), token: read_token()?, - agent, + agent: builder.build(), }) } @@ -109,6 +152,61 @@ impl RemoteStore { } } +/// Determine the TLS mode from the environment, or `None` for plain HTTP. +fn read_tls_mode() -> Option { + if env_truthy("BELOW_REMOTE_TLS_INSECURE") { + return Some(TlsMode::Insecure); + } + if let Some(ca) = std::env::var_os("BELOW_REMOTE_CA_FILE") { + return Some(TlsMode::Ca(PathBuf::from(ca))); + } + if env_truthy("BELOW_REMOTE_TLS") { + return Some(TlsMode::Default); + } + None +} + +fn env_truthy(key: &str) -> bool { + match std::env::var(key) { + Ok(v) => matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on"), + Err(_) => false, + } +} + +/// Build a TLS client config that verifies the server against a CA bundle. +fn build_ca_config(ca_path: &Path) -> Result { + let provider = Arc::new(rustls::crypto::ring::default_provider()); + let mut roots = RootCertStore::empty(); + let mut reader = BufReader::new( + File::open(ca_path).with_context(|| format!("Failed to open CA file {ca_path:?}"))?, + ); + for cert in rustls_pemfile::certs(&mut reader) { + let cert = cert.with_context(|| format!("Failed to parse CA file {ca_path:?}"))?; + roots + .add(cert) + .context("Failed to add CA certificate to root store")?; + } + if roots.is_empty() { + bail!("No certificates found in CA file {ca_path:?}"); + } + Ok(ClientConfig::builder_with_provider(provider) + .with_safe_default_protocol_versions() + .context("Failed to initialize TLS protocol versions")? + .with_root_certificates(roots) + .with_no_client_auth()) +} + +/// Build a TLS client config that skips server certificate verification. +fn build_insecure_config() -> ClientConfig { + let provider = Arc::new(rustls::crypto::ring::default_provider()); + ClientConfig::builder_with_provider(provider.clone()) + .with_safe_default_protocol_versions() + .expect("ring provider supports default protocol versions") + .dangerous() + .with_custom_certificate_verifier(Arc::new(InsecureVerifier { provider })) + .with_no_client_auth() +} + /// Read the bearer token from `BELOW_REMOTE_TOKEN` or `BELOW_REMOTE_TOKEN_FILE`. fn read_token() -> Result> { if let Ok(token) = std::env::var("BELOW_REMOTE_TOKEN") { @@ -123,3 +221,56 @@ fn read_token() -> Result> { } Ok(None) } + +/// Verifier that accepts any server certificate. Insecure; testing only. +#[derive(Debug)] +struct InsecureVerifier { + provider: Arc, +} + +impl ServerCertVerifier for InsecureVerifier { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> std::result::Result { + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> std::result::Result { + rustls::crypto::verify_tls12_signature( + message, + cert, + dss, + &self.provider.signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> std::result::Result { + rustls::crypto::verify_tls13_signature( + message, + cert, + dss, + &self.provider.signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + self.provider + .signature_verification_algorithms + .supported_schemes() + } +}