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

fix(771): Adding mTLS support in QUIC #899

Merged
merged 6 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
22 changes: 14 additions & 8 deletions Cargo.lock

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

35 changes: 22 additions & 13 deletions io/zenoh-link-commons/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,44 @@
# ZettaScale Zenoh Team, <[email protected]>
#
[package]
rust-version = { workspace = true }
name = "zenoh-link-commons"
version = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
categories = { workspace = true }
description = "Internal crate for zenoh."
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
name = "zenoh-link-commons"
repository = { workspace = true }
rust-version = { workspace = true }
version = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
compression = []

[dependencies]
async-trait = { workspace = true }
base64 = { workspace = true, optional = true }
flume = { workspace = true }
futures = { workspace = true }
rustls = { workspace = true }
rustls-webpki = { workspace = true }
flume = { workspace = true }
tracing = {workspace = true}
serde = { workspace = true, features = ["default"] }
tokio = { workspace = true, features = [
"fs",
"io-util",
"net",
"sync",
"time",
] }
tokio-util = { workspace = true, features = ["rt"] }
tracing = { workspace = true }
webpki-roots = { workspace = true, optional = true }
zenoh-buffers = { workspace = true }
zenoh-codec = { workspace = true }
zenoh-config = { workspace = true }
zenoh-core = { workspace = true }
zenoh-protocol = { workspace = true }
zenoh-result = { workspace = true }
zenoh-util = { workspace = true }
zenoh-runtime = { workspace = true }
tokio = { workspace = true, features = ["io-util", "net", "fs", "sync", "time"] }
tokio-util = { workspace = true, features = ["rt"] }
futures = { workspace = true }
zenoh-util = { workspace = true }
1 change: 1 addition & 0 deletions io/zenoh-link-commons/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern crate alloc;
mod listener;
mod multicast;
pub mod tls;

Mallets marked this conversation as resolved.
Show resolved Hide resolved
mod unicast;

use alloc::{borrow::ToOwned, boxed::Box, string::String, vec, vec::Vec};
Expand Down
33 changes: 20 additions & 13 deletions io/zenoh-links/zenoh-link-quic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,46 @@
# ZettaScale Zenoh Team, <[email protected]>
#
[package]
rust-version = { workspace = true }
name = "zenoh-link-quic"
version = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
categories = { workspace = true }
description = "Internal crate for zenoh."
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
name = "zenoh-link-quic"
repository = { workspace = true }
rust-version = { workspace = true }
version = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-trait = { workspace = true }
base64 = { workspace = true }
futures = { workspace = true }
tracing = {workspace = true}
quinn = { workspace = true }
rustls-native-certs = { workspace = true }
rustls-pemfile = { workspace = true }
rustls-pki-types = { workspace = true }
rustls-webpki = { workspace = true }
secrecy = {workspace = true }
tokio = { workspace = true, features = ["io-util", "net", "fs", "sync", "time"] }
secrecy = { workspace = true }
tokio = { workspace = true, features = [
"fs",
"io-util",
"net",
"sync",
"time",
] }
tokio-util = { workspace = true, features = ["rt"] }
tracing = { workspace = true }
webpki-roots = { workspace = true }
zenoh-config = { workspace = true }
zenoh-core = { workspace = true }
zenoh-link-commons = { workspace = true }
zenoh-protocol = { workspace = true }
zenoh-result = { workspace = true }
zenoh-runtime = { workspace = true }
zenoh-sync = { workspace = true }
zenoh-util = { workspace = true }
zenoh-runtime = { workspace = true }

# Lock due to quinn not supporting rustls 0.22 yet
rustls = { version = "0.21", features = ["dangerous_configuration", "quic"] }
tokio-rustls = "0.24.1"
rustls-pemfile = { version = "1" }
124 changes: 20 additions & 104 deletions io/zenoh-links/zenoh-link-quic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,17 @@
//!
//! [Click here for Zenoh's documentation](../zenoh/index.html)
use async_trait::async_trait;
use config::{
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;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

use zenoh_core::zconfigurable;
use zenoh_link_commons::{ConfigurationInspector, LocatorInspector};
use zenoh_protocol::core::{
endpoint::{Address, Parameters},
Locator,
};
use zenoh_result::{bail, zerror, ZResult};
use zenoh_link_commons::LocatorInspector;
use zenoh_protocol::core::Locator;
use zenoh_result::ZResult;

mod unicast;
mod utils;
mod verify;
pub use unicast::*;
pub use utils::TlsConfigurator as QuicConfigurator;

// Default ALPN protocol
pub const ALPN_QUIC_HTTP: &[&[u8]] = &[b"hq-29"];
Expand Down Expand Up @@ -64,77 +56,6 @@ impl LocatorInspector for QuicLocatorInspector {
}
}

#[derive(Default, Clone, Copy, Debug)]
pub struct QuicConfigurator;

impl ConfigurationInspector<Config> for QuicConfigurator {
fn inspect_config(&self, config: &Config) -> ZResult<String> {
let mut ps: Vec<(&str, &str)> = vec![];

let c = config.transport().link().tls();

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(),
));
}
_ => {}
}

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(),
));
}
_ => {}
}

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")),
false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")),
};
}

let mut s = String::new();
Parameters::extend(ps.drain(..), &mut s);

Ok(s)
}
}

zconfigurable! {
// Default MTU (QUIC PDU) in bytes.
static ref QUIC_DEFAULT_MTU: u16 = QUIC_MAX_MTU;
Expand All @@ -155,27 +76,22 @@ pub mod config {

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_PRIVATE_KEY_BASE_64: &str = "server_private_key_base64";
Mallets marked this conversation as resolved.
Show resolved Hide resolved

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_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_SERVER_NAME_VERIFICATION: &str = "server_name_verification";
pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = "true";
}
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";

async fn get_quic_addr(address: &Address<'_>) -> ZResult<SocketAddr> {
match tokio::net::lookup_host(address.as_str()).await?.next() {
Some(addr) => Ok(addr),
None => bail!("Couldn't resolve QUIC locator address: {}", address),
}
}
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 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:?}"))?)
pub const TLS_CLIENT_AUTH: &str = "client_auth";

pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification";
pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = "true";
}
Loading