Skip to content

Commit

Permalink
feat(575): adding support for inline certificates/keys as base64 enco…
Browse files Browse the repository at this point in the history
…ded (#577)
  • Loading branch information
gabrik authored Nov 2, 2023
1 parent b3ba1c4 commit 4b34f76
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 26 deletions.
17 changes: 16 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions commons/zenoh-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ zenoh-core = { workspace = true }
zenoh-protocol = { workspace = true }
zenoh-result = { workspace = true }
zenoh-util = { workspace = true }
secrecy = { workspace = true }
40 changes: 38 additions & 2 deletions commons/zenoh-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<SecretString>;

pub type ValidationFunction = std::sync::Arc<
dyn Fn(
&str,
Expand Down Expand Up @@ -293,8 +317,20 @@ validated_struct::validator! {
client_auth: Option<bool>,
client_private_key: Option<String>,
client_certificate: Option<String>,
server_name_verification: Option<bool>
},
server_name_verification: Option<bool>,
// Skip serializing field because they contain secrets
#[serde(skip_serializing)]
root_ca_certificate_base64: Option<SecretValue>,
#[serde(skip_serializing)]
server_private_key_base64: Option<SecretValue>,
#[serde(skip_serializing)]
server_certificate_base64: Option<SecretValue>,
#[serde(skip_serializing)]
client_private_key_base64 : Option<SecretValue>,
#[serde(skip_serializing)]
client_certificate_base64 : Option<SecretValue>,
}
,
pub unixpipe: #[derive(Default)]
UnixPipeConf {
file_access_mask: Option<u32>
Expand Down
2 changes: 2 additions & 0 deletions io/zenoh-links/zenoh-link-quic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ zenoh-protocol = { workspace = true }
zenoh-result = { workspace = true }
zenoh-sync = { workspace = true }
zenoh-util = { workspace = true }
base64 = { workspace = true }
secrecy = {workspace = true }
69 changes: 61 additions & 8 deletions io/zenoh-links/zenoh-link-quic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
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 secrecy::ExposeSecret;
use std::net::SocketAddr;
use zenoh_config::Config;
use zenoh_core::zconfigurable;
Expand All @@ -31,7 +33,7 @@ use zenoh_protocol::core::{
endpoint::{Address, Parameters},
Locator,
};
use zenoh_result::{bail, ZResult};
use zenoh_result::{bail, zerror, ZResult};

mod unicast;
mod verify;
Expand Down Expand Up @@ -72,15 +74,55 @@ impl ConfigurationInspector<Config> 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.expose_secret(),
));
}
_ => {}
}
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.expose_secret(),
));
}
_ => {}
}
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.expose_secret(),
));
}
_ => {}
}

if let Some(server_name_verification) = c.server_name_verification() {
match server_name_verification {
true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")),
Expand Down Expand Up @@ -111,12 +153,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";
Expand All @@ -128,3 +173,11 @@ async fn get_quic_addr(address: &Address<'_>) -> ZResult<SocketAddr> {
None => bail!("Couldn't resolve QUIC locator address: {}", address),
}
}

pub fn base64_decode(data: &str) -> ZResult<Vec<u8>> {
use base64::engine::general_purpose;
use base64::Engine;
Ok(general_purpose::STANDARD
.decode(data)
.map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?)
}
7 changes: 7 additions & 0 deletions io/zenoh-links/zenoh-link-quic/src/unicast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// ZettaScale Zenoh Team, <[email protected]>
//

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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions io/zenoh-links/zenoh-link-tls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ zenoh-protocol = { workspace = true }
zenoh-result = { workspace = true }
zenoh-sync = { workspace = true }
zenoh-util = { workspace = true }
base64 = { workspace = true }
secrecy = {workspace = true }
Loading

0 comments on commit 4b34f76

Please sign in to comment.