Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(575): adding support for inline certificates/keys as base64 encoded #577

Merged
merged 4 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading