From ef9c0a297c53a68a80c5cecc678a8e446d301830 Mon Sep 17 00:00:00 2001 From: Mateusz Kaczanowski Date: Tue, 17 Dec 2024 18:10:27 +0100 Subject: [PATCH] update ureq to 3.0.0 for better TLS handling --- Cargo.lock | 423 ++++++++++++++++-- Cargo.toml | 6 +- src/keyring/providers/hashicorp/client.rs | 4 +- .../providers/hashicorp/vault_client.rs | 235 ++++------ 4 files changed, 488 insertions(+), 180 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6ce6919..a09d6537 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -215,21 +215,20 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-rs" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd82dba44d209fddb11c190e0a94b78651f95299598e472215667417a03ff1d" +checksum = "f47bb8cc16b669d267eeccf585aea077d0882f4777b1c1f740217885d6e6e5a3" dependencies = [ "aws-lc-sys", - "mirai-annotations", "paste", "zeroize", ] [[package]] name = "aws-lc-sys" -version = "0.22.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" +checksum = "a2101df3813227bbaaaa0b04cd61c534c7954b22bd68d399b440be937dc63ff7" dependencies = [ "bindgen", "cc", @@ -571,9 +570,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.51" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" dependencies = [ "cc", ] @@ -619,6 +618,35 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding 2.3.1", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna 1.0.3", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url 2.5.2", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -827,6 +855,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + [[package]] name = "dunce" version = "1.0.5" @@ -957,7 +1005,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -1206,7 +1254,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", @@ -1229,7 +1277,7 @@ dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http", + "http 0.2.12", "httpdate", "mime", "sha1", @@ -1241,7 +1289,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http", + "http 0.2.12", ] [[package]] @@ -1314,11 +1362,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1332,6 +1380,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1339,7 +1398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", "pin-project-lite", ] @@ -1366,7 +1425,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "httparse", "httpdate", @@ -1401,6 +1460,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1428,6 +1605,27 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indenter" version = "0.3.3" @@ -1555,12 +1753,12 @@ checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -1587,6 +1785,18 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.4.12" @@ -1678,12 +1888,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - [[package]] name = "mockito" version = "0.31.1" @@ -2353,9 +2557,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.18" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "aws-lc-rs", "log", @@ -2367,6 +2571,15 @@ dependencies = [ "zeroize", ] +[[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.10.0" @@ -2640,7 +2853,7 @@ checksum = "fad8561dab46cf18be56eef057219f48f576d6bb91847cc33d7d4f2779bcd217" dependencies = [ "futures-executor", "headers", - "http", + "http 0.2.12", "hyper", "tokio", "tokio-native-tls", @@ -2701,6 +2914,12 @@ dependencies = [ "der 0.7.9", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" @@ -2762,6 +2981,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "tempfile" version = "3.13.0" @@ -2952,6 +3182,16 @@ dependencies = [ "log", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -3259,22 +3499,38 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +version = "3.0.0-rc3" +source = "git+https://github.com/algesten/ureq.git?tag=3.0.0-rc3#8231cf8290cd2b7767d4bb7a59c5140fda8c9f93" dependencies = [ "base64 0.22.1", + "cc", + "cookie_store", "flate2", "log", "once_cell", + "percent-encoding 2.3.1", "rustls", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", - "url 2.5.2", + "ureq-proto", + "utf-8", "webpki-roots", ] +[[package]] +name = "ureq-proto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcec9cbfbf05a7feef00db0295c6183f468635c7adb12a31e941f3660b071bff" +dependencies = [ + "http 1.2.0", + "httparse", + "log", + "url 2.5.2", +] + [[package]] name = "url" version = "1.7.2" @@ -3298,6 +3554,24 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -3425,9 +3699,9 @@ checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -3641,6 +3915,42 @@ dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", + "synstructure 0.13.1", +] + [[package]] name = "yubihsm" version = "0.42.1" @@ -3697,6 +4007,27 @@ dependencies = [ "syn 2.0.85", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", + "synstructure 0.13.1", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -3716,3 +4047,25 @@ dependencies = [ "quote", "syn 2.0.85", ] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] diff --git a/Cargo.toml b/Cargo.toml index c9b17aed..46344b20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,13 +55,15 @@ yubihsm = { version = "0.42", features = ["secp256k1", "setup", "usb"], optional zeroize = "1" # HashiCorp deps -ureq = { version = "2.12.1", default-features=false, features = ["tls", "json", "gzip"], optional = true} +# NOTE: ureq 3.0.0 has a much better support for tls (rustls) and most imporantly redacts +# the vault token from the logs. Remember to upgrade to stable version as soon as it is available +ureq = { git = "https://github.com/algesten/ureq.git", tag = "3.0.0-rc3", default-features=false, features = ["rustls", "json", "gzip"], optional = true} +rustls = { version = "0.23.18" } base64 = { version = "0.13.0", optional = true} aes-kw = { version = "0.2.1", features = ["std"], optional = true} rsa = { version = "0.6.1", default = true, optional = true} rand = { version = "0.7", optional = true} aes-gcm = { version = "0.10.1", optional = true} -rustls = { version = "0.23.18", features = ["ring"] } [dev-dependencies] abscissa_core = { version = "0.7", features = ["testing"] } diff --git a/src/keyring/providers/hashicorp/client.rs b/src/keyring/providers/hashicorp/client.rs index 0a11dcae..05de52eb 100644 --- a/src/keyring/providers/hashicorp/client.rs +++ b/src/keyring/providers/hashicorp/client.rs @@ -214,7 +214,7 @@ mod tests { ) .expect("Failed to connect"); - let body = serde_json::to_string(&SignRequest { + let body = serde_json::to_string_pretty(&SignRequest { input: TEST_PAYLOAD_TO_SIGN_BASE64.into(), }) .unwrap(); @@ -266,7 +266,7 @@ mod tests { ) .expect("Failed to connect"); - let body = serde_json::to_string(&SignRequest { + let body = serde_json::to_string_pretty(&SignRequest { input: TEST_PAYLOAD_TO_SIGN_BASE64.into(), }) .unwrap(); diff --git a/src/keyring/providers/hashicorp/vault_client.rs b/src/keyring/providers/hashicorp/vault_client.rs index 0a2fb4f0..6f99607b 100644 --- a/src/keyring/providers/hashicorp/vault_client.rs +++ b/src/keyring/providers/hashicorp/vault_client.rs @@ -1,21 +1,25 @@ use abscissa_core::prelude::*; use std::collections::{BTreeMap, HashMap}; -use std::sync; +use std::fs; +use std::sync::{Arc, Mutex, OnceLock}; +use std::time::Duration; use super::error::Error; -use std::time::Duration; -use ureq::Agent; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::CertificateDer; +use ureq::{ + config::AutoHeaderValue, + http::Response, + tls::{Certificate, RootCerts, TlsConfig}, + Agent, Body, +}; use crate::config::provider::hashicorp::VaultEndpointConfig; use crate::keyring::ed25519; use serde::{Deserialize, Serialize}; use serde_json::Value; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; -use rustls::pki_types::{pem::PemObject, CertificateDer, ServerName, UnixTime}; -use rustls::{DigitallySignedStruct, SignatureScheme}; - /// Vault message envelop #[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -112,58 +116,6 @@ impl std::fmt::Display for CreateKeyType { } } -#[derive(Debug)] -struct NoVerification; - -impl ServerCertVerifier for NoVerification { - fn verify_server_cert( - &self, - _end_entity: &CertificateDer<'_>, - _intermediates: &[CertificateDer<'_>], - _server_name: &ServerName<'_>, - _ocsp_response: &[u8], - _now: UnixTime, - ) -> Result { - Ok(ServerCertVerified::assertion()) - } - - fn verify_tls12_signature( - &self, - _message: &[u8], - _cert: &CertificateDer<'_>, - _dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - - fn verify_tls13_signature( - &self, - _message: &[u8], - _cert: &CertificateDer<'_>, - _dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - - fn supported_verify_schemes(&self) -> Vec { - vec![ - SignatureScheme::RSA_PKCS1_SHA1, - SignatureScheme::ECDSA_SHA1_Legacy, - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::ECDSA_NISTP256_SHA256, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::ECDSA_NISTP384_SHA384, - SignatureScheme::RSA_PKCS1_SHA512, - SignatureScheme::ECDSA_NISTP521_SHA512, - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512, - SignatureScheme::ED25519, - SignatureScheme::ED448, - ] - } -} - #[derive(Debug)] pub(crate) struct VaultClient { agent: Agent, @@ -189,46 +141,30 @@ impl VaultClient { // let mut client = Client::new(host, token)?; // default conect timeout is 30s, this should be ok, since we block - let mut agent_builder = ureq::AgentBuilder::new() - .timeout_read(Duration::from_secs(5)) - .timeout_write(Duration::from_secs(5)) - .user_agent(&format!( + let mut agent_builder = Agent::config_builder() + .timeout_global(Some(Duration::from_secs(5))) + .user_agent(AutoHeaderValue::Provided(Arc::new(format!( "{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION") - )); + )))); if ca_cert.is_some() || skip_verify.is_some() { - // see https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html#method.install_default - rustls::crypto::ring::default_provider() - .install_default() - .expect("Failed to install rustls crypto provider"); - - let tls_config_builder = rustls::ClientConfig::builder(); - if skip_verify.is_some_and(|x| x) { - let tls_config = tls_config_builder - .dangerous() - .with_custom_certificate_verifier(sync::Arc::new(NoVerification)) - .with_no_client_auth(); + let tls_config = TlsConfig::builder().disable_verification(true).build(); - agent_builder = agent_builder.tls_config(sync::Arc::new(tls_config)); + agent_builder = agent_builder.tls_config(tls_config); } else if let Some(ca_cert) = ca_cert { - let mut roots = rustls::RootCertStore::empty(); - - let certs: Vec<_> = CertificateDer::pem_file_iter(ca_cert).unwrap().collect(); - for cert in certs { - roots.add(cert.unwrap()).unwrap(); - } + let cert = read_cert(&ca_cert); + let certs: Vec> = vec![Certificate::from_der(cert)]; + let root_certs = RootCerts::new_with_certs(certs.as_slice()); + let tls_config = TlsConfig::builder().root_certs(root_certs).build(); - let tls_config = tls_config_builder - .with_root_certificates(roots) - .with_no_client_auth(); - agent_builder = agent_builder.tls_config(sync::Arc::new(tls_config)); + agent_builder = agent_builder.tls_config(tls_config); } } - let agent: Agent = agent_builder.build(); + let agent: Agent = agent_builder.build().new_agent(); VaultClient { api_endpoint: api_endpoint.into(), @@ -249,18 +185,17 @@ impl VaultClient { keys: BTreeMap>, } + let uri = format!("{}{}/{}", self.api_endpoint, self.endpoints.keys, key_name); + // https://developer.hashicorp.com/vault/api-docs/secret/transit#read-key - let res = self - .agent - .get(&format!( - "{}{}/{}", - self.api_endpoint, self.endpoints.keys, key_name - )) - .set(VAULT_TOKEN, &self.token) - .call(); - - let response = self.check_response_status_code(res)?; - let data = if let Some(data) = response.into_json::>()?.data { + let res = self.agent.get(&uri).header(VAULT_TOKEN, &self.token).call(); + + let response = self.check_response_status_code(&uri, res)?; + let data = if let Some(data) = response + .into_body() + .read_json::>()? + .data + { data } else { return Err(Error::InvalidPubKey( @@ -304,7 +239,7 @@ impl VaultClient { let pubk = base64::decode(pubk)?; debug!( - "Public key: base64 decoded {}, size:{}", + "Public key: base64 decoded {}, size: {}", key_name, pubk.len() ); @@ -316,16 +251,11 @@ impl VaultClient { } pub fn handshake(&self) -> Result<(), Error> { - let res = self - .agent - .get(&format!( - "{}{}", - self.api_endpoint, self.endpoints.handshake, - )) - .set(VAULT_TOKEN, &self.token) - .call(); - - self.check_response_status_code(res)?; + let uri = format!("{}{}", self.api_endpoint, self.endpoints.handshake,); + + let res = self.agent.get(&uri).header(VAULT_TOKEN, &self.token).call(); + + self.check_response_status_code(&uri, res)?; Ok(()) } @@ -348,17 +278,17 @@ impl VaultClient { debug!("signing request: base64 encoded and about to submit for signing..."); + let uri = format!("{}{}/{}", self.api_endpoint, self.endpoints.sign, key_name); + let res = self .agent - .post(&format!( - "{}{}/{}", - self.api_endpoint, self.endpoints.sign, key_name - )) - .set(VAULT_TOKEN, &self.token) + .post(&uri) + .header(VAULT_TOKEN, &self.token) .send_json(body); - let response = self.check_response_status_code(res)?; - let data = if let Some(data) = response.into_json::>()?.data { + let response = self.check_response_status_code(&uri, res)?; + let data = if let Some(data) = response.into_body().read_json::>()?.data + { data } else { return Err(Error::NoSignature); @@ -400,17 +330,16 @@ impl VaultClient { public_key: String, } - let res = self - .agent - .get(&format!( - "{}{}", - self.api_endpoint, self.endpoints.wrapping_key - )) - .set(VAULT_TOKEN, &self.token) - .call(); - - let response = self.check_response_status_code(res)?; - let data = if let Some(data) = response.into_json::>()?.data { + let uri = format!("{}{}", self.api_endpoint, self.endpoints.wrapping_key); + + let res = self.agent.get(&uri).header(VAULT_TOKEN, &self.token).call(); + + let response = self.check_response_status_code(&uri, res)?; + let data = if let Some(data) = response + .into_body() + .read_json::>()? + .data + { data } else { return Err(Error::InvalidPubKey("Error getting wrapping key!".into())); @@ -433,40 +362,64 @@ impl VaultClient { exportable, }; + let uri = format!( + "{}{}/{}/import", + self.api_endpoint, self.endpoints.keys, key_name + ); + let res = self .agent - .post(&format!( - "{}{}/{}/import", - self.api_endpoint, self.endpoints.keys, key_name - )) - .set(VAULT_TOKEN, &self.token) + .post(&uri) + .header(VAULT_TOKEN, &self.token) .send_json(body); - self.check_response_status_code(res)?; + self.check_response_status_code(&uri, res)?; Ok(()) } fn check_response_status_code( &self, - response: Result, - ) -> Result { + uri: &str, + response: Result, ureq::Error>, + ) -> Result, Error> { match response { Ok(response) => Ok(response), - Err(ureq::Error::Status(code, response)) => { + Err(ureq::Error::StatusCode(code)) => { if self.exit_on_error.contains(&code) { panic!( "{}", - Error::ProhibitedResponseCode( - code.to_string(), - response.get_url().to_string(), - ) + Error::ProhibitedResponseCode(code.to_string(), uri.into()) ); } else { - Err(ureq::Error::Status(code, response))? + Err(ureq::Error::StatusCode(code))? } } Err(err) => Err(err.into()), } } } + +fn read_cert(path: &str) -> &'static [u8] { + // a static cache to store file contents per file path + static CERT_CACHE: OnceLock>>> = OnceLock::new(); + + // initialize the cache + let cache = CERT_CACHE.get_or_init(|| Mutex::new(HashMap::new())); + + // access the cache and ensure the content for the given path + let mut map = cache.lock().unwrap(); + if !map.contains_key(path) { + let content = fs::read(path).expect("Failed to read CA certificate"); + + // NOTE: `Certificate::from_pem` from ureq crate does not parse the PEM file correctly + // in version 3.0.0-rc3, so we use `CertificateDer::from_pem_slice` from rustls crate + let cert_der: CertificateDer<'static> = CertificateDer::from_pem_slice(&content).unwrap(); + map.insert(path.to_string(), cert_der.to_vec()); + } + + // leak the cert to get a 'static reference + let content = map.get(path).unwrap(); + let static_content: &'static [u8] = Box::leak(content.clone().into_boxed_slice()); + static_content +}