From 146c6fef8c02da54bbfddbc68a0b9d337ccf54bb Mon Sep 17 00:00:00 2001 From: gabrik Date: Tue, 31 Oct 2023 14:02:11 +0100 Subject: [PATCH 1/4] feat(575): adding support for inline certificates/keys as base64 encoded Signed-off-by: gabrik --- Cargo.lock | 2 + Cargo.toml | 2 +- commons/zenoh-config/src/lib.rs | 5 + io/zenoh-links/zenoh-link-quic/Cargo.toml | 1 + io/zenoh-links/zenoh-link-quic/src/lib.rs | 59 ++++++++++-- io/zenoh-links/zenoh-link-quic/src/unicast.rs | 7 ++ io/zenoh-links/zenoh-link-tls/Cargo.toml | 1 + io/zenoh-links/zenoh-link-tls/src/lib.rs | 92 ++++++++++++++++--- io/zenoh-links/zenoh-link-tls/src/unicast.rs | 27 +++++- 9 files changed, 173 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e4c4bcc18..218226bdc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4674,6 +4674,7 @@ dependencies = [ "async-rustls", "async-std", "async-trait", + "base64 0.21.4", "futures", "log", "quinn", @@ -4733,6 +4734,7 @@ dependencies = [ "async-rustls", "async-std", "async-trait", + "base64 0.21.4", "futures", "log", "rustls", diff --git a/Cargo.toml b/Cargo.toml index 157ba72da2..cd8254efc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ async-global-executor = "2.3.1" async-rustls = "0.4.0" async-std = { version = "=1.12.0", default-features = false } # Default features are disabled due to some crates' requirements async-trait = "0.1.60" -base64 = "0.21.0" +base64 = "0.21.4" bincode = "1.3.3" clap = "3.2.23" const_format = "0.2.30" diff --git a/commons/zenoh-config/src/lib.rs b/commons/zenoh-config/src/lib.rs index b0857f2caf..d9948e1b90 100644 --- a/commons/zenoh-config/src/lib.rs +++ b/commons/zenoh-config/src/lib.rs @@ -288,11 +288,16 @@ validated_struct::validator! { pub tls: #[derive(Default)] TLSConf { root_ca_certificate: Option, + root_ca_certificate_base64: Option, server_private_key: Option, + server_private_key_base64: Option, server_certificate: Option, + server_certificate_base64: Option, client_auth: Option, client_private_key: Option, + client_private_key_base64 : Option, client_certificate: Option, + client_certificate_base64 : Option, server_name_verification: Option }, pub unixpipe: #[derive(Default)] diff --git a/io/zenoh-links/zenoh-link-quic/Cargo.toml b/io/zenoh-links/zenoh-link-quic/Cargo.toml index 4d9a4e0b3d..1ace2170ee 100644 --- a/io/zenoh-links/zenoh-link-quic/Cargo.toml +++ b/io/zenoh-links/zenoh-link-quic/Cargo.toml @@ -42,3 +42,4 @@ zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-sync = { workspace = true } zenoh-util = { workspace = true } +base64 = { workspace = true } diff --git a/io/zenoh-links/zenoh-link-quic/src/lib.rs b/io/zenoh-links/zenoh-link-quic/src/lib.rs index f5449a767e..8725d54e08 100644 --- a/io/zenoh-links/zenoh-link-quic/src/lib.rs +++ b/io/zenoh-links/zenoh-link-quic/src/lib.rs @@ -20,7 +20,8 @@ use async_std::net::ToSocketAddrs; use async_trait::async_trait; use config::{ - TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_NAME_VERIFICATION, + TLS_ROOT_CA_CERTIFICATE_BASE64, TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_BASE64, + TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE64, TLS_SERVER_PRIVATE_KEY_FILE, }; use std::net::SocketAddr; @@ -31,7 +32,7 @@ use zenoh_protocol::core::{ endpoint::{Address, Parameters}, Locator, }; -use zenoh_result::{bail, ZResult}; +use zenoh_result::{bail, zerror, ZResult}; mod unicast; mod verify; @@ -72,15 +73,46 @@ impl ConfigurationInspector for QuicConfigurator { let mut ps: Vec<(&str, &str)> = vec![]; let c = config.transport().link().tls(); - if let Some(tls_ca_certificate) = c.root_ca_certificate() { - ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, tls_ca_certificate)); + + match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") + } + (Some(ca_certificate), None) => { + ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); + } + (None, Some(ca_certificate)) => { + ps.push((TLS_ROOT_CA_CERTIFICATE_BASE64, ca_certificate)); + } + _ => {} } - if let Some(tls_server_private_key) = c.server_private_key() { - ps.push((TLS_SERVER_PRIVATE_KEY_FILE, tls_server_private_key)); + + match (c.server_private_key(), c.server_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") + } + (Some(server_private_key), None) => { + ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); + } + (None, Some(server_private_key)) => { + ps.push((TLS_SERVER_PRIVATE_KEY_BASE64, server_private_key)); + } + _ => {} } - if let Some(tls_server_certificate) = c.server_certificate() { - ps.push((TLS_SERVER_CERTIFICATE_FILE, tls_server_certificate)); + + match (c.server_certificate(), c.server_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") + } + (Some(server_certificate), None) => { + ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); + } + (None, Some(server_certificate)) => { + ps.push((TLS_SERVER_CERTIFICATE_BASE64, server_certificate)); + } + _ => {} } + if let Some(server_name_verification) = c.server_name_verification() { match server_name_verification { true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), @@ -111,12 +143,15 @@ zconfigurable! { pub mod config { pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; + pub const TLS_ROOT_CA_CERTIFICATE_BASE64: &str = "root_ca_certificate_base64"; pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; + pub const TLS_SERVER_PRIVATE_KEY_BASE64: &str = "server_private_key_base64"; pub const TLS_SERVER_CERTIFICATE_FILE: &str = "tls_server_certificate_file"; pub const TLS_SERVER_CERTIFICATE_RAW: &str = "tls_server_certificate_raw"; + pub const TLS_SERVER_CERTIFICATE_BASE64: &str = "tls_server_certificate_base64"; pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = "true"; @@ -128,3 +163,11 @@ async fn get_quic_addr(address: &Address<'_>) -> ZResult { None => bail!("Couldn't resolve QUIC locator address: {}", address), } } + +pub fn base64_decode(data: &str) -> ZResult> { + use base64::engine::general_purpose; + use base64::Engine; + Ok(general_purpose::STANDARD + .decode(data) + .map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?) +} diff --git a/io/zenoh-links/zenoh-link-quic/src/unicast.rs b/io/zenoh-links/zenoh-link-quic/src/unicast.rs index dd08a0af27..70bd3ee769 100644 --- a/io/zenoh-links/zenoh-link-quic/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-quic/src/unicast.rs @@ -12,6 +12,7 @@ // ZettaScale Zenoh Team, // +use crate::base64_decode; use crate::{ config::*, get_quic_addr, verify::WebPkiVerifierAnyServerName, ALPN_QUIC_HTTP, QUIC_ACCEPT_THROTTLE_TIME, QUIC_DEFAULT_MTU, QUIC_LOCATOR_PREFIX, @@ -246,6 +247,8 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { // Read the certificates let f = if let Some(value) = epconf.get(TLS_ROOT_CA_CERTIFICATE_RAW) { value.as_bytes().to_vec() + } else if let Some(b64_certificate) = epconf.get(TLS_ROOT_CA_CERTIFICATE_BASE64) { + base64_decode(b64_certificate)? } else if let Some(value) = epconf.get(TLS_ROOT_CA_CERTIFICATE_FILE) { async_std::fs::read(value) .await @@ -334,6 +337,8 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { let f = if let Some(value) = epconf.get(TLS_SERVER_CERTIFICATE_RAW) { value.as_bytes().to_vec() + } else if let Some(b64_certificate) = epconf.get(TLS_SERVER_CERTIFICATE_BASE64) { + base64_decode(b64_certificate)? } else if let Some(value) = epconf.get(TLS_SERVER_CERTIFICATE_FILE) { async_std::fs::read(value) .await @@ -350,6 +355,8 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { // Private keys let f = if let Some(value) = epconf.get(TLS_SERVER_PRIVATE_KEY_RAW) { value.as_bytes().to_vec() + } else if let Some(b64_key) = epconf.get(TLS_SERVER_PRIVATE_KEY_BASE64) { + base64_decode(b64_key)? } else if let Some(value) = epconf.get(TLS_SERVER_PRIVATE_KEY_FILE) { async_std::fs::read(value) .await diff --git a/io/zenoh-links/zenoh-link-tls/Cargo.toml b/io/zenoh-links/zenoh-link-tls/Cargo.toml index dfe2cd3562..129cba5db9 100644 --- a/io/zenoh-links/zenoh-link-tls/Cargo.toml +++ b/io/zenoh-links/zenoh-link-tls/Cargo.toml @@ -41,3 +41,4 @@ zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-sync = { workspace = true } zenoh-util = { workspace = true } +base64 = { workspace = true } diff --git a/io/zenoh-links/zenoh-link-tls/src/lib.rs b/io/zenoh-links/zenoh-link-tls/src/lib.rs index 8fb2f899a8..c0dd9cef83 100644 --- a/io/zenoh-links/zenoh-link-tls/src/lib.rs +++ b/io/zenoh-links/zenoh-link-tls/src/lib.rs @@ -21,9 +21,10 @@ use async_rustls::rustls::ServerName; use async_std::net::ToSocketAddrs; use async_trait::async_trait; use config::{ - TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_FILE, TLS_CLIENT_PRIVATE_KEY_FILE, - TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_NAME_VERIFICATION, - TLS_SERVER_PRIVATE_KEY_FILE, + TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_BASE64, TLS_CLIENT_CERTIFICATE_FILE, + TLS_CLIENT_PRIVATE_KEY_BASE64, TLS_CLIENT_PRIVATE_KEY_FILE, TLS_ROOT_CA_CERTIFICATE_BASE64, + TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, + TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, TLS_SERVER_PRIVATE_KEY_FILE, }; use std::{convert::TryFrom, net::SocketAddr}; use zenoh_config::Config; @@ -69,27 +70,79 @@ impl ConfigurationInspector for TlsConfigurator { let mut ps: Vec<(&str, &str)> = vec![]; let c = config.transport().link().tls(); - if let Some(ca_certificate) = c.root_ca_certificate() { - ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); + + match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") + } + (Some(ca_certificate), None) => { + ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); + } + (None, Some(ca_certificate)) => { + ps.push((TLS_ROOT_CA_CERTIFICATE_BASE64, ca_certificate)); + } + _ => {} } - if let Some(server_private_key) = c.server_private_key() { - ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); + + match (c.server_private_key(), c.server_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") + } + (Some(server_private_key), None) => { + ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); + } + (None, Some(server_private_key)) => { + ps.push((TLS_SERVER_PRIVATE_KEY_BASE_64, server_private_key)); + } + _ => {} } - if let Some(server_certificate) = c.server_certificate() { - ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); + + match (c.server_certificate(), c.server_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") + } + (Some(server_certificate), None) => { + ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); + } + (None, Some(server_certificate)) => { + ps.push((TLS_SERVER_CERTIFICATE_BASE64, server_certificate)); + } + _ => {} } + if let Some(client_auth) = c.client_auth() { match client_auth { true => ps.push((TLS_CLIENT_AUTH, "true")), false => ps.push((TLS_CLIENT_AUTH, "false")), }; } - if let Some(client_private_key) = c.client_private_key() { - ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); + + match (c.client_private_key(), c.client_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") + } + (Some(client_private_key), None) => { + ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); + } + (None, Some(client_private_key)) => { + ps.push((TLS_CLIENT_PRIVATE_KEY_BASE64, client_private_key)); + } + _ => {} } - if let Some(client_certificate) = c.client_certificate() { - ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); + + match (c.client_private_key(), c.client_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") + } + (Some(client_certificate), None) => { + ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); + } + (None, Some(client_certificate)) => { + ps.push((TLS_CLIENT_CERTIFICATE_BASE64, client_certificate)); + } + _ => {} } + if let Some(server_name_verification) = c.server_name_verification() { match server_name_verification { true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), @@ -120,18 +173,23 @@ zconfigurable! { pub mod config { pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; + pub const TLS_ROOT_CA_CERTIFICATE_BASE64: &str = "root_ca_certificate_base64"; pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; + pub const TLS_SERVER_PRIVATE_KEY_BASE_64: &str = "server_private_key_base64"; pub const TLS_SERVER_CERTIFICATE_FILE: &str = "server_certificate_file"; pub const TLS_SERVER_CERTIFICATE_RAW: &str = "server_certificate_raw"; + pub const TLS_SERVER_CERTIFICATE_BASE64: &str = "server_certificate_base64"; pub const TLS_CLIENT_PRIVATE_KEY_FILE: &str = "client_private_key_file"; pub const TLS_CLIENT_PRIVATE_KEY_RAW: &str = "client_private_key_raw"; + pub const TLS_CLIENT_PRIVATE_KEY_BASE64: &str = "client_private_key_base64"; pub const TLS_CLIENT_CERTIFICATE_FILE: &str = "client_certificate_file"; pub const TLS_CLIENT_CERTIFICATE_RAW: &str = "client_certificate_raw"; + pub const TLS_CLIENT_CERTIFICATE_BASE64: &str = "client_certificate_base64"; pub const TLS_CLIENT_AUTH: &str = "client_auth"; @@ -156,3 +214,11 @@ pub fn get_tls_host<'a>(address: &'a Address<'a>) -> ZResult<&'a str> { pub fn get_tls_server_name(address: &Address<'_>) -> ZResult { Ok(ServerName::try_from(get_tls_host(address)?).map_err(|e| zerror!(e))?) } + +pub fn base64_decode(data: &str) -> ZResult> { + use base64::engine::general_purpose; + use base64::Engine; + Ok(general_purpose::STANDARD + .decode(data) + .map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?) +} diff --git a/io/zenoh-links/zenoh-link-tls/src/unicast.rs b/io/zenoh-links/zenoh-link-tls/src/unicast.rs index ff8d68e6bd..63c6d63b1e 100644 --- a/io/zenoh-links/zenoh-link-tls/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-tls/src/unicast.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use crate::{ - config::*, get_tls_addr, get_tls_host, get_tls_server_name, + base64_decode, config::*, get_tls_addr, get_tls_host, get_tls_server_name, verify::WebPkiVerifierAnyServerName, TLS_ACCEPT_THROTTLE_TIME, TLS_DEFAULT_MTU, TLS_LINGER_TIMEOUT, TLS_LOCATOR_PREFIX, }; @@ -583,6 +583,7 @@ impl TlsServerConfig { config, TLS_SERVER_PRIVATE_KEY_RAW, TLS_SERVER_PRIVATE_KEY_FILE, + TLS_SERVER_PRIVATE_KEY_BASE_64, ) .await } @@ -592,6 +593,7 @@ impl TlsServerConfig { config, TLS_SERVER_CERTIFICATE_RAW, TLS_SERVER_CERTIFICATE_FILE, + TLS_SERVER_CERTIFICATE_BASE64, ) .await } @@ -700,6 +702,7 @@ impl TlsClientConfig { config, TLS_CLIENT_PRIVATE_KEY_RAW, TLS_CLIENT_PRIVATE_KEY_FILE, + TLS_CLIENT_PRIVATE_KEY_BASE64, ) .await } @@ -709,6 +712,7 @@ impl TlsClientConfig { config, TLS_CLIENT_CERTIFICATE_RAW, TLS_CLIENT_CERTIFICATE_FILE, + TLS_CLIENT_CERTIFICATE_BASE64, ) .await } @@ -718,9 +722,12 @@ async fn load_tls_key( config: &Config<'_>, tls_private_key_raw_config_key: &str, tls_private_key_file_config_key: &str, + tls_private_key_base64_config_key: &str, ) -> ZResult> { if let Some(value) = config.get(tls_private_key_raw_config_key) { return Ok(value.as_bytes().to_vec()); + } else if let Some(b64_key) = config.get(tls_private_key_base64_config_key) { + return base64_decode(b64_key); } else if let Some(value) = config.get(tls_private_key_file_config_key) { return Ok(fs::read(value) .await @@ -740,9 +747,12 @@ async fn load_tls_certificate( config: &Config<'_>, tls_certificate_raw_config_key: &str, tls_certificate_file_config_key: &str, + tls_certificate_base64_config_key: &str, ) -> ZResult> { if let Some(value) = config.get(tls_certificate_raw_config_key) { return Ok(value.as_bytes().to_vec()); + } else if let Some(b64_certificate) = config.get(tls_certificate_base64_config_key) { + return base64_decode(b64_certificate); } else if let Some(value) = config.get(tls_certificate_file_config_key) { return Ok(fs::read(value) .await @@ -767,6 +777,21 @@ fn load_trust_anchors(config: &Config<'_>) -> ZResult> { root_cert_store.add_trust_anchors(trust_anchors.into_iter()); return Ok(Some(root_cert_store)); } + if let Some(b64_certificate) = config.get(TLS_ROOT_CA_CERTIFICATE_BASE64) { + let certificate_pem = base64_decode(b64_certificate)?; + let mut pem = BufReader::new(certificate_pem.as_slice()); + let certs = rustls_pemfile::certs(&mut pem)?; + let trust_anchors = certs.iter().map(|cert| { + let ta = TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); + OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject, + ta.spki, + ta.name_constraints, + ) + }); + root_cert_store.add_trust_anchors(trust_anchors.into_iter()); + return Ok(Some(root_cert_store)); + } if let Some(filename) = config.get(TLS_ROOT_CA_CERTIFICATE_FILE) { let mut pem = BufReader::new(File::open(filename)?); let certs = rustls_pemfile::certs(&mut pem)?; From 8a5f71ef6eb30af831c2edaadfdd4e8ddcd605f0 Mon Sep 17 00:00:00 2001 From: gabrik Date: Tue, 31 Oct 2023 14:43:42 +0100 Subject: [PATCH 2/4] feat(575): fix typo in Signed-off-by: gabrik --- io/zenoh-links/zenoh-link-tls/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io/zenoh-links/zenoh-link-tls/src/lib.rs b/io/zenoh-links/zenoh-link-tls/src/lib.rs index c0dd9cef83..5875acecce 100644 --- a/io/zenoh-links/zenoh-link-tls/src/lib.rs +++ b/io/zenoh-links/zenoh-link-tls/src/lib.rs @@ -130,9 +130,9 @@ impl ConfigurationInspector for TlsConfigurator { _ => {} } - match (c.client_private_key(), c.client_private_key_base64()) { + match (c.client_certificate(), c.client_certificate_base64()) { (Some(_), Some(_)) => { - bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") + bail!("Only one between 'client_certificate' and 'client_certificate_base64' can be present!") } (Some(client_certificate), None) => { ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); From 2ca6bf15ad7bdad1f895c0c47a3ecc7c4bd4b90e Mon Sep 17 00:00:00 2001 From: gabrik Date: Tue, 31 Oct 2023 18:30:39 +0100 Subject: [PATCH 3/4] feat(575): WIP hiding secrets in logging not working Signed-off-by: gabrik --- Cargo.lock | 15 +++++++++++++- commons/zenoh-config/src/lib.rs | 18 +++++++++------- io/zenoh-links/zenoh-link-quic/Cargo.toml | 2 +- io/zenoh-links/zenoh-link-quic/src/lib.rs | 15 +++++++++++--- io/zenoh-links/zenoh-link-tls/Cargo.toml | 2 +- io/zenoh-links/zenoh-link-tls/src/lib.rs | 25 ++++++++++++++++++----- 6 files changed, 59 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 218226bdc0..24a474ff18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3143,6 +3143,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "serde", + "zeroize", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -3943,7 +3953,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "static_assertions", ] @@ -4547,6 +4557,7 @@ dependencies = [ "flume", "json5", "num_cpus", + "secrecy", "serde", "serde_json", "serde_yaml", @@ -4682,6 +4693,7 @@ dependencies = [ "rustls-native-certs", "rustls-pemfile", "rustls-webpki", + "secrecy", "zenoh-config", "zenoh-core", "zenoh-link-commons", @@ -4740,6 +4752,7 @@ dependencies = [ "rustls", "rustls-pemfile", "rustls-webpki", + "secrecy", "webpki-roots", "zenoh-config", "zenoh-core", diff --git a/commons/zenoh-config/src/lib.rs b/commons/zenoh-config/src/lib.rs index d9948e1b90..9345118464 100644 --- a/commons/zenoh-config/src/lib.rs +++ b/commons/zenoh-config/src/lib.rs @@ -288,18 +288,22 @@ validated_struct::validator! { pub tls: #[derive(Default)] TLSConf { root_ca_certificate: Option, - root_ca_certificate_base64: Option, server_private_key: Option, - server_private_key_base64: Option, server_certificate: Option, - server_certificate_base64: Option, client_auth: Option, client_private_key: Option, - client_private_key_base64 : Option, client_certificate: Option, - client_certificate_base64 : Option, - server_name_verification: Option - }, + server_name_verification: Option, + pub private : #[derive(Default)] + Base64Data { + root_ca_certificate_base64: Option, + server_private_key_base64: Option, + server_certificate_base64: Option, + client_private_key_base64 : Option, + client_certificate_base64 : Option, + } + } + , pub unixpipe: #[derive(Default)] UnixPipeConf { file_access_mask: Option diff --git a/io/zenoh-links/zenoh-link-quic/Cargo.toml b/io/zenoh-links/zenoh-link-quic/Cargo.toml index 1ace2170ee..20baa21f0e 100644 --- a/io/zenoh-links/zenoh-link-quic/Cargo.toml +++ b/io/zenoh-links/zenoh-link-quic/Cargo.toml @@ -42,4 +42,4 @@ zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-sync = { workspace = true } zenoh-util = { workspace = true } -base64 = { workspace = true } +base64 = { workspace = true } \ No newline at end of file diff --git a/io/zenoh-links/zenoh-link-quic/src/lib.rs b/io/zenoh-links/zenoh-link-quic/src/lib.rs index 8725d54e08..5dc5e9ba5b 100644 --- a/io/zenoh-links/zenoh-link-quic/src/lib.rs +++ b/io/zenoh-links/zenoh-link-quic/src/lib.rs @@ -74,7 +74,10 @@ impl ConfigurationInspector for QuicConfigurator { let c = config.transport().link().tls(); - match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { + match ( + c.root_ca_certificate(), + c.private().root_ca_certificate_base64(), + ) { (Some(_), Some(_)) => { bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") } @@ -87,7 +90,10 @@ impl ConfigurationInspector for QuicConfigurator { _ => {} } - match (c.server_private_key(), c.server_private_key_base64()) { + match ( + c.server_private_key(), + c.private().server_private_key_base64(), + ) { (Some(_), Some(_)) => { bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") } @@ -100,7 +106,10 @@ impl ConfigurationInspector for QuicConfigurator { _ => {} } - match (c.server_certificate(), c.server_certificate_base64()) { + match ( + c.server_certificate(), + c.private().server_certificate_base64(), + ) { (Some(_), Some(_)) => { bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") } diff --git a/io/zenoh-links/zenoh-link-tls/Cargo.toml b/io/zenoh-links/zenoh-link-tls/Cargo.toml index 129cba5db9..c397ad29d1 100644 --- a/io/zenoh-links/zenoh-link-tls/Cargo.toml +++ b/io/zenoh-links/zenoh-link-tls/Cargo.toml @@ -41,4 +41,4 @@ zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-sync = { workspace = true } zenoh-util = { workspace = true } -base64 = { workspace = true } +base64 = { workspace = true } \ No newline at end of file diff --git a/io/zenoh-links/zenoh-link-tls/src/lib.rs b/io/zenoh-links/zenoh-link-tls/src/lib.rs index 5875acecce..79a17de401 100644 --- a/io/zenoh-links/zenoh-link-tls/src/lib.rs +++ b/io/zenoh-links/zenoh-link-tls/src/lib.rs @@ -71,7 +71,10 @@ impl ConfigurationInspector for TlsConfigurator { let c = config.transport().link().tls(); - match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { + match ( + c.root_ca_certificate(), + c.private().root_ca_certificate_base64(), + ) { (Some(_), Some(_)) => { bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") } @@ -84,7 +87,10 @@ impl ConfigurationInspector for TlsConfigurator { _ => {} } - match (c.server_private_key(), c.server_private_key_base64()) { + match ( + c.server_private_key(), + c.private().server_private_key_base64(), + ) { (Some(_), Some(_)) => { bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") } @@ -97,7 +103,10 @@ impl ConfigurationInspector for TlsConfigurator { _ => {} } - match (c.server_certificate(), c.server_certificate_base64()) { + match ( + c.server_certificate(), + c.private().server_certificate_base64(), + ) { (Some(_), Some(_)) => { bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") } @@ -117,7 +126,10 @@ impl ConfigurationInspector for TlsConfigurator { }; } - match (c.client_private_key(), c.client_private_key_base64()) { + match ( + c.client_private_key(), + c.private().client_private_key_base64(), + ) { (Some(_), Some(_)) => { bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") } @@ -130,7 +142,10 @@ impl ConfigurationInspector for TlsConfigurator { _ => {} } - match (c.client_certificate(), c.client_certificate_base64()) { + match ( + c.client_certificate(), + c.private().client_certificate_base64(), + ) { (Some(_), Some(_)) => { bail!("Only one between 'client_certificate' and 'client_certificate_base64' can be present!") } From 9a2601317151d3a647902511589a92e8cb1af010 Mon Sep 17 00:00:00 2001 From: gabrik Date: Thu, 2 Nov 2023 10:40:20 +0100 Subject: [PATCH 4/4] feat(575): using a mix of secrecy + serde::skip_serializing to avoid leaks of secrets in logs Signed-off-by: gabrik --- Cargo.toml | 1 + commons/zenoh-config/Cargo.toml | 1 + commons/zenoh-config/src/lib.rs | 43 +++++++++++++++---- io/zenoh-links/zenoh-link-quic/Cargo.toml | 3 +- io/zenoh-links/zenoh-link-quic/src/lib.rs | 31 +++++++------- io/zenoh-links/zenoh-link-tls/Cargo.toml | 3 +- io/zenoh-links/zenoh-link-tls/src/lib.rs | 51 ++++++++++++----------- 7 files changed, 83 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd8254efc2..a707ab390c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,6 +128,7 @@ rustls = { version = "0.21.5", features = ["dangerous_configuration"] } rustls-native-certs = "0.6.2" rustls-pemfile = "1.0.2" schemars = "0.8.12" +secrecy = {version = "0.8.0", features = ["serde", "alloc"]} serde = { version = "1.0.154", default-features = false, features = [ "derive", ] } # Default features are disabled due to usage in no_std crates diff --git a/commons/zenoh-config/Cargo.toml b/commons/zenoh-config/Cargo.toml index 3bf62bfdb6..f0189ff3e7 100644 --- a/commons/zenoh-config/Cargo.toml +++ b/commons/zenoh-config/Cargo.toml @@ -35,3 +35,4 @@ zenoh-core = { workspace = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-util = { workspace = true } +secrecy = { workspace = true } diff --git a/commons/zenoh-config/src/lib.rs b/commons/zenoh-config/src/lib.rs index 9345118464..45b9d4a218 100644 --- a/commons/zenoh-config/src/lib.rs +++ b/commons/zenoh-config/src/lib.rs @@ -16,6 +16,7 @@ pub mod defaults; mod include; use include::recursive_include; +use secrecy::{CloneableSecret, DebugSecret, Secret, SerializableSecret, Zeroize}; use serde::{ de::{self, MapAccess, Visitor}, Deserialize, Serialize, @@ -46,6 +47,29 @@ use zenoh_protocol::{ use zenoh_result::{bail, zerror, ZResult}; use zenoh_util::LibLoader; +// Wrappers for secrecy of values +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct SecretString(String); + +impl Deref for SecretString { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl SerializableSecret for SecretString {} +impl DebugSecret for SecretString {} +impl CloneableSecret for SecretString {} +impl Zeroize for SecretString { + fn zeroize(&mut self) { + self.0 = "".to_string(); + } +} + +pub type SecretValue = Secret; + pub type ValidationFunction = std::sync::Arc< dyn Fn( &str, @@ -294,14 +318,17 @@ validated_struct::validator! { client_private_key: Option, client_certificate: Option, server_name_verification: Option, - pub private : #[derive(Default)] - Base64Data { - root_ca_certificate_base64: Option, - server_private_key_base64: Option, - server_certificate_base64: Option, - client_private_key_base64 : Option, - client_certificate_base64 : Option, - } + // Skip serializing field because they contain secrets + #[serde(skip_serializing)] + root_ca_certificate_base64: Option, + #[serde(skip_serializing)] + server_private_key_base64: Option, + #[serde(skip_serializing)] + server_certificate_base64: Option, + #[serde(skip_serializing)] + client_private_key_base64 : Option, + #[serde(skip_serializing)] + client_certificate_base64 : Option, } , pub unixpipe: #[derive(Default)] diff --git a/io/zenoh-links/zenoh-link-quic/Cargo.toml b/io/zenoh-links/zenoh-link-quic/Cargo.toml index 20baa21f0e..1cc2d82744 100644 --- a/io/zenoh-links/zenoh-link-quic/Cargo.toml +++ b/io/zenoh-links/zenoh-link-quic/Cargo.toml @@ -42,4 +42,5 @@ zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-sync = { workspace = true } zenoh-util = { workspace = true } -base64 = { workspace = true } \ No newline at end of file +base64 = { workspace = true } +secrecy = {workspace = true } \ No newline at end of file diff --git a/io/zenoh-links/zenoh-link-quic/src/lib.rs b/io/zenoh-links/zenoh-link-quic/src/lib.rs index 5dc5e9ba5b..4f268200a2 100644 --- a/io/zenoh-links/zenoh-link-quic/src/lib.rs +++ b/io/zenoh-links/zenoh-link-quic/src/lib.rs @@ -24,6 +24,7 @@ use config::{ TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE64, TLS_SERVER_PRIVATE_KEY_FILE, }; +use secrecy::ExposeSecret; use std::net::SocketAddr; use zenoh_config::Config; use zenoh_core::zconfigurable; @@ -74,10 +75,7 @@ impl ConfigurationInspector for QuicConfigurator { let c = config.transport().link().tls(); - match ( - c.root_ca_certificate(), - c.private().root_ca_certificate_base64(), - ) { + match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { (Some(_), Some(_)) => { bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") } @@ -85,15 +83,15 @@ impl ConfigurationInspector for QuicConfigurator { ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); } (None, Some(ca_certificate)) => { - ps.push((TLS_ROOT_CA_CERTIFICATE_BASE64, ca_certificate)); + ps.push(( + TLS_ROOT_CA_CERTIFICATE_BASE64, + ca_certificate.expose_secret(), + )); } _ => {} } - match ( - c.server_private_key(), - c.private().server_private_key_base64(), - ) { + match (c.server_private_key(), c.server_private_key_base64()) { (Some(_), Some(_)) => { bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") } @@ -101,15 +99,15 @@ impl ConfigurationInspector for QuicConfigurator { ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); } (None, Some(server_private_key)) => { - ps.push((TLS_SERVER_PRIVATE_KEY_BASE64, server_private_key)); + ps.push(( + TLS_SERVER_PRIVATE_KEY_BASE64, + server_private_key.expose_secret(), + )); } _ => {} } - match ( - c.server_certificate(), - c.private().server_certificate_base64(), - ) { + match (c.server_certificate(), c.server_certificate_base64()) { (Some(_), Some(_)) => { bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") } @@ -117,7 +115,10 @@ impl ConfigurationInspector for QuicConfigurator { ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); } (None, Some(server_certificate)) => { - ps.push((TLS_SERVER_CERTIFICATE_BASE64, server_certificate)); + ps.push(( + TLS_SERVER_CERTIFICATE_BASE64, + server_certificate.expose_secret(), + )); } _ => {} } diff --git a/io/zenoh-links/zenoh-link-tls/Cargo.toml b/io/zenoh-links/zenoh-link-tls/Cargo.toml index c397ad29d1..5d047b1160 100644 --- a/io/zenoh-links/zenoh-link-tls/Cargo.toml +++ b/io/zenoh-links/zenoh-link-tls/Cargo.toml @@ -41,4 +41,5 @@ zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-sync = { workspace = true } zenoh-util = { workspace = true } -base64 = { workspace = true } \ No newline at end of file +base64 = { workspace = true } +secrecy = {workspace = true } \ No newline at end of file diff --git a/io/zenoh-links/zenoh-link-tls/src/lib.rs b/io/zenoh-links/zenoh-link-tls/src/lib.rs index 79a17de401..9b45b5e68b 100644 --- a/io/zenoh-links/zenoh-link-tls/src/lib.rs +++ b/io/zenoh-links/zenoh-link-tls/src/lib.rs @@ -26,6 +26,7 @@ use config::{ TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, TLS_SERVER_PRIVATE_KEY_FILE, }; +use secrecy::ExposeSecret; use std::{convert::TryFrom, net::SocketAddr}; use zenoh_config::Config; use zenoh_core::zconfigurable; @@ -71,10 +72,7 @@ impl ConfigurationInspector for TlsConfigurator { let c = config.transport().link().tls(); - match ( - c.root_ca_certificate(), - c.private().root_ca_certificate_base64(), - ) { + match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { (Some(_), Some(_)) => { bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") } @@ -82,15 +80,15 @@ impl ConfigurationInspector for TlsConfigurator { ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); } (None, Some(ca_certificate)) => { - ps.push((TLS_ROOT_CA_CERTIFICATE_BASE64, ca_certificate)); + ps.push(( + TLS_ROOT_CA_CERTIFICATE_BASE64, + ca_certificate.expose_secret(), + )); } _ => {} } - match ( - c.server_private_key(), - c.private().server_private_key_base64(), - ) { + match (c.server_private_key(), c.server_private_key_base64()) { (Some(_), Some(_)) => { bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") } @@ -98,15 +96,15 @@ impl ConfigurationInspector for TlsConfigurator { ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); } (None, Some(server_private_key)) => { - ps.push((TLS_SERVER_PRIVATE_KEY_BASE_64, server_private_key)); + ps.push(( + TLS_SERVER_PRIVATE_KEY_BASE_64, + server_private_key.expose_secret(), + )); } _ => {} } - match ( - c.server_certificate(), - c.private().server_certificate_base64(), - ) { + match (c.server_certificate(), c.server_certificate_base64()) { (Some(_), Some(_)) => { bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") } @@ -114,7 +112,10 @@ impl ConfigurationInspector for TlsConfigurator { ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); } (None, Some(server_certificate)) => { - ps.push((TLS_SERVER_CERTIFICATE_BASE64, server_certificate)); + ps.push(( + TLS_SERVER_CERTIFICATE_BASE64, + server_certificate.expose_secret(), + )); } _ => {} } @@ -126,10 +127,7 @@ impl ConfigurationInspector for TlsConfigurator { }; } - match ( - c.client_private_key(), - c.private().client_private_key_base64(), - ) { + match (c.client_private_key(), c.client_private_key_base64()) { (Some(_), Some(_)) => { bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") } @@ -137,15 +135,15 @@ impl ConfigurationInspector for TlsConfigurator { ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); } (None, Some(client_private_key)) => { - ps.push((TLS_CLIENT_PRIVATE_KEY_BASE64, client_private_key)); + ps.push(( + TLS_CLIENT_PRIVATE_KEY_BASE64, + client_private_key.expose_secret(), + )); } _ => {} } - match ( - c.client_certificate(), - c.private().client_certificate_base64(), - ) { + match (c.client_certificate(), c.client_certificate_base64()) { (Some(_), Some(_)) => { bail!("Only one between 'client_certificate' and 'client_certificate_base64' can be present!") } @@ -153,7 +151,10 @@ impl ConfigurationInspector for TlsConfigurator { ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); } (None, Some(client_certificate)) => { - ps.push((TLS_CLIENT_CERTIFICATE_BASE64, client_certificate)); + ps.push(( + TLS_CLIENT_CERTIFICATE_BASE64, + client_certificate.expose_secret(), + )); } _ => {} }