use std::path::PathBuf; use anyhow::Context; use quinn::ServerConfig; use quinn_proto::crypto::rustls::QuicServerConfig; use rcgen::generate_simple_self_signed; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use rustls::version::TLS13; /// Ensure a self-signed certificate exists on disk and return a QUIC server config. /// When `production` is true, cert and key must already exist (no auto-generation). pub fn build_server_config( cert_path: &PathBuf, key_path: &PathBuf, production: bool, ) -> anyhow::Result { if !cert_path.exists() || !key_path.exists() { if production { anyhow::bail!( "TLS cert or key missing at {:?} / {:?}; production mode forbids auto-generation", cert_path, key_path ); } generate_self_signed_cert(cert_path, key_path)?; } let cert_bytes = std::fs::read(cert_path).context("read cert")?; let key_bytes = std::fs::read(key_path).context("read key")?; let cert_chain = vec![CertificateDer::from(cert_bytes)]; let key = PrivateKeyDer::try_from(key_bytes).map_err(|_| anyhow::anyhow!("invalid key"))?; let mut tls = rustls::ServerConfig::builder_with_protocol_versions(&[&TLS13]) .with_no_client_auth() .with_single_cert(cert_chain, key)?; tls.alpn_protocols = vec![b"capnp".to_vec()]; let crypto = QuicServerConfig::try_from(tls) .map_err(|e| anyhow::anyhow!("invalid server TLS config: {e}"))?; Ok(ServerConfig::with_crypto(std::sync::Arc::new(crypto))) } fn generate_self_signed_cert(cert_path: &PathBuf, key_path: &PathBuf) -> anyhow::Result<()> { if let Some(parent) = cert_path.parent() { std::fs::create_dir_all(parent).context("create cert dir")?; } if let Some(parent) = key_path.parent() { std::fs::create_dir_all(parent).context("create key dir")?; } let subject_alt_names = vec![ "localhost".to_string(), "127.0.0.1".to_string(), "::1".to_string(), ]; let issued = generate_simple_self_signed(subject_alt_names)?; let key_der = issued.key_pair.serialize_der(); std::fs::write(cert_path, issued.cert.der()).context("write cert")?; std::fs::write(key_path, &key_der).context("write key")?; tracing::info!( cert = %cert_path.display(), key = %key_path.display(), "generated self-signed TLS certificate" ); Ok(()) }