Add `--tls-root` and `--tls-disable-verify` to moq-pub. (#114)
This commit is contained in:
parent
24cf36e923
commit
d55c4a80d1
|
@ -1066,6 +1066,7 @@ dependencies = [
|
|||
"rfc6381-codec",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tracing",
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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()];
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
Loading…
Reference in New Issue