Add `--tls-root` and `--tls-disable-verify` to moq-pub. (#114)

This commit is contained in:
kixelated 2023-10-30 22:54:27 +09:00 committed by GitHub
parent 24cf36e923
commit d55c4a80d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 116 additions and 39 deletions

1
Cargo.lock generated
View File

@ -1066,6 +1066,7 @@ dependencies = [
"rfc6381-codec", "rfc6381-codec",
"rustls", "rustls",
"rustls-native-certs", "rustls-native-certs",
"rustls-pemfile",
"serde_json", "serde_json",
"tokio", "tokio",
"tracing", "tracing",

View File

@ -23,8 +23,9 @@ webtransport-quinn = "0.6"
url = "2" url = "2"
# Crypto # Crypto
rustls = "0.21" rustls = { version = "0.21", features = ["dangerous_configuration"] }
rustls-native-certs = "0.6" rustls-native-certs = "0.6"
rustls-pemfile = "1"
# Async stuff # Async stuff
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }

View File

@ -1,5 +1,5 @@
use clap::Parser; use clap::Parser;
use std::net; use std::{net, path};
use url::Url; use url::Url;
#[derive(Parser, Clone, Debug)] #[derive(Parser, Clone, Debug)]
@ -21,6 +21,19 @@ pub struct Config {
/// Connect to the given URL starting with https:// /// Connect to the given URL starting with https://
#[arg(value_parser = moq_url)] #[arg(value_parser = moq_url)]
pub url: Url, pub url: Url,
/// Use the TLS root CA at this path, encoded as PEM.
///
/// This value can be provided multiple times for multiple roots.
/// If this is empty, system roots will be used instead
#[arg(long)]
pub tls_root: Vec<path::PathBuf>,
/// Danger: Disable TLS certificate verification.
///
/// Fine for local development, but should be used in caution in production.
#[arg(long)]
pub tls_disable_verify: bool,
} }
fn moq_url(s: &str) -> Result<Url, String> { fn moq_url(s: &str) -> Result<Url, String> {

View File

@ -1,3 +1,5 @@
use std::{fs, io, sync::Arc, time};
use anyhow::Context; use anyhow::Context;
use clap::Parser; use clap::Parser;
@ -26,10 +28,28 @@ async fn main() -> anyhow::Result<()> {
let (publisher, subscriber) = broadcast::new(""); let (publisher, subscriber) = broadcast::new("");
let mut media = Media::new(&config, publisher).await?; let mut media = Media::new(&config, publisher).await?;
// Ugh, just let me use my native root certs already // Create a list of acceptable root certificates.
let mut roots = rustls::RootCertStore::empty(); let mut roots = rustls::RootCertStore::empty();
for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") {
roots.add(&rustls::Certificate(cert.0)).unwrap(); if config.tls_root.is_empty() {
// Add the platform's native root certificates.
for cert in rustls_native_certs::load_native_certs().context("could not load platform certs")? {
roots
.add(&rustls::Certificate(cert.0))
.context("failed to add root cert")?;
}
} else {
// Add the specified root certificates.
for root in &config.tls_root {
let root = fs::File::open(root).context("failed to open root cert file")?;
let mut root = io::BufReader::new(root);
let root = rustls_pemfile::certs(&mut root).context("failed to read root cert")?;
anyhow::ensure!(root.len() == 1, "expected a single root cert");
let root = rustls::Certificate(root[0].to_owned());
roots.add(&root).context("failed to add root cert")?;
}
} }
let mut tls_config = rustls::ClientConfig::builder() let mut tls_config = rustls::ClientConfig::builder()
@ -37,6 +57,12 @@ async fn main() -> anyhow::Result<()> {
.with_root_certificates(roots) .with_root_certificates(roots)
.with_no_client_auth(); .with_no_client_auth();
// Allow disabling TLS verification altogether.
if config.tls_disable_verify {
let noop = NoCertificateVerification {};
tls_config.dangerous().set_certificate_verifier(Arc::new(noop));
}
tls_config.alpn_protocols = vec![webtransport_quinn::ALPN.to_vec()]; // this one is important tls_config.alpn_protocols = vec![webtransport_quinn::ALPN.to_vec()]; // this one is important
let arc_tls_config = std::sync::Arc::new(tls_config); let arc_tls_config = std::sync::Arc::new(tls_config);
@ -63,3 +89,19 @@ async fn main() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
pub struct NoCertificateVerification {}
impl rustls::client::ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(
&self,
_end_entity: &rustls::Certificate,
_intermediates: &[rustls::Certificate],
_server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
Ok(rustls::client::ServerCertVerified::assertion())
}
}

View File

@ -23,7 +23,7 @@ url = "2"
# Crypto # Crypto
ring = "0.16" ring = "0.16"
rustls = "0.21" rustls = { version = "0.21", features = ["dangerous_configuration"] }
rustls-pemfile = "1" rustls-pemfile = "1"
rustls-native-certs = "0.6" rustls-native-certs = "0.6"
webpki = "0.22" webpki = "0.22"

View File

@ -31,6 +31,12 @@ pub struct Config {
#[arg(long)] #[arg(long)]
pub tls_root: Vec<path::PathBuf>, pub tls_root: Vec<path::PathBuf>,
/// Danger: Disable TLS certificate verification.
///
/// Fine for local development and between relays, but should be used in caution in production.
#[arg(long)]
pub tls_disable_verify: bool,
/// Optional: Use the moq-api via HTTP to store origin information. /// Optional: Use the moq-api via HTTP to store origin information.
#[arg(long)] #[arg(long)]
pub api: Option<Url>, pub api: Option<Url>,

View File

@ -19,8 +19,8 @@ pub struct Quic {
impl Quic { impl Quic {
// Create a QUIC endpoint that can be used for both clients and servers. // Create a QUIC endpoint that can be used for both clients and servers.
pub async fn new(config: Config, tls: Tls) -> anyhow::Result<Self> { pub async fn new(config: Config, tls: Tls) -> anyhow::Result<Self> {
let mut client_config = tls.client(); let mut client_config = tls.client.clone();
let mut server_config = tls.server(); let mut server_config = tls.server.clone();
client_config.alpn_protocols = vec![webtransport_quinn::ALPN.to_vec()]; client_config.alpn_protocols = vec![webtransport_quinn::ALPN.to_vec()];
server_config.alpn_protocols = vec![webtransport_quinn::ALPN.to_vec()]; server_config.alpn_protocols = vec![webtransport_quinn::ALPN.to_vec()];

View File

@ -3,23 +3,19 @@ use ring::digest::{digest, SHA256};
use rustls::server::{ClientHello, ResolvesServerCert}; use rustls::server::{ClientHello, ResolvesServerCert};
use rustls::sign::CertifiedKey; use rustls::sign::CertifiedKey;
use rustls::{Certificate, PrivateKey, RootCertStore}; use rustls::{Certificate, PrivateKey, RootCertStore};
use rustls::{ClientConfig, ServerConfig};
use std::fs;
use std::io::{self, Cursor, Read}; use std::io::{self, Cursor, Read};
use std::path; use std::path;
use std::sync::Arc; use std::sync::Arc;
use std::{fs, time};
use webpki::{DnsNameRef, EndEntityCert}; use webpki::{DnsNameRef, EndEntityCert};
use crate::Config; use crate::Config;
#[derive(Clone)] #[derive(Clone)]
pub struct Tls { pub struct Tls {
// Support serving multiple certificates, choosing one that looks valid for the given SNI. pub server: rustls::ServerConfig,
// We store the parsed certificate, and the certified cert/key that rustls expects pub client: rustls::ClientConfig,
serve: Arc<ServeCerts>, pub fingerprints: Vec<String>,
// Accept any cert that is trusted by the system's native trust store.
accept: Arc<RootCertStore>,
} }
impl Tls { impl Tls {
@ -56,32 +52,34 @@ impl Tls {
} }
} }
// Create the TLS configuration we'll use as a client (relay -> relay)
let mut client = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(roots)
.with_no_client_auth();
// Allow disabling TLS verification altogether.
if config.tls_disable_verify {
let noop = NoCertificateVerification {};
client.dangerous().set_certificate_verifier(Arc::new(noop));
}
let fingerprints = serve.fingerprints();
// Create the TLS configuration we'll use as a server (relay <- browser)
let server = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_cert_resolver(Arc::new(serve));
let certs = Self { let certs = Self {
serve: Arc::new(serve), server,
accept: Arc::new(roots), client,
fingerprints,
}; };
Ok(certs) Ok(certs)
} }
pub fn client(&self) -> ClientConfig {
rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(self.accept.clone())
.with_no_client_auth()
}
pub fn server(&self) -> ServerConfig {
rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_cert_resolver(self.serve.clone())
}
// Return the SHA256 fingerprint of our certificates.
pub fn fingerprints(&self) -> Vec<String> {
self.serve.fingerprints()
}
} }
#[derive(Default)] #[derive(Default)]
@ -166,3 +164,19 @@ impl ResolvesServerCert for ServeCerts {
self.list.last().cloned() self.list.last().cloned()
} }
} }
pub struct NoCertificateVerification {}
impl rustls::client::ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(
&self,
_end_entity: &rustls::Certificate,
_intermediates: &[rustls::Certificate],
_server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
Ok(rustls::client::ServerCertVerified::assertion())
}
}

View File

@ -17,9 +17,9 @@ impl Web {
pub fn new(config: Config, tls: Tls) -> Self { pub fn new(config: Config, tls: Tls) -> Self {
// Get the first certificate's fingerprint. // Get the first certificate's fingerprint.
// TODO serve all of them so we can support multiple signature algorithms. // TODO serve all of them so we can support multiple signature algorithms.
let fingerprint = tls.fingerprints().first().expect("missing certificate").clone(); let fingerprint = tls.fingerprints.first().expect("missing certificate").clone();
let mut tls_config = tls.server(); let mut tls_config = tls.server.clone();
tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
let tls_config = axum_server::tls_rustls::RustlsConfig::from_config(Arc::new(tls_config)); let tls_config = axum_server::tls_rustls::RustlsConfig::from_config(Arc::new(tls_config));