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",
"rustls",
"rustls-native-certs",
"rustls-pemfile",
"serde_json",
"tokio",
"tracing",

View File

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

View File

@ -1,5 +1,5 @@
use clap::Parser;
use std::net;
use std::{net, path};
use url::Url;
#[derive(Parser, Clone, Debug)]
@ -21,6 +21,19 @@ pub struct Config {
/// Connect to the given URL starting with https://
#[arg(value_parser = moq_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> {

View File

@ -1,3 +1,5 @@
use std::{fs, io, sync::Arc, time};
use anyhow::Context;
use clap::Parser;
@ -26,10 +28,28 @@ async fn main() -> anyhow::Result<()> {
let (publisher, subscriber) = broadcast::new("");
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();
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()
@ -37,6 +57,12 @@ async fn main() -> anyhow::Result<()> {
.with_root_certificates(roots)
.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
let arc_tls_config = std::sync::Arc::new(tls_config);
@ -63,3 +89,19 @@ async fn main() -> anyhow::Result<()> {
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
ring = "0.16"
rustls = "0.21"
rustls = { version = "0.21", features = ["dangerous_configuration"] }
rustls-pemfile = "1"
rustls-native-certs = "0.6"
webpki = "0.22"

View File

@ -31,6 +31,12 @@ pub struct Config {
#[arg(long)]
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.
#[arg(long)]
pub api: Option<Url>,

View File

@ -19,8 +19,8 @@ pub struct Quic {
impl Quic {
// Create a QUIC endpoint that can be used for both clients and servers.
pub async fn new(config: Config, tls: Tls) -> anyhow::Result<Self> {
let mut client_config = tls.client();
let mut server_config = tls.server();
let mut client_config = tls.client.clone();
let mut server_config = tls.server.clone();
client_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::sign::CertifiedKey;
use rustls::{Certificate, PrivateKey, RootCertStore};
use rustls::{ClientConfig, ServerConfig};
use std::fs;
use std::io::{self, Cursor, Read};
use std::path;
use std::sync::Arc;
use std::{fs, time};
use webpki::{DnsNameRef, EndEntityCert};
use crate::Config;
#[derive(Clone)]
pub struct Tls {
// Support serving multiple certificates, choosing one that looks valid for the given SNI.
// We store the parsed certificate, and the certified cert/key that rustls expects
serve: Arc<ServeCerts>,
// Accept any cert that is trusted by the system's native trust store.
accept: Arc<RootCertStore>,
pub server: rustls::ServerConfig,
pub client: rustls::ClientConfig,
pub fingerprints: Vec<String>,
}
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 {
serve: Arc::new(serve),
accept: Arc::new(roots),
server,
client,
fingerprints,
};
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)]
@ -166,3 +164,19 @@ impl ResolvesServerCert for ServeCerts {
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 {
// Get the first certificate's fingerprint.
// 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()];
let tls_config = axum_server::tls_rustls::RustlsConfig::from_config(Arc::new(tls_config));