diff --git a/Cargo.Bazel.lock b/Cargo.Bazel.lock index d7c1631d9..ab628e588 100644 --- a/Cargo.Bazel.lock +++ b/Cargo.Bazel.lock @@ -1,5 +1,5 @@ { - "checksum": "a6b16863ad5d0a1a72b20d57a7b103696e736e78f865de8cee087e4876a07970", + "checksum": "af852655d9eacd497a80742a4a6e9066b95c0a236ee1b77f438a7a8d7fef1237", "crates": { "addr2line 0.21.0": { "name": "addr2line", @@ -9089,6 +9089,10 @@ "id": "tokio-util 0.7.10", "target": "tokio_util" }, + { + "id": "tonic 0.10.2", + "target": "tonic" + }, { "id": "tracing 0.1.40", "target": "tracing" @@ -15084,6 +15088,7 @@ ], "crate_features": { "common": [ + "default", "logging", "tls12" ], @@ -15326,6 +15331,7 @@ "default", "gzip", "prost", + "tls", "transport" ], "selects": {} @@ -15384,10 +15390,22 @@ "id": "prost 0.12.3", "target": "prost" }, + { + "id": "rustls 0.21.10", + "target": "rustls" + }, + { + "id": "rustls-pemfile 1.0.4", + "target": "rustls_pemfile" + }, { "id": "tokio 1.35.1", "target": "tokio" }, + { + "id": "tokio-rustls 0.24.1", + "target": "tokio_rustls" + }, { "id": "tokio-stream 0.1.14", "target": "tokio_stream" diff --git a/Cargo.lock b/Cargo.lock index 5ec15448e..0759d7db4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" dependencies = [ "anstyle", "anstyle-parse", @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener", "event-listener-strategy", @@ -151,7 +151,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -162,7 +162,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -639,9 +639,9 @@ checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64-simd" @@ -741,9 +741,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.12" +version = "4.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" +checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" dependencies = [ "clap_builder", "clap_derive", @@ -751,9 +751,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.12" +version = "4.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" dependencies = [ "anstream", "anstyle", @@ -770,7 +770,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -861,9 +861,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -888,22 +888,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-bigint" @@ -1046,9 +1042,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.2" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "218a870470cce1469024e9fb66b901aa983929d81304a1cdb299f28118e550d5" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ "concurrent-queue", "parking", @@ -1198,7 +1194,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -1243,9 +1239,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -1271,9 +1267,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" dependencies = [ "bytes", "fnv", @@ -1499,9 +1495,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libredox" @@ -1547,9 +1543,9 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea9b256699eda7b0387ffbc776dd625e28bde3918446381781245b7a50349d8" +checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" dependencies = [ "twox-hash", ] @@ -1830,6 +1826,7 @@ dependencies = [ "sha2", "tokio", "tokio-util", + "tonic", "tracing", ] @@ -2016,9 +2013,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" dependencies = [ "memchr", "thiserror", @@ -2027,9 +2024,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" dependencies = [ "pest", "pest_generator", @@ -2037,22 +2034,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" dependencies = [ "once_cell", "pest", @@ -2086,7 +2083,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2140,7 +2137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2169,9 +2166,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.74" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -2196,7 +2193,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2226,7 +2223,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.46", + "syn 2.0.48", "tempfile", "which", ] @@ -2241,7 +2238,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2413,9 +2410,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.4.1", "errno", @@ -2438,14 +2435,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.0", + "rustls-webpki 0.102.1", "subtle", "zeroize", ] @@ -2499,9 +2496,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.0" +version = "0.102.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89" +checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" dependencies = [ "ring", "rustls-pki-types", @@ -2590,29 +2587,29 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" [[package]] name = "serde" -version = "1.0.194" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.194" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] name = "serde_json" -version = "1.0.110" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -2728,9 +2725,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" [[package]] name = "socket2" @@ -2789,9 +2786,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.46" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -2834,7 +2831,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2928,7 +2925,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2947,7 +2944,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.22.1", + "rustls 0.22.2", "rustls-pki-types", "tokio", ] @@ -2998,7 +2995,10 @@ dependencies = [ "percent-encoding", "pin-project", "prost", + "rustls 0.21.10", + "rustls-pemfile 1.0.4", "tokio", + "tokio-rustls 0.24.1", "tokio-stream", "tower", "tower-layer", @@ -3016,7 +3016,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -3071,7 +3071,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -3447,7 +3447,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e18cf0ed5..2103e255d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ scopeguard = "1.2.0" serde_json5 = "0.1.0" tokio = { version = "1.35.1", features = ["rt-multi-thread", "signal"] } tokio-rustls = "0.25.0" -tonic = { version = "0.10.2", features = ["gzip"] } +tonic = { version = "0.10.2", features = ["gzip", "tls"] } tower = "0.4.13" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/nativelink-config/src/cas_server.rs b/nativelink-config/src/cas_server.rs index a771f61c3..5134079f5 100644 --- a/nativelink-config/src/cas_server.rs +++ b/nativelink-config/src/cas_server.rs @@ -18,9 +18,10 @@ use serde::Deserialize; use crate::schedulers::SchedulerConfig; use crate::serde_utils::{ - convert_numeric_with_shellexpand, convert_optinoal_numeric_with_shellexpand, convert_string_with_shellexpand, + convert_numeric_with_shellexpand, convert_optional_numeric_with_shellexpand, + convert_optional_string_with_shellexpand, convert_string_with_shellexpand, }; -use crate::stores::{ConfigDigestHashFunction, StoreConfig, StoreRefName}; +use crate::stores::{ClientTlsConfig, ConfigDigestHashFunction, StoreConfig, StoreRefName}; /// Name of the scheduler. This type will be used when referencing a /// scheduler in the `CasConfig::schedulers`'s map key. @@ -77,6 +78,11 @@ pub struct AcStoreConfig { /// This store name referenced here may be reused multiple times. #[serde(deserialize_with = "convert_string_with_shellexpand")] pub ac_store: StoreRefName, + + /// Whether the Action Cache store may be written to, this if set to false + /// it is only possible to read from the Action Cache. + #[serde(default)] + pub read_only: bool, } #[derive(Deserialize, Debug)] @@ -221,6 +227,16 @@ pub struct TlsConfig { /// Path to the private key file. #[serde(deserialize_with = "convert_string_with_shellexpand")] pub key_file: String, + + /// Path to the certificate authority for mTLS, if client authentication is + /// required for this endpoint. + #[serde(default, deserialize_with = "convert_optional_string_with_shellexpand")] + pub client_ca_file: Option, + + /// Path to the certificate revocation list for mTLS, if client + /// authentication is required for this endpoint. + #[serde(default, deserialize_with = "convert_optional_string_with_shellexpand")] + pub client_crl_file: Option, } /// Advanced Http configurations. These are generally should not be set. @@ -233,38 +249,38 @@ pub struct TlsConfig { pub struct HttpServerConfig { /// Interval to send keep-alive pings via HTTP2. /// Note: This is in seconds. - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub http2_keep_alive_interval: Option, - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub experimental_http2_max_pending_accept_reset_streams: Option, - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub experimental_http2_initial_stream_window_size: Option, - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub experimental_http2_initial_connection_window_size: Option, #[serde(default)] pub experimental_http2_adaptive_window: Option, - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub experimental_http2_max_frame_size: Option, - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub experimental_http2_max_concurrent_streams: Option, /// Note: This is in seconds. - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub experimental_http2_keep_alive_timeout: Option, - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub experimental_http2_max_send_buf_size: Option, #[serde(default)] pub experimental_http2_enable_connect_protocol: Option, - #[serde(default, deserialize_with = "convert_optinoal_numeric_with_shellexpand")] + #[serde(default, deserialize_with = "convert_optional_numeric_with_shellexpand")] pub experimental_http2_max_header_list_size: Option, } @@ -337,6 +353,9 @@ pub struct EndpointConfig { /// Timeout in seconds that a request should take. /// Default: 5 (seconds) pub timeout: Option, + + /// The TLS configuration to use to connect to the endpoint. + pub tls_config: Option, } #[allow(non_camel_case_types)] diff --git a/nativelink-config/src/schedulers.rs b/nativelink-config/src/schedulers.rs index 3dc954a0b..8aae44adc 100644 --- a/nativelink-config/src/schedulers.rs +++ b/nativelink-config/src/schedulers.rs @@ -17,7 +17,7 @@ use std::collections::HashMap; use serde::Deserialize; use crate::serde_utils::{convert_numeric_with_shellexpand, convert_string_with_shellexpand}; -use crate::stores::{Retry, StoreRefName}; +use crate::stores::{ClientTlsConfig, Retry, StoreRefName}; #[allow(non_camel_case_types)] #[derive(Deserialize, Debug)] @@ -129,9 +129,13 @@ pub struct GrpcScheduler { /// The upstream scheduler to forward requests to. #[serde(deserialize_with = "convert_string_with_shellexpand")] pub endpoint: String, + /// Retry configuration to use when a network request fails. #[serde(default)] pub retry: Retry, + + /// The TLS configuration to use to connect to the endpoint. + pub tls_config: Option, } #[derive(Deserialize, Debug)] diff --git a/nativelink-config/src/serde_utils.rs b/nativelink-config/src/serde_utils.rs index efa750d93..937825e36 100644 --- a/nativelink-config/src/serde_utils.rs +++ b/nativelink-config/src/serde_utils.rs @@ -58,7 +58,7 @@ where } /// Same as convert_numeric_with_shellexpand, but supports Option. -pub fn convert_optinoal_numeric_with_shellexpand<'de, D, T, E>(deserializer: D) -> Result, D::Error> +pub fn convert_optional_numeric_with_shellexpand<'de, D, T, E>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, E: fmt::Display, @@ -106,3 +106,17 @@ pub fn convert_string_with_shellexpand<'de, D: Deserializer<'de>>(deserializer: let value = String::deserialize(deserializer)?; Ok((*(shellexpand::env(&value).map_err(de::Error::custom)?)).to_string()) } + +/// Same as convert_string_with_shellexpand, but supports Option. +pub fn convert_optional_string_with_shellexpand<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + let value = Option::::deserialize(deserializer)?; + if let Some(value) = value { + Ok(Some( + (*(shellexpand::env(&value).map_err(de::Error::custom)?)).to_string(), + )) + } else { + Ok(None) + } +} diff --git a/nativelink-config/src/stores.rs b/nativelink-config/src/stores.rs index 6b5e5a662..9ee9a33d7 100644 --- a/nativelink-config/src/stores.rs +++ b/nativelink-config/src/stores.rs @@ -14,7 +14,9 @@ use serde::{Deserialize, Serialize}; -use crate::serde_utils::{convert_numeric_with_shellexpand, convert_string_with_shellexpand}; +use crate::serde_utils::{ + convert_numeric_with_shellexpand, convert_optional_string_with_shellexpand, convert_string_with_shellexpand, +}; /// Name of the store. This type will be used when referencing a store /// in the `CasConfig::stores`'s map key. @@ -476,6 +478,21 @@ pub enum StoreType { ac, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ClientTlsConfig { + /// Path to the certificate authority to use to validate the remote. + #[serde(deserialize_with = "convert_string_with_shellexpand")] + pub ca_file: String, + + /// Path to the certificate file for client authentication. + #[serde(deserialize_with = "convert_optional_string_with_shellexpand")] + pub cert_file: Option, + + /// Path to the private key file for client authentication. + #[serde(deserialize_with = "convert_optional_string_with_shellexpand")] + pub key_file: Option, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct GrpcStore { /// Instance name for GRPC calls. Proxy calls will have the instance_name changed to this. @@ -492,6 +509,9 @@ pub struct GrpcStore { /// Retry configuration to use when a network request fails. #[serde(default)] pub retry: Retry, + + /// The TLS configuration to use to connect to the endpoints. + pub tls_config: Option, } /// Retry configuration. This configuration is exponential and each iteration diff --git a/nativelink-scheduler/Cargo.toml b/nativelink-scheduler/Cargo.toml index e29ef1873..ed781c9c5 100644 --- a/nativelink-scheduler/Cargo.toml +++ b/nativelink-scheduler/Cargo.toml @@ -25,7 +25,7 @@ rand = "0.8.5" scopeguard = "1.2.0" tokio = { version = "1.35.1", features = ["sync", "rt", "parking_lot"] } tokio-stream = { version = "0.1.14", features = ["sync"] } -tonic = { version = "0.10.2", features = ["gzip"] } +tonic = { version = "0.10.2", features = ["gzip", "tls"] } tracing = "0.1.40" [dev-dependencies] diff --git a/nativelink-scheduler/src/grpc_scheduler.rs b/nativelink-scheduler/src/grpc_scheduler.rs index fb856bc94..50e4ae656 100644 --- a/nativelink-scheduler/src/grpc_scheduler.rs +++ b/nativelink-scheduler/src/grpc_scheduler.rs @@ -29,6 +29,7 @@ use nativelink_proto::build::bazel::remote::execution::v2::{ use nativelink_proto::google::longrunning::Operation; use nativelink_util::action_messages::{ActionInfo, ActionInfoHashKey, ActionState, DEFAULT_EXECUTION_PRIORITY}; use nativelink_util::retry::{ExponentialBackoff, Retrier, RetryResult}; +use nativelink_util::tls_utils; use parking_lot::Mutex; use rand::rngs::OsRng; use rand::Rng; @@ -70,14 +71,13 @@ impl GrpcScheduler { config: &nativelink_config::schedulers::GrpcScheduler, jitter_fn: Box Duration + Send + Sync>, ) -> Result { - let endpoint = transport::Channel::balance_list(std::iter::once( - transport::Endpoint::new(config.endpoint.clone()) - .err_tip(|| format!("Could not parse {} in GrpcScheduler", config.endpoint))?, - )); - + let channel = transport::Channel::balance_list(std::iter::once(tls_utils::endpoint_from( + &config.endpoint, + tls_utils::load_client_config(&config.tls_config)?, + )?)); Ok(Self { - capabilities_client: CapabilitiesClient::new(endpoint.clone()), - execution_client: ExecutionClient::new(endpoint), + capabilities_client: CapabilitiesClient::new(channel.clone()), + execution_client: ExecutionClient::new(channel), platform_property_managers: Mutex::new(HashMap::new()), jitter_fn, retry: config.retry.clone(), diff --git a/nativelink-service/Cargo.toml b/nativelink-service/Cargo.toml index 75ee83d99..c5c27c469 100644 --- a/nativelink-service/Cargo.toml +++ b/nativelink-service/Cargo.toml @@ -19,7 +19,7 @@ prost = "0.12.3" rand = "0.8.5" tokio = { version = "1.35.1", features = ["sync", "rt"] } tokio-stream = { version = "0.1.14", features = ["sync"] } -tonic = { version = "0.10.2", features = ["gzip"] } +tonic = { version = "0.10.2", features = ["gzip", "tls"] } tracing = "0.1.40" uuid = { version = "1.6.1", features = ["v4"] } diff --git a/nativelink-service/src/ac_server.rs b/nativelink-service/src/ac_server.rs index 5a15dfee6..bbf06dbd8 100644 --- a/nativelink-service/src/ac_server.rs +++ b/nativelink-service/src/ac_server.rs @@ -20,7 +20,7 @@ use std::time::Instant; use bytes::BytesMut; use nativelink_config::cas_server::{AcStoreConfig, InstanceName}; -use nativelink_error::{make_input_err, Code, Error, ResultExt}; +use nativelink_error::{make_err, make_input_err, Code, Error, ResultExt}; use nativelink_proto::build::bazel::remote::execution::v2::action_cache_server::{ ActionCache, ActionCacheServer as Server, }; @@ -36,8 +36,14 @@ use prost::Message; use tonic::{Request, Response, Status}; use tracing::{error, info}; +#[derive(Clone)] +pub struct AcStoreInfo { + store: Arc, + read_only: bool, +} + pub struct AcServer { - stores: HashMap>, + stores: HashMap, } impl AcServer { @@ -47,7 +53,13 @@ impl AcServer { let store = store_manager .get_store(&ac_cfg.ac_store) .ok_or_else(|| make_input_err!("'ac_store': '{}' does not exist", ac_cfg.ac_store))?; - stores.insert(instance_name.to_string(), store); + stores.insert( + instance_name.to_string(), + AcStoreInfo { + store, + read_only: ac_cfg.read_only, + }, + ); } Ok(AcServer { stores: stores.clone() }) } @@ -63,7 +75,7 @@ impl AcServer { let get_action_request = grpc_request.into_inner(); let instance_name = &get_action_request.instance_name; - let store = self + let store_info = self .stores .get(instance_name) .err_tip(|| format!("'instance_name' not configured for '{}'", instance_name))?; @@ -76,14 +88,14 @@ impl AcServer { .try_into()?; // If we are a GrpcStore we shortcut here, as this is a special store. - let any_store = store.clone().inner_store(Some(digest)).as_any(); + let any_store = store_info.store.clone().inner_store(Some(digest)).as_any(); let maybe_grpc_store = any_store.downcast_ref::>(); if let Some(grpc_store) = maybe_grpc_store { return grpc_store.get_action_result(Request::new(get_action_request)).await; } Ok(Response::new( - get_and_decode_digest::(Pin::new(store.as_ref()), &digest).await?, + get_and_decode_digest::(Pin::new(store_info.store.as_ref()), &digest).await?, )) } @@ -94,11 +106,18 @@ impl AcServer { let update_action_request = grpc_request.into_inner(); let instance_name = &update_action_request.instance_name; - let store = self + let store_info = self .stores .get(instance_name) .err_tip(|| format!("'instance_name' not configured for '{}'", instance_name))?; + if store_info.read_only { + return Err(make_err!( + Code::PermissionDenied, + "The store '{instance_name}' is read only on this endpoint", + )); + } + let digest: DigestInfo = update_action_request .action_digest .clone() @@ -106,7 +125,8 @@ impl AcServer { .try_into()?; // If we are a GrpcStore we shortcut here, as this is a special store. - let any_store = store.clone().inner_store(Some(digest)).as_any(); + let any_store = store_info.store.clone().inner_store(Some(digest)).as_any(); + let maybe_grpc_store = any_store.downcast_ref::>(); if let Some(grpc_store) = maybe_grpc_store { return grpc_store @@ -123,7 +143,7 @@ impl AcServer { .encode(&mut store_data) .err_tip(|| "Provided ActionResult could not be serialized")?; - Pin::new(store.as_ref()) + Pin::new(store_info.store.as_ref()) .update_oneshot(digest, store_data.freeze()) .await .err_tip(|| "Failed to update in action cache")?; diff --git a/nativelink-service/tests/ac_server_test.rs b/nativelink-service/tests/ac_server_test.rs index 2ebc9ea24..04a10234e 100644 --- a/nativelink-service/tests/ac_server_test.rs +++ b/nativelink-service/tests/ac_server_test.rs @@ -75,6 +75,7 @@ fn make_ac_server(store_manager: &StoreManager) -> Result { &hashmap! { "foo_instance_name".to_string() => nativelink_config::cas_server::AcStoreConfig{ ac_store: "main_ac".to_string(), + read_only: false, } }, store_manager, diff --git a/nativelink-store/Cargo.toml b/nativelink-store/Cargo.toml index 5751c0269..a1c67bdb1 100644 --- a/nativelink-store/Cargo.toml +++ b/nativelink-store/Cargo.toml @@ -33,7 +33,7 @@ shellexpand = "3.1.0" tokio = { version = "1.35.1" } tokio-stream = { version = "0.1.14", features = ["fs"] } tokio-util = { version = "0.7.10" } -tonic = { version = "0.10.2", features = ["gzip"] } +tonic = { version = "0.10.2", features = ["gzip", "tls"] } tracing = "0.1.40" uuid = { version = "1.6.1", features = ["v4"] } diff --git a/nativelink-store/src/grpc_store.rs b/nativelink-store/src/grpc_store.rs index 7ea6c47ec..107909364 100644 --- a/nativelink-store/src/grpc_store.rs +++ b/nativelink-store/src/grpc_store.rs @@ -37,6 +37,7 @@ use nativelink_util::buf_channel::{DropCloserReadHalf, DropCloserWriteHalf}; use nativelink_util::common::DigestInfo; use nativelink_util::retry::{ExponentialBackoff, Retrier, RetryResult}; use nativelink_util::store_trait::{Store, UploadSizeInfo}; +use nativelink_util::tls_utils; use nativelink_util::write_request_stream_wrapper::WriteRequestStreamWrapper; use parking_lot::Mutex; use prost::Message; @@ -86,19 +87,20 @@ impl GrpcStore { jitter_fn: Box Duration + Send + Sync>, ) -> Result { error_if!(config.endpoints.is_empty(), "Expected at least 1 endpoint in GrpcStore"); + let tls_config = tls_utils::load_client_config(&config.tls_config)?; let mut endpoints = Vec::with_capacity(config.endpoints.len()); for endpoint in &config.endpoints { // TODO(allada) This should be moved to be done in utils/serde_utils.rs like the others. // We currently don't have a way to handle those helpers with vectors. let endpoint = shellexpand::env(&endpoint) - .map_err(|e| make_input_err!("{}", e)) - .err_tip(|| "Could expand endpoint in GrpcStore")? + .map_err(|e| make_input_err!("{e}")) + .err_tip(|| "Could not expand endpoint in GrpcStore")? .to_string(); - endpoints.push( - transport::Endpoint::new(endpoint.clone()) - .err_tip(|| format!("Could not connect to {} in GrpcStore", endpoint))?, - ); + let endpoint = tls_utils::endpoint_from(&endpoint, tls_config.clone()) + .map_err(|e| make_input_err!("Invalid URI for GrpcStore endpoint : {e:?}"))?; + + endpoints.push(endpoint); } let conn = transport::Channel::balance_list(endpoints.into_iter()); diff --git a/nativelink-util/BUILD.bazel b/nativelink-util/BUILD.bazel index d30035748..9d37be3ab 100644 --- a/nativelink-util/BUILD.bazel +++ b/nativelink-util/BUILD.bazel @@ -22,6 +22,7 @@ rust_library( "src/resource_info.rs", "src/retry.rs", "src/store_trait.rs", + "src/tls_utils.rs", "src/write_counter.rs", "src/write_request_stream_wrapper.rs", ], @@ -49,6 +50,7 @@ rust_library( "@crate_index//:sha2", "@crate_index//:tokio", "@crate_index//:tokio-util", + "@crate_index//:tonic", "@crate_index//:tracing", ], ) diff --git a/nativelink-util/Cargo.toml b/nativelink-util/Cargo.toml index 37057c2e5..3bcf20bd7 100644 --- a/nativelink-util/Cargo.toml +++ b/nativelink-util/Cargo.toml @@ -25,6 +25,7 @@ serde = { version = "1.0.193", features = ["derive"] } sha2 = "0.10.8" tokio = { version = "1.35.1", features = [ "sync", "fs", "rt", "time", "io-util", "macros" ] } tokio-util = { version = "0.7.10" } +tonic = { version = "0.10.2", features = ["tls"] } tracing = "0.1.40" [dev-dependencies] diff --git a/nativelink-util/src/lib.rs b/nativelink-util/src/lib.rs index beaae5d04..3dad898e8 100644 --- a/nativelink-util/src/lib.rs +++ b/nativelink-util/src/lib.rs @@ -24,5 +24,6 @@ pub mod platform_properties; pub mod resource_info; pub mod retry; pub mod store_trait; +pub mod tls_utils; pub mod write_counter; pub mod write_request_stream_wrapper; diff --git a/nativelink-util/src/tls_utils.rs b/nativelink-util/src/tls_utils.rs new file mode 100644 index 000000000..cf9b74ab0 --- /dev/null +++ b/nativelink-util/src/tls_utils.rs @@ -0,0 +1,66 @@ +// Copyright 2024 The Native Link Authors. All rights reserved. +// +// 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. + +use tonic::transport::Uri; + +use nativelink_config::stores::ClientTlsConfig; +use nativelink_error::{make_err, make_input_err, Code, Error}; + +pub fn load_client_config(config: &Option) -> Result, Error> { + let Some(config) = config else { + return Ok(None); + }; + + let read_config = tonic::transport::ClientTlsConfig::new().ca_certificate(tonic::transport::Certificate::from_pem( + std::fs::read_to_string(&config.ca_file)?, + )); + let config = if let Some(client_certificate) = &config.cert_file { + let Some(client_key) = &config.key_file else { + return Err(make_err!(Code::Internal, "Client certificate specified, but no key")); + }; + read_config.identity(tonic::transport::Identity::from_pem( + std::fs::read_to_string(client_certificate)?, + std::fs::read_to_string(client_key)?, + )) + } else { + if config.key_file.is_some() { + return Err(make_err!(Code::Internal, "Client key specified, but no certificate")); + } + read_config + }; + + Ok(Some(config)) +} + +pub fn endpoint_from( + endpoint: &str, + tls_config: Option, +) -> Result { + let endpoint = Uri::try_from(endpoint) + .map_err(|e| make_err!(Code::Internal, "Unable to parse endpoint {endpoint}: {e:?}"))?; + + let endpoint_transport = if let Some(tls_config) = &tls_config { + let Some(authority) = endpoint.authority() else { + return Err(make_input_err!("Unable to determine authority of endpont: {endpoint}")); + }; + let tls_config = tls_config.clone().domain_name(authority.host()); + tonic::transport::Endpoint::from(endpoint) + .tls_config(tls_config) + .map_err(|e| make_input_err!("Setting mTLS configuration: {e:?}"))? + } else { + tonic::transport::Endpoint::from(endpoint) + }; + + Ok(endpoint_transport) +} diff --git a/nativelink-worker/Cargo.toml b/nativelink-worker/Cargo.toml index af0e103db..19b577e01 100644 --- a/nativelink-worker/Cargo.toml +++ b/nativelink-worker/Cargo.toml @@ -30,7 +30,7 @@ serde_json5 = "0.1.0" shlex = "1.2.0" tokio = { version = "1.35.1", features = ["sync", "rt", "process"] } tokio-stream = { version = "0.1.14", features = ["fs"] } -tonic = { version = "0.10.2", features = ["gzip"] } +tonic = { version = "0.10.2", features = ["gzip", "tls"] } tracing = "0.1.40" uuid = { version = "1.6.1", features = ["v4"] } diff --git a/nativelink-worker/src/local_worker.rs b/nativelink-worker/src/local_worker.rs index f63d49d27..e08f28b55 100644 --- a/nativelink-worker/src/local_worker.rs +++ b/nativelink-worker/src/local_worker.rs @@ -37,11 +37,11 @@ use nativelink_util::metrics_utils::{ AsyncCounterWrapper, Collector, CollectorState, CounterWithTime, MetricsComponent, Registry, }; use nativelink_util::store_trait::Store; +use nativelink_util::tls_utils; use tokio::process; use tokio::sync::mpsc; use tokio::time::sleep; use tokio_stream::wrappers::UnboundedReceiverStream; -use tonic::transport::Channel as TonicChannel; use tonic::Streaming; use tracing::{error, warn}; @@ -381,21 +381,17 @@ pub async fn new_local_worker( let timeout = config.worker_api_endpoint.timeout.unwrap_or(DEFAULT_ENDPOINT_TIMEOUT_S); let timeout_duration = Duration::from_secs_f32(timeout); - let uri = config - .worker_api_endpoint - .uri - .clone() - .try_into() - .map_err(|e| make_input_err!("Invalid URI for worker endpoint : {:?}", e))?; - let endpoint = TonicChannel::builder(uri) + let tls_config = tls_utils::load_client_config(&config.worker_api_endpoint.tls_config)?; + let endpoint = tls_utils::endpoint_from(&config.worker_api_endpoint.uri, tls_config) + .map_err(|e| make_input_err!("Invalid URI for worker endpoint : {e:?}"))? .connect_timeout(timeout_duration) .timeout(timeout_duration); + let transport = endpoint.connect().await.map_err(|e| { make_err!( Code::Internal, - "Could not connect to endpoint {}: {:?}", - config.worker_api_endpoint.uri, - e + "Could not connect to endpoint {}: {e:?}", + config.worker_api_endpoint.uri ) })?; Ok(WorkerApiClient::new(transport).into()) diff --git a/src/bin/nativelink.rs b/src/bin/nativelink.rs index 29de99661..9c01d330b 100644 --- a/src/bin/nativelink.rs +++ b/src/bin/nativelink.rs @@ -1,4 +1,4 @@ -// Copyright 2022 The Native Link Authors. All rights reserved. +// Copyright 2024 The Native Link Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -46,13 +46,15 @@ use nativelink_util::metrics_utils::{ }; use nativelink_worker::local_worker::new_local_worker; use parking_lot::Mutex; -use rustls_pemfile::{certs as extract_certs, pkcs8_private_keys}; +use rustls_pemfile::{certs as extract_certs, crls as extract_crls}; use scopeguard::guard; use tokio::net::TcpListener; #[cfg(target_family = "unix")] use tokio::signal::unix::{signal, SignalKind}; use tokio::task::spawn_blocking; -use tokio_rustls::rustls::ServerConfig as TlsServerConfig; +use tokio_rustls::rustls::pki_types::{CertificateDer, CertificateRevocationListDer}; +use tokio_rustls::rustls::server::WebPkiClientVerifier; +use tokio_rustls::rustls::{RootCertStore, ServerConfig as TlsServerConfig}; use tokio_rustls::TlsAcceptor; use tonic::codec::CompressionEncoding; use tonic::transport::Server as TonicServer; @@ -443,37 +445,72 @@ async fn inner_main(cfg: CasConfig, server_start_timestamp: u64) -> Result<(), B // Configure our TLS acceptor if we have TLS configured. let maybe_tls_acceptor = http_config.tls.map_or(Ok(None), |tls_config| { - let mut cert_reader = std::io::BufReader::new( - std::fs::File::open(&tls_config.cert_file) - .err_tip(|| format!("Could not open cert file {}", tls_config.cert_file))?, - ); - let mut certs = vec![]; - for cert in extract_certs(&mut cert_reader) { - certs.push(cert.err_tip(|| format!("Could not extract certs from file {}", tls_config.cert_file))?); + fn read_cert(cert_file: &str) -> Result>, Error> { + let mut cert_reader = std::io::BufReader::new( + std::fs::File::open(cert_file).err_tip(|| format!("Could not open cert file {cert_file}"))?, + ); + let certs = extract_certs(&mut cert_reader) + .map(|certificate| certificate.map(CertificateDer::from)) + .collect::>, _>>() + .err_tip(|| format!("Could not extract certs from file {cert_file}"))?; + Ok(certs) } + let certs = read_cert(&tls_config.cert_file)?; let mut key_reader = std::io::BufReader::new( std::fs::File::open(&tls_config.key_file) .err_tip(|| format!("Could not open key file {}", tls_config.key_file))?, ); - let key = { - let keys = pkcs8_private_keys(&mut key_reader).collect::>(); - if keys.len() != 1 { - return Err(Box::new(make_err!( - Code::InvalidArgument, - "Expected 1 key in file {}, found {} keys", - tls_config.key_file, - keys.len() - ))); + let key = match rustls_pemfile::read_one(&mut key_reader) + .err_tip(|| format!("Could not extract key(s) from file {}", tls_config.key_file))? + { + Some(rustls_pemfile::Item::Pkcs8Key(key)) => key.into(), + Some(rustls_pemfile::Item::Sec1Key(key)) => key.into(), + Some(rustls_pemfile::Item::Pkcs1Key(key)) => key.into(), + _ => { + return Err(make_err!( + Code::Internal, + "No keys found in file {}", + tls_config.key_file + )) + } + }; + if let Ok(Some(_)) = rustls_pemfile::read_one(&mut key_reader) { + return Err(make_err!( + Code::InvalidArgument, + "Expected 1 key in file {}", + tls_config.key_file + )); + } + let verifier = if let Some(client_ca_file) = &tls_config.client_ca_file { + let mut client_auth_roots = RootCertStore::empty(); + for cert in read_cert(client_ca_file)?.into_iter() { + client_auth_roots + .add(cert) + .map_err(|e| make_err!(Code::Internal, "Could not read client CA: {e:?}"))?; } - keys.into_iter() - .next() - .unwrap() - .err_tip(|| format!("Could not extract key(s) from file {}", tls_config.key_file))? + let crls = if let Some(client_crl_file) = &tls_config.client_crl_file { + let mut crl_reader = std::io::BufReader::new( + std::fs::File::open(client_crl_file) + .err_tip(|| format!("Could not open CRL file {client_crl_file}"))?, + ); + extract_crls(&mut crl_reader) + .map(|crl| crl.map(CertificateRevocationListDer::from)) + .collect::>() + .err_tip(|| format!("Could not extract CRLs from file {client_crl_file}"))? + } else { + Vec::new() + }; + WebPkiClientVerifier::builder(Arc::new(client_auth_roots)) + .with_crls(crls) + .build() + .map_err(|e| make_err!(Code::Internal, "Could not create WebPkiClientVerifier: {e:?}"))? + } else { + WebPkiClientVerifier::no_client_auth() }; let mut config = TlsServerConfig::builder() - .with_no_client_auth() - .with_single_cert(certs, key.into()) - .map_err(|e| make_err!(Code::Internal, "Could not create TlsServerConfig : {:?}", e))?; + .with_client_cert_verifier(verifier) + .with_single_cert(certs, key) + .map_err(|e| make_err!(Code::Internal, "Could not create TlsServerConfig : {e:?}"))?; config.alpn_protocols.push("h2".into()); Ok(Some(TlsAcceptor::from(Arc::new(config))))