1use std::path::PathBuf;
15use std::sync::Arc;
16use std::time::Duration;
17
18use arc_swap::ArcSwap;
19use hyper::body::{Body, Incoming};
20use hyper::rt::{Read, Write};
21use hyper::service::Service;
22use hyper::{Request, Response};
23use hyper_util::rt::TokioExecutor;
24use hyper_util::server::conn::auto;
25use notify::{Event, EventKind, RecursiveMode, Watcher};
26use rustls::pki_types::pem::PemObject;
27use rustls::pki_types::{CertificateDer, PrivateKeyDer};
28use rustls::server::{ClientHello, ResolvesServerCert};
29use rustls::sign::CertifiedKey;
30use rustls::ServerConfig;
31use tokio::sync::mpsc;
32use tokio_rustls::TlsAcceptor;
33use tracing::{error, info, warn};
34
35use crate::acme::challenge::{ChallengeSolver, ACME_TLS_ALPN};
36use crate::acme::ManagedCerts;
37use crate::config::TlsConfig;
38use crate::stream::sni::SniPattern;
39
40#[derive(Clone, Debug)]
43pub struct AcmeHooks {
44 pub certs: ManagedCerts,
45 pub solver: Arc<dyn ChallengeSolver>,
46}
47
48#[derive(Debug)]
50enum CertSlot {
51 File(Arc<CertifiedKey>),
53 Managed(String),
56}
57
58#[derive(Debug)]
63struct SniCertResolver {
64 certs: Vec<(SniPattern, CertSlot)>,
65 default: CertSlot,
66 acme: Option<AcmeHooks>,
67}
68
69impl SniCertResolver {
70 fn slot_key(&self, slot: &CertSlot) -> Option<Arc<CertifiedKey>> {
71 match slot {
72 CertSlot::File(ck) => Some(ck.clone()),
73 CertSlot::Managed(id) => self
74 .acme
75 .as_ref()?
76 .certs
77 .load()
78 .get(id)
79 .map(|c| c.key.clone()),
80 }
81 }
82}
83
84impl ResolvesServerCert for SniCertResolver {
85 fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
86 if let Some(hooks) = &self.acme {
87 let only_acme = client_hello
88 .alpn()
89 .map(|mut alpn| alpn.next() == Some(ACME_TLS_ALPN) && alpn.next().is_none())
90 .unwrap_or(false);
91 if only_acme {
92 return hooks.solver.challenge_cert(client_hello.server_name()?);
94 }
95 }
96 if let Some(name) = client_hello.server_name() {
97 for (pattern, slot) in &self.certs {
98 if pattern.matches(name) {
99 return self.slot_key(slot);
100 }
101 }
102 }
103 self.slot_key(&self.default)
104 }
105}
106
107fn certified_key(
110 chain: Vec<CertificateDer<'static>>,
111 key: PrivateKeyDer<'static>,
112 provider: &rustls::crypto::CryptoProvider,
113) -> Result<CertifiedKey, TlsError> {
114 CertifiedKey::from_der(chain, key, provider).map_err(|e| TlsError::RustlsConfig(e.to_string()))
115}
116
117pub type SharedTlsConfig = Arc<ArcSwap<ServerConfig>>;
120
121#[derive(Debug, thiserror::Error)]
125pub enum TlsError {
126 #[error("failed to read TLS certificate '{0}': {1}")]
127 CertRead(String, String),
128 #[error("TLS certificate file '{0}' contained no certificates")]
129 NoCerts(String),
130 #[error("failed to read TLS private key '{0}': {1}")]
131 KeyRead(String, String),
132 #[error("TLS private key file '{0}' contained no private key")]
133 NoKey(String),
134 #[error("unsupported TLS min_version '{0}' (expected \"1.2\" or \"1.3\")")]
135 BadMinVersion(String),
136 #[error("failed to build TLS server config: {0}")]
137 RustlsConfig(String),
138 #[error("failed to read client-CA bundle '{0}': {1}")]
139 ClientCaRead(String, String),
140 #[error("client-CA bundle '{0}' contained no certificates")]
141 NoClientCaCerts(String),
142 #[error("failed to build client certificate verifier: {0}")]
143 ClientVerifier(String),
144 #[error("TLS slot '{0}' has no certificate source (set cert_path/key_path, or acme)")]
145 MissingCertSource(String),
146 #[error(
147 "TLS slot '{0}' is ACME-managed but no ACME runtime was provided (is `acme:` configured?)"
148 )]
149 AcmeNotWired(String),
150}
151
152fn file_pair<'a>(
156 cert: &'a Option<String>,
157 key: &'a Option<String>,
158 what: &str,
159) -> Result<(&'a str, &'a str), TlsError> {
160 match (cert, key) {
161 (Some(c), Some(k)) => Ok((c.as_str(), k.as_str())),
162 _ => Err(TlsError::MissingCertSource(what.to_string())),
163 }
164}
165
166pub fn install_crypto_provider() {
174 static INSTALL_PROVIDER: std::sync::Once = std::sync::Once::new();
175 INSTALL_PROVIDER.call_once(|| {
176 let _ = rustls::crypto::ring::default_provider().install_default();
177 });
178}
179
180pub fn load_cert_chain(path: &str) -> Result<Vec<CertificateDer<'static>>, TlsError> {
183 let data =
184 std::fs::read(path).map_err(|e| TlsError::CertRead(path.to_string(), e.to_string()))?;
185 let certs = CertificateDer::pem_slice_iter(&data)
186 .collect::<Result<Vec<_>, _>>()
187 .map_err(|e| TlsError::CertRead(path.to_string(), e.to_string()))?;
188 if certs.is_empty() {
189 return Err(TlsError::NoCerts(path.to_string()));
190 }
191 Ok(certs)
192}
193
194pub fn load_private_key(path: &str) -> Result<PrivateKeyDer<'static>, TlsError> {
197 let data =
198 std::fs::read(path).map_err(|e| TlsError::KeyRead(path.to_string(), e.to_string()))?;
199 match PrivateKeyDer::pem_slice_iter(&data).next() {
200 Some(Ok(key)) => Ok(key),
201 Some(Err(e)) => Err(TlsError::KeyRead(path.to_string(), e.to_string())),
202 None => Err(TlsError::NoKey(path.to_string())),
203 }
204}
205
206fn load_client_ca_roots(path: &str) -> Result<rustls::RootCertStore, TlsError> {
210 let data =
211 std::fs::read(path).map_err(|e| TlsError::ClientCaRead(path.to_string(), e.to_string()))?;
212 let certs = CertificateDer::pem_slice_iter(&data)
213 .collect::<Result<Vec<_>, _>>()
214 .map_err(|e| TlsError::ClientCaRead(path.to_string(), e.to_string()))?;
215 let mut roots = rustls::RootCertStore::empty();
216 let (added, _skipped) = roots.add_parsable_certificates(certs);
217 if added == 0 {
218 return Err(TlsError::NoClientCaCerts(path.to_string()));
219 }
220 Ok(roots)
221}
222
223pub fn build_server_config(
228 tls: &TlsConfig,
229 http2_enabled: bool,
230 acme: Option<&AcmeHooks>,
231) -> Result<Arc<ServerConfig>, TlsError> {
232 install_crypto_provider();
233
234 let versions: &[&'static rustls::SupportedProtocolVersion] = match tls.min_version.as_str() {
235 "1.2" => &[&rustls::version::TLS13, &rustls::version::TLS12],
236 "1.3" => &[&rustls::version::TLS13],
237 other => return Err(TlsError::BadMinVersion(other.to_string())),
238 };
239
240 let provider = Arc::new(rustls::crypto::ring::default_provider());
241 let builder = ServerConfig::builder_with_provider(provider.clone())
242 .with_protocol_versions(versions)
243 .map_err(|e| TlsError::RustlsConfig(e.to_string()))?;
244
245 let builder = match &tls.client_ca_path {
248 Some(ca_path) => {
249 let roots = load_client_ca_roots(ca_path)?;
250 let vbuilder = rustls::server::WebPkiClientVerifier::builder_with_provider(
251 Arc::new(roots),
252 provider.clone(),
253 );
254 let vbuilder = if tls.client_cert_required {
255 vbuilder
256 } else {
257 vbuilder.allow_unauthenticated()
258 };
259 let verifier = vbuilder
260 .build()
261 .map_err(|e| TlsError::ClientVerifier(e.to_string()))?;
262 builder.with_client_cert_verifier(verifier)
263 }
264 None => builder.with_no_client_auth(),
265 };
266
267 fn slot(
271 what: &str,
272 cert: &Option<String>,
273 key: &Option<String>,
274 acme_slot: &Option<crate::config::AcmeSlot>,
275 domains_for_id: Vec<String>,
276 acme: Option<&AcmeHooks>,
277 provider: &Arc<rustls::crypto::CryptoProvider>,
278 ) -> Result<CertSlot, TlsError> {
279 if acme_slot.is_some() {
280 if acme.is_none() {
281 return Err(TlsError::AcmeNotWired(what.to_string()));
282 }
283 let (id, _) = crate::acme::CertId::from_domains(&domains_for_id)
284 .map_err(|e| TlsError::RustlsConfig(e.to_string()))?;
285 return Ok(CertSlot::Managed(id.as_str().to_string()));
286 }
287 let (c, k) = file_pair(cert, key, what)?;
288 Ok(CertSlot::File(Arc::new(certified_key(
289 load_cert_chain(c)?,
290 load_private_key(k)?,
291 provider,
292 )?)))
293 }
294
295 let default = slot(
296 "default",
297 &tls.cert_path,
298 &tls.key_path,
299 &tls.acme,
300 tls.acme
301 .as_ref()
302 .map(|s| s.domains.clone())
303 .unwrap_or_default(),
304 acme,
305 &provider,
306 )?;
307 let mut certs = Vec::with_capacity(tls.sni_certs.len());
308 for sc in &tls.sni_certs {
309 let domains = sc
310 .acme_domains()
311 .map_err(TlsError::RustlsConfig)?
312 .unwrap_or_default();
313 certs.push((
314 SniPattern::parse(&sc.server_name),
315 slot(
316 &sc.server_name,
317 &sc.cert_path,
318 &sc.key_path,
319 &sc.acme,
320 domains,
321 acme,
322 &provider,
323 )?,
324 ));
325 }
326
327 let mut config = builder.with_cert_resolver(Arc::new(SniCertResolver {
330 certs,
331 default,
332 acme: acme.cloned(),
333 }));
334
335 config.alpn_protocols = if http2_enabled {
336 vec![b"h2".to_vec(), b"http/1.1".to_vec()]
338 } else {
339 vec![b"http/1.1".to_vec()]
340 };
341 if acme.is_some() {
342 config.alpn_protocols.push(ACME_TLS_ALPN.to_vec());
346 }
347
348 Ok(Arc::new(config))
349}
350
351#[allow(dead_code)]
357pub fn build_acceptor(tls: &TlsConfig, http2_enabled: bool) -> Result<TlsAcceptor, TlsError> {
358 Ok(TlsAcceptor::from(build_server_config(
359 tls,
360 http2_enabled,
361 None,
362 )?))
363}
364
365pub fn build_reloadable(
369 tls: &TlsConfig,
370 http2_enabled: bool,
371 acme: Option<&AcmeHooks>,
372) -> Result<SharedTlsConfig, TlsError> {
373 Ok(Arc::new(ArcSwap::new(build_server_config(
374 tls,
375 http2_enabled,
376 acme,
377 )?)))
378}
379
380pub fn current_acceptor(shared: &SharedTlsConfig) -> TlsAcceptor {
383 TlsAcceptor::from(shared.load_full())
384}
385
386pub fn negotiated_acme_challenge<IO>(stream: &tokio_rustls::server::TlsStream<IO>) -> bool {
389 stream.get_ref().1.alpn_protocol() == Some(ACME_TLS_ALPN)
390}
391
392#[derive(Debug, Clone, PartialEq)]
394pub struct ClientCertIdentity {
395 pub fingerprint: String,
397 pub subject_cn: Option<String>,
399 pub san_dns: Vec<String>,
401}
402
403pub fn client_cert_identity<IO>(
407 stream: &tokio_rustls::server::TlsStream<IO>,
408) -> Option<ClientCertIdentity> {
409 let leaf = stream.get_ref().1.peer_certificates()?.first()?;
410 let digest = ring::digest::digest(&ring::digest::SHA256, leaf.as_ref());
411 let fingerprint = digest
412 .as_ref()
413 .iter()
414 .map(|b| format!("{:02x}", b))
415 .collect();
416 let (subject_cn, san_dns) = parse_client_identity(leaf.as_ref());
417 Some(ClientCertIdentity {
418 fingerprint,
419 subject_cn,
420 san_dns,
421 })
422}
423
424fn parse_client_identity(der: &[u8]) -> (Option<String>, Vec<String>) {
427 use x509_parser::prelude::*;
428 let cert = match X509Certificate::from_der(der) {
429 Ok((_rem, cert)) => cert,
430 Err(_) => return (None, Vec::new()),
431 };
432 let subject_cn = cert
433 .subject()
434 .iter_common_name()
435 .filter_map(|a| a.as_str().ok())
436 .next()
437 .map(String::from);
438 let san_dns = match cert.subject_alternative_name() {
439 Ok(Some(ext)) => ext
440 .value
441 .general_names
442 .iter()
443 .filter_map(|gn| match gn {
444 GeneralName::DNSName(name) => Some((*name).to_string()),
445 _ => None,
446 })
447 .collect(),
448 _ => Vec::new(),
449 };
450 (subject_cn, san_dns)
451}
452
453pub fn spawn_cert_watcher(
463 tls: TlsConfig,
464 http2_enabled: bool,
465 shared: SharedTlsConfig,
466 label: &'static str,
467 acme: Option<AcmeHooks>,
468) {
469 let (tx, mut rx) = mpsc::channel::<()>(1);
470
471 let mut paths: Vec<&String> = Vec::new();
475 paths.extend(tls.cert_path.iter());
476 paths.extend(tls.key_path.iter());
477 for sc in &tls.sni_certs {
478 paths.extend(sc.cert_path.iter());
479 paths.extend(sc.key_path.iter());
480 }
481 if paths.is_empty() {
482 info!(
483 "{} has no file-based certificates; cert watcher not started",
484 label
485 );
486 return;
487 }
488 let mut dirs: Vec<PathBuf> = Vec::new();
489 for path in paths {
490 let dir = PathBuf::from(path)
491 .parent()
492 .map(PathBuf::from)
493 .unwrap_or_else(|| PathBuf::from("."));
494 if !dirs.contains(&dir) {
495 dirs.push(dir);
496 }
497 }
498
499 std::thread::spawn(move || {
500 let mut watcher =
501 match notify::recommended_watcher(move |res: Result<Event, notify::Error>| {
502 if let Ok(event) = res {
503 if matches!(event.kind, EventKind::Modify(_) | EventKind::Create(_)) {
504 let _ = tx.blocking_send(());
505 }
506 }
507 }) {
508 Ok(w) => w,
509 Err(e) => {
510 error!("{} cert watcher failed to start: {}", label, e);
511 return;
512 }
513 };
514
515 for dir in &dirs {
516 if let Err(e) = watcher.watch(dir, RecursiveMode::Recursive) {
517 error!("{} cert watcher failed to watch {:?}: {}", label, dir, e);
518 }
519 }
520 info!("{} TLS certificate watcher started on {:?}", label, dirs);
521
522 loop {
524 std::thread::sleep(Duration::from_secs(3600));
525 }
526 });
527
528 tokio::spawn(async move {
529 loop {
530 if rx.recv().await.is_none() {
531 break;
532 }
533 tokio::time::sleep(Duration::from_millis(500)).await;
535 while rx.try_recv().is_ok() {}
536
537 match build_server_config(&tls, http2_enabled, acme.as_ref()) {
538 Ok(config) => {
539 shared.store(config);
540 info!("{} TLS certificate reloaded", label);
541 }
542 Err(e) => warn!(
543 "{} TLS certificate reload failed (keeping current): {}",
544 label, e
545 ),
546 }
547 }
548 });
549}
550
551#[allow(dead_code)]
570pub async fn serve_connection<I, S, B>(io: I, service: S, http2_enabled: bool)
571where
572 I: Read + Write + Unpin + Send + 'static,
573 S: Service<Request<Incoming>, Response = Response<B>> + Send + 'static,
574 S::Future: Send + 'static,
575 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
576 B: Body + Send + 'static,
577 B::Data: Send,
578 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
579{
580 if let Err(err) = build_connection(io, service, http2_enabled).await {
581 error!("Connection error: {}", err);
582 }
583}
584
585pub fn build_connection<I, S, B>(
596 io: I,
597 service: S,
598 http2_enabled: bool,
599) -> auto::UpgradeableConnection<'static, I, S, TokioExecutor>
600where
601 I: Read + Write + Unpin + Send + 'static,
602 S: Service<Request<Incoming>, Response = Response<B>> + Send + 'static,
603 S::Future: Send + 'static,
604 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
605 B: Body + Send + 'static,
606 B::Data: Send,
607 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
608{
609 if http2_enabled {
610 auto::Builder::new(TokioExecutor::new())
611 .http2()
612 .enable_connect_protocol()
613 .serve_connection_with_upgrades(io, service)
614 .into_owned()
615 } else {
616 auto::Builder::new(TokioExecutor::new())
617 .http1_only()
618 .serve_connection_with_upgrades(io, service)
619 .into_owned()
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626 use crate::config::TlsConfig;
627
628 fn self_signed(
631 tag: &str,
632 min_version: &str,
633 ) -> (TlsConfig, std::path::PathBuf, std::path::PathBuf) {
634 let certified = rcgen::generate_simple_self_signed(vec!["localhost".to_string()]).unwrap();
635 let dir = std::env::temp_dir();
636 let pid = std::process::id();
637 let cert_path = dir.join(format!("featherbit_{}_{}.crt", tag, pid));
638 let key_path = dir.join(format!("featherbit_{}_{}.key", tag, pid));
639 std::fs::write(&cert_path, certified.cert.pem()).unwrap();
640 std::fs::write(&key_path, certified.signing_key.serialize_pem()).unwrap();
641 let tls = TlsConfig {
642 cert_path: Some(cert_path.to_string_lossy().into_owned()),
643 key_path: Some(key_path.to_string_lossy().into_owned()),
644 min_version: min_version.to_string(),
645 client_ca_path: None,
646 client_cert_required: true,
647 sni_certs: Vec::new(),
648 acme: None,
649 };
650 (tls, cert_path, key_path)
651 }
652
653 #[test]
654 fn test_install_crypto_provider_idempotent() {
655 install_crypto_provider();
656 install_crypto_provider();
657 }
658
659 #[test]
660 fn test_load_cert_and_key() {
661 let (tls, cert, key) = self_signed("load", "1.2");
662 assert!(!load_cert_chain(tls.cert_path.as_deref().unwrap())
663 .unwrap()
664 .is_empty());
665 load_private_key(tls.key_path.as_deref().unwrap()).unwrap();
666 let _ = std::fs::remove_file(cert);
667 let _ = std::fs::remove_file(key);
668 }
669
670 #[test]
671 fn test_load_cert_missing_file() {
672 let err = load_cert_chain("does-not-exist.pem").unwrap_err();
673 assert!(matches!(err, TlsError::CertRead(_, _)));
674 }
675
676 #[test]
677 fn test_load_key_no_key_in_pem() {
678 let (tls, cert, key) = self_signed("nokey", "1.2");
680 let err = load_private_key(tls.cert_path.as_deref().unwrap()).unwrap_err();
681 assert!(matches!(err, TlsError::NoKey(_)));
682 let _ = std::fs::remove_file(cert);
683 let _ = std::fs::remove_file(key);
684 }
685
686 #[test]
687 fn test_alpn_reflects_http2_flag() {
688 let (tls, cert, key) = self_signed("alpn", "1.2");
689
690 let with_h2 = build_server_config(&tls, true, None).unwrap();
691 assert_eq!(
692 with_h2.alpn_protocols,
693 vec![b"h2".to_vec(), b"http/1.1".to_vec()]
694 );
695
696 let without_h2 = build_server_config(&tls, false, None).unwrap();
697 assert_eq!(without_h2.alpn_protocols, vec![b"http/1.1".to_vec()]);
698
699 let _ = std::fs::remove_file(cert);
700 let _ = std::fs::remove_file(key);
701 }
702
703 #[test]
704 fn test_min_version_1_3_ok_and_bad_rejected() {
705 let (mut tls, cert, key) = self_signed("minver", "1.3");
706 build_server_config(&tls, true, None).unwrap();
707
708 tls.min_version = "sslv3".to_string();
709 let err = build_server_config(&tls, true, None).unwrap_err();
710 assert!(matches!(err, TlsError::BadMinVersion(v) if v == "sslv3"));
711
712 let _ = std::fs::remove_file(cert);
713 let _ = std::fs::remove_file(key);
714 }
715
716 #[tokio::test]
720 async fn test_tls_round_trip() {
721 use bytes::Bytes;
722 use http_body_util::Full;
723 use hyper::service::service_fn;
724 use hyper_util::rt::TokioIo;
725 use tokio::net::TcpListener;
726
727 let (tls, cert, key) = self_signed("rt", "1.2");
728 let acceptor = build_acceptor(&tls, true).unwrap();
729
730 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
731 let addr = listener.local_addr().unwrap();
732
733 tokio::spawn(async move {
734 let (stream, _) = listener.accept().await.unwrap();
735 let tls_stream = match acceptor.accept(stream).await {
736 Ok(s) => s,
737 Err(_) => return,
738 };
739 let service = service_fn(|_req| async {
740 Ok::<_, hyper::Error>(Response::new(Full::new(Bytes::from_static(b"ok"))))
741 });
742 serve_connection(TokioIo::new(tls_stream), service, true).await;
743 });
744
745 let client = reqwest::Client::builder()
746 .danger_accept_invalid_certs(true)
747 .use_rustls_tls() .build()
749 .unwrap();
750 let resp = client
751 .get(format!("https://{}/", addr))
752 .send()
753 .await
754 .unwrap();
755 assert_eq!(resp.status(), 200);
756 assert_eq!(resp.version(), reqwest::Version::HTTP_2);
758 assert_eq!(resp.text().await.unwrap(), "ok");
759
760 let _ = std::fs::remove_file(cert);
761 let _ = std::fs::remove_file(key);
762 }
763
764 fn write_fresh_cert(cert_path: &std::path::Path, key_path: &std::path::Path) {
767 let certified = rcgen::generate_simple_self_signed(vec!["localhost".to_string()]).unwrap();
768 std::fs::write(cert_path, certified.cert.pem()).unwrap();
769 std::fs::write(key_path, certified.signing_key.serialize_pem()).unwrap();
770 }
771
772 #[derive(Debug)]
775 struct CapturingVerifier {
776 captured: std::sync::Arc<std::sync::Mutex<Option<Vec<u8>>>>,
777 provider: rustls::crypto::CryptoProvider,
778 }
779
780 impl rustls::client::danger::ServerCertVerifier for CapturingVerifier {
781 fn verify_server_cert(
782 &self,
783 end_entity: &rustls::pki_types::CertificateDer<'_>,
784 _intermediates: &[rustls::pki_types::CertificateDer<'_>],
785 _server_name: &rustls::pki_types::ServerName<'_>,
786 _ocsp_response: &[u8],
787 _now: rustls::pki_types::UnixTime,
788 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
789 *self.captured.lock().unwrap() = Some(end_entity.as_ref().to_vec());
790 Ok(rustls::client::danger::ServerCertVerified::assertion())
791 }
792 fn verify_tls12_signature(
793 &self,
794 message: &[u8],
795 cert: &rustls::pki_types::CertificateDer<'_>,
796 dss: &rustls::DigitallySignedStruct,
797 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
798 rustls::crypto::verify_tls12_signature(
799 message,
800 cert,
801 dss,
802 &self.provider.signature_verification_algorithms,
803 )
804 }
805 fn verify_tls13_signature(
806 &self,
807 message: &[u8],
808 cert: &rustls::pki_types::CertificateDer<'_>,
809 dss: &rustls::DigitallySignedStruct,
810 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
811 match rustls::crypto::verify_tls13_signature(
812 message,
813 cert,
814 dss,
815 &self.provider.signature_verification_algorithms,
816 ) {
817 Ok(v) => Ok(v),
818 Err(_) => {
827 let spki = crate::acme::leaf_spki(cert.as_ref())
828 .map_err(|e| rustls::Error::General(e.to_string()))?;
829 rustls::crypto::verify_tls13_signature_with_raw_key(
830 message,
831 &rustls::pki_types::SubjectPublicKeyInfoDer::from(spki),
832 dss,
833 &self.provider.signature_verification_algorithms,
834 )
835 }
836 }
837 }
838 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
839 self.provider
840 .signature_verification_algorithms
841 .supported_schemes()
842 }
843 }
844
845 async fn spawn_reload_server(shared: SharedTlsConfig) -> std::net::SocketAddr {
848 use tokio::net::TcpListener;
849 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
850 let addr = listener.local_addr().unwrap();
851 tokio::spawn(async move {
852 while let Ok((stream, _)) = listener.accept().await {
853 let acceptor = current_acceptor(&shared);
854 tokio::spawn(async move {
855 let _ = acceptor.accept(stream).await; });
857 }
858 });
859 addr
860 }
861
862 async fn served_leaf_cert(addr: std::net::SocketAddr) -> Vec<u8> {
864 served_leaf_cert_sni(addr, "localhost").await
865 }
866
867 async fn served_leaf_cert_sni(addr: std::net::SocketAddr, sni: &str) -> Vec<u8> {
869 use tokio::net::TcpStream;
870 install_crypto_provider();
871 let captured = std::sync::Arc::new(std::sync::Mutex::new(None));
872 let verifier = std::sync::Arc::new(CapturingVerifier {
873 captured: captured.clone(),
874 provider: rustls::crypto::ring::default_provider(),
875 });
876 let config = rustls::ClientConfig::builder()
877 .dangerous()
878 .with_custom_certificate_verifier(verifier)
879 .with_no_client_auth();
880 let connector = tokio_rustls::TlsConnector::from(Arc::new(config));
881 let tcp = TcpStream::connect(addr).await.unwrap();
882 let name = rustls::pki_types::ServerName::try_from(sni.to_string()).unwrap();
883 let _ = connector.connect(name, tcp).await.unwrap();
884 let leaf = captured.lock().unwrap().clone();
885 leaf.expect("no server certificate captured")
886 }
887
888 #[tokio::test]
889 async fn test_cert_hot_reload_swaps_served_cert() {
890 let dir = std::env::temp_dir();
891 let pid = std::process::id();
892 let cert = dir.join(format!("featherbit_reload_{}.crt", pid));
893 let key = dir.join(format!("featherbit_reload_{}.key", pid));
894 write_fresh_cert(&cert, &key);
895 let tls = TlsConfig {
896 cert_path: Some(cert.to_string_lossy().into_owned()),
897 key_path: Some(key.to_string_lossy().into_owned()),
898 min_version: "1.2".to_string(),
899 client_ca_path: None,
900 client_cert_required: true,
901 sni_certs: Vec::new(),
902 acme: None,
903 };
904
905 let shared = build_reloadable(&tls, false, None).unwrap();
906 let addr = spawn_reload_server(shared.clone()).await;
907
908 let leaf_a = served_leaf_cert(addr).await;
909
910 write_fresh_cert(&cert, &key);
913 shared.store(build_server_config(&tls, false, None).unwrap());
914
915 let leaf_b = served_leaf_cert(addr).await;
916
917 assert_ne!(
918 leaf_a, leaf_b,
919 "served certificate should change after reload"
920 );
921
922 let _ = std::fs::remove_file(&cert);
923 let _ = std::fs::remove_file(&key);
924 }
925
926 #[tokio::test]
927 async fn test_cert_hot_reload_via_file_watcher() {
928 let dir = std::env::temp_dir();
929 let pid = std::process::id();
930 let cert = dir.join(format!("featherbit_watch_{}.crt", pid));
931 let key = dir.join(format!("featherbit_watch_{}.key", pid));
932 write_fresh_cert(&cert, &key);
933 let tls = TlsConfig {
934 cert_path: Some(cert.to_string_lossy().into_owned()),
935 key_path: Some(key.to_string_lossy().into_owned()),
936 min_version: "1.2".to_string(),
937 client_ca_path: None,
938 client_cert_required: true,
939 sni_certs: Vec::new(),
940 acme: None,
941 };
942
943 let shared = build_reloadable(&tls, false, None).unwrap();
944 spawn_cert_watcher(tls.clone(), false, shared.clone(), "test", None);
945 let addr = spawn_reload_server(shared.clone()).await;
946
947 let leaf_a = served_leaf_cert(addr).await;
948
949 write_fresh_cert(&cert, &key);
952
953 let mut leaf_b = leaf_a.clone();
954 for _ in 0..50 {
955 tokio::time::sleep(Duration::from_millis(200)).await;
956 leaf_b = served_leaf_cert(addr).await;
957 if leaf_b != leaf_a {
958 break;
959 }
960 }
961 assert_ne!(
962 leaf_a, leaf_b,
963 "file watcher should hot-reload the certificate"
964 );
965
966 let _ = std::fs::remove_file(&cert);
967 let _ = std::fs::remove_file(&key);
968 }
969
970 fn gen_ca() -> (rcgen::Certificate, rcgen::KeyPair) {
973 let mut params = rcgen::CertificateParams::new(Vec::<String>::new()).unwrap();
974 params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
975 let key = rcgen::KeyPair::generate().unwrap();
976 let cert = params.self_signed(&key).unwrap();
977 (cert, key)
978 }
979
980 fn gen_signed(
981 cn: &str,
982 ca_cert: &rcgen::Certificate,
983 ca_key: &rcgen::KeyPair,
984 ) -> (rcgen::Certificate, rcgen::KeyPair) {
985 let mut params = rcgen::CertificateParams::new(vec![cn.to_string()]).unwrap();
987 params
988 .distinguished_name
989 .push(rcgen::DnType::CommonName, cn);
990 let key = rcgen::KeyPair::generate().unwrap();
991 let issuer = rcgen::Issuer::from_ca_cert_der(ca_cert.der(), ca_key).unwrap();
993 let cert = params.signed_by(&key, &issuer).unwrap();
994 (cert, key)
995 }
996
997 fn sha256_hex(bytes: &[u8]) -> String {
998 ring::digest::digest(&ring::digest::SHA256, bytes)
999 .as_ref()
1000 .iter()
1001 .map(|b| format!("{:02x}", b))
1002 .collect()
1003 }
1004
1005 fn mtls_config(
1009 tag: &str,
1010 required: bool,
1011 ) -> (
1012 TlsConfig,
1013 rcgen::Certificate,
1014 rcgen::KeyPair,
1015 Vec<std::path::PathBuf>,
1016 ) {
1017 let (ca_cert, ca_key) = gen_ca();
1018 let server = rcgen::generate_simple_self_signed(vec!["localhost".to_string()]).unwrap();
1019 let dir = std::env::temp_dir();
1020 let pid = std::process::id();
1021 let scert = dir.join(format!("fb_mtls_{}_{}.crt", tag, pid));
1022 let skey = dir.join(format!("fb_mtls_{}_{}.key", tag, pid));
1023 let ca = dir.join(format!("fb_mtls_{}_{}_ca.crt", tag, pid));
1024 std::fs::write(&scert, server.cert.pem()).unwrap();
1025 std::fs::write(&skey, server.signing_key.serialize_pem()).unwrap();
1026 std::fs::write(&ca, ca_cert.pem()).unwrap();
1027 let tls = TlsConfig {
1028 cert_path: Some(scert.to_string_lossy().into_owned()),
1029 key_path: Some(skey.to_string_lossy().into_owned()),
1030 min_version: "1.2".to_string(),
1031 client_ca_path: Some(ca.to_string_lossy().into_owned()),
1032 client_cert_required: required,
1033 sni_certs: Vec::new(),
1034 acme: None,
1035 };
1036 (tls, ca_cert, ca_key, vec![scert, skey, ca])
1037 }
1038
1039 async fn spawn_mtls_server(
1042 tls: &TlsConfig,
1043 ) -> (
1044 std::net::SocketAddr,
1045 std::sync::Arc<std::sync::Mutex<Option<ClientCertIdentity>>>,
1046 ) {
1047 use tokio::net::TcpListener;
1048 let acceptor = build_acceptor(tls, false).unwrap();
1049 let captured = std::sync::Arc::new(std::sync::Mutex::new(None));
1050 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1051 let addr = listener.local_addr().unwrap();
1052 let cap = captured.clone();
1053 tokio::spawn(async move {
1054 use tokio::io::AsyncWriteExt;
1055 while let Ok((stream, _)) = listener.accept().await {
1056 let acceptor = acceptor.clone();
1057 let cap = cap.clone();
1058 tokio::spawn(async move {
1059 if let Ok(mut tls_stream) = acceptor.accept(stream).await {
1060 *cap.lock().unwrap() = client_cert_identity(&tls_stream);
1061 let _ = tls_stream.write_all(b"1").await;
1064 let _ = tls_stream.flush().await;
1065 tokio::time::sleep(Duration::from_millis(50)).await;
1066 }
1067 });
1068 }
1069 });
1070 (addr, captured)
1071 }
1072
1073 async fn mtls_client_connects(
1080 addr: std::net::SocketAddr,
1081 client: Option<(
1082 Vec<rustls::pki_types::CertificateDer<'static>>,
1083 rustls::pki_types::PrivateKeyDer<'static>,
1084 )>,
1085 ) -> bool {
1086 use tokio::io::AsyncReadExt;
1087 use tokio::net::TcpStream;
1088 install_crypto_provider();
1089 let verifier = std::sync::Arc::new(CapturingVerifier {
1090 captured: std::sync::Arc::new(std::sync::Mutex::new(None)),
1091 provider: rustls::crypto::ring::default_provider(),
1092 });
1093 let builder = rustls::ClientConfig::builder()
1094 .dangerous()
1095 .with_custom_certificate_verifier(verifier);
1096 let config = match client {
1097 Some((chain, key)) => builder.with_client_auth_cert(chain, key).unwrap(),
1098 None => builder.with_no_client_auth(),
1099 };
1100 let connector = tokio_rustls::TlsConnector::from(Arc::new(config));
1101 let tcp = TcpStream::connect(addr).await.unwrap();
1102 let name = rustls::pki_types::ServerName::try_from("localhost").unwrap();
1103 let mut tls = match connector.connect(name, tcp).await {
1104 Ok(t) => t,
1105 Err(_) => return false,
1106 };
1107 let mut buf = [0u8; 1];
1108 tls.read(&mut buf).await.is_ok()
1111 }
1112
1113 fn client_material(
1114 cert: &rcgen::Certificate,
1115 key: &rcgen::KeyPair,
1116 ) -> (
1117 Vec<rustls::pki_types::CertificateDer<'static>>,
1118 rustls::pki_types::PrivateKeyDer<'static>,
1119 ) {
1120 let chain = vec![cert.der().clone()];
1121 let key_der = rustls::pki_types::PrivateKeyDer::Pkcs8(key.serialize_der().into());
1122 (chain, key_der)
1123 }
1124
1125 #[test]
1126 fn test_parse_client_identity() {
1127 let mut params = rcgen::CertificateParams::new(vec![
1128 "a.example.com".to_string(),
1129 "b.example.com".to_string(),
1130 ])
1131 .unwrap();
1132 params
1133 .distinguished_name
1134 .push(rcgen::DnType::CommonName, "svc-a");
1135 let key = rcgen::KeyPair::generate().unwrap();
1136 let cert = params.self_signed(&key).unwrap();
1137
1138 let (cn, san) = parse_client_identity(cert.der().as_ref());
1139 assert_eq!(cn.as_deref(), Some("svc-a"));
1140 assert_eq!(
1141 san,
1142 vec!["a.example.com".to_string(), "b.example.com".to_string()]
1143 );
1144
1145 let (cn, san) = parse_client_identity(&[0xff; 16]);
1147 assert_eq!(cn, None);
1148 assert!(san.is_empty());
1149 }
1150
1151 #[test]
1152 fn test_mtls_config_builds_and_rejects_empty_ca() {
1153 let (tls, _ca_cert, _ca_key, paths) = mtls_config("cfg", true);
1154 build_server_config(&tls, false, None).unwrap();
1156 let mut optional = tls.clone();
1157 optional.client_cert_required = false;
1158 build_server_config(&optional, false, None).unwrap();
1159
1160 let empty = std::env::temp_dir().join(format!("fb_mtls_empty_{}.pem", std::process::id()));
1162 std::fs::write(&empty, b"not a certificate").unwrap();
1163 let mut bad = tls;
1164 bad.client_ca_path = Some(empty.to_string_lossy().into_owned());
1165 assert!(matches!(
1166 build_server_config(&bad, false, None),
1167 Err(TlsError::NoClientCaCerts(_))
1168 ));
1169
1170 let _ = std::fs::remove_file(&empty);
1171 for p in paths {
1172 let _ = std::fs::remove_file(p);
1173 }
1174 }
1175
1176 #[tokio::test]
1177 async fn test_mtls_required_enforces_client_cert() {
1178 let (tls, ca_cert, ca_key, paths) = mtls_config("req", true);
1179 let (client_cert, client_key) = gen_signed("client-1", &ca_cert, &ca_key);
1180 let expected_fp = sha256_hex(client_cert.der().as_ref());
1181
1182 let (addr, captured) = spawn_mtls_server(&tls).await;
1183
1184 let ok = mtls_client_connects(addr, Some(client_material(&client_cert, &client_key))).await;
1187 assert!(ok, "client with a valid cert should connect");
1188 let mut id = None;
1189 for _ in 0..20 {
1190 tokio::time::sleep(Duration::from_millis(25)).await;
1191 id = captured.lock().unwrap().clone();
1192 if id.is_some() {
1193 break;
1194 }
1195 }
1196 let id = id.expect("server should capture client identity");
1197 assert_eq!(id.fingerprint, expected_fp);
1198 assert_eq!(id.subject_cn.as_deref(), Some("client-1"));
1199 assert!(id.san_dns.iter().any(|s| s == "client-1"));
1200
1201 let ok = mtls_client_connects(addr, None).await;
1203 assert!(!ok, "client without a cert must be rejected when required");
1204
1205 for p in paths {
1206 let _ = std::fs::remove_file(p);
1207 }
1208 }
1209
1210 #[tokio::test]
1211 async fn test_mtls_optional_allows_anonymous() {
1212 let (tls, _ca_cert, _ca_key, paths) = mtls_config("opt", false);
1213 let (addr, captured) = spawn_mtls_server(&tls).await;
1214
1215 let ok = mtls_client_connects(addr, None).await;
1217 assert!(ok, "anonymous client should connect in optional mode");
1218 tokio::time::sleep(Duration::from_millis(75)).await;
1219 assert_eq!(captured.lock().unwrap().clone(), None);
1220
1221 for p in paths {
1222 let _ = std::fs::remove_file(p);
1223 }
1224 }
1225
1226 fn write_named_cert(tag: &str, sans: Vec<String>) -> (String, String, Vec<u8>) {
1231 let certified = rcgen::generate_simple_self_signed(sans).unwrap();
1232 let dir = std::env::temp_dir();
1233 let pid = std::process::id();
1234 let cert = dir.join(format!("fb_sni_{}_{}.crt", tag, pid));
1235 let key = dir.join(format!("fb_sni_{}_{}.key", tag, pid));
1236 std::fs::write(&cert, certified.cert.pem()).unwrap();
1237 std::fs::write(&key, certified.signing_key.serialize_pem()).unwrap();
1238 let leaf = certified.cert.der().as_ref().to_vec();
1239 (
1240 cert.to_string_lossy().into_owned(),
1241 key.to_string_lossy().into_owned(),
1242 leaf,
1243 )
1244 }
1245
1246 #[tokio::test]
1247 async fn test_sni_multicert_selects_by_hostname() {
1248 use crate::config::SniCert;
1249
1250 let (def_cert, def_key, def_leaf) = write_named_cert("def", vec!["localhost".to_string()]);
1251 let (a_cert, a_key, a_leaf) = write_named_cert("a", vec!["a.example.com".to_string()]);
1252 let (w_cert, w_key, w_leaf) =
1253 write_named_cert("wild", vec!["x.tenant.example.com".to_string()]);
1254
1255 let tls = TlsConfig {
1256 cert_path: Some(def_cert.clone()),
1257 key_path: Some(def_key.clone()),
1258 min_version: "1.2".to_string(),
1259 client_ca_path: None,
1260 client_cert_required: true,
1261 sni_certs: vec![
1262 SniCert {
1263 server_name: "a.example.com".to_string(),
1264 cert_path: Some(a_cert.clone()),
1265 key_path: Some(a_key.clone()),
1266 acme: None,
1267 },
1268 SniCert {
1269 server_name: "*.tenant.example.com".to_string(),
1270 cert_path: Some(w_cert.clone()),
1271 key_path: Some(w_key.clone()),
1272 acme: None,
1273 },
1274 ],
1275 acme: None,
1276 };
1277
1278 let shared = build_reloadable(&tls, false, None).unwrap();
1279 let addr = spawn_reload_server(shared).await;
1280
1281 assert_eq!(served_leaf_cert_sni(addr, "a.example.com").await, a_leaf);
1283 assert_eq!(
1284 served_leaf_cert_sni(addr, "x.tenant.example.com").await,
1285 w_leaf
1286 );
1287 assert_eq!(
1288 served_leaf_cert_sni(addr, "unmatched.example.com").await,
1289 def_leaf
1290 );
1291 assert_ne!(a_leaf, def_leaf);
1293 assert_ne!(w_leaf, def_leaf);
1294
1295 for p in [def_cert, def_key, a_cert, a_key, w_cert, w_key] {
1296 let _ = std::fs::remove_file(p);
1297 }
1298 }
1299
1300 #[derive(Debug)]
1303 struct FakeSolver(std::sync::Mutex<std::collections::HashMap<String, Arc<CertifiedKey>>>);
1304
1305 impl crate::acme::challenge::ChallengeSolver for FakeSolver {
1306 fn challenge_cert(&self, server_name: &str) -> Option<Arc<CertifiedKey>> {
1307 self.0.lock().unwrap().get(server_name).cloned()
1308 }
1309 }
1310
1311 fn managed_tls(domains: &[&str]) -> TlsConfig {
1312 TlsConfig {
1313 cert_path: None,
1314 key_path: None,
1315 acme: Some(crate::config::AcmeSlot {
1316 domains: domains.iter().map(|d| d.to_string()).collect(),
1317 }),
1318 min_version: "1.2".to_string(),
1319 client_ca_path: None,
1320 client_cert_required: true,
1321 sni_certs: Vec::new(),
1322 }
1323 }
1324
1325 fn hooks_with_placeholder(
1326 domains: &[&str],
1327 ) -> (crate::server::tls::AcmeHooks, crate::acme::CertId, Vec<u8>) {
1328 let domains: Vec<String> = domains.iter().map(|d| d.to_string()).collect();
1329 let (id, norm) = crate::acme::CertId::from_domains(&domains).unwrap();
1330 let (key, leaf) = crate::acme::placeholder_cert(&norm).unwrap();
1331 let certs = crate::acme::new_managed_certs();
1332 crate::acme::publish(
1333 &certs,
1334 &id,
1335 crate::acme::ManagedCert {
1336 key,
1337 leaf_der: leaf.clone(),
1338 state: crate::acme::CertState::Placeholder,
1339 meta: crate::acme::CertMeta::default(),
1340 domains: norm,
1341 },
1342 );
1343 let solver: Arc<dyn crate::acme::challenge::ChallengeSolver> =
1344 Arc::new(FakeSolver(std::sync::Mutex::new(Default::default())));
1345 (AcmeHooks { certs, solver }, id, leaf)
1346 }
1347
1348 async fn handshake_with_alpn(
1351 addr: std::net::SocketAddr,
1352 sni: &str,
1353 alpn: Vec<Vec<u8>>,
1354 ) -> Result<(Vec<u8>, Option<Vec<u8>>), String> {
1355 use tokio::net::TcpStream;
1356 install_crypto_provider();
1357 let captured = std::sync::Arc::new(std::sync::Mutex::new(None));
1358 let verifier = std::sync::Arc::new(CapturingVerifier {
1359 captured: captured.clone(),
1360 provider: rustls::crypto::ring::default_provider(),
1361 });
1362 let mut config = rustls::ClientConfig::builder()
1363 .dangerous()
1364 .with_custom_certificate_verifier(verifier)
1365 .with_no_client_auth();
1366 config.alpn_protocols = alpn;
1367 let connector = tokio_rustls::TlsConnector::from(Arc::new(config));
1368 let tcp = TcpStream::connect(addr).await.unwrap();
1369 let name = rustls::pki_types::ServerName::try_from(sni.to_string()).unwrap();
1370 let stream = connector
1371 .connect(name, tcp)
1372 .await
1373 .map_err(|e| e.to_string())?;
1374 let negotiated = stream.get_ref().1.alpn_protocol().map(|p| p.to_vec());
1375 let leaf = captured.lock().unwrap().clone().ok_or("no cert")?;
1376 Ok((leaf, negotiated))
1377 }
1378
1379 #[tokio::test]
1380 async fn test_managed_slot_serves_placeholder_then_swapped_cert_without_rebuild() {
1381 let (hooks, id, placeholder_leaf) = hooks_with_placeholder(&["m.example.com"]);
1382 let tls = managed_tls(&["m.example.com"]);
1383 let shared = build_reloadable(&tls, false, Some(&hooks)).unwrap();
1384 let addr = spawn_reload_server(shared).await;
1385
1386 assert_eq!(
1387 served_leaf_cert_sni(addr, "m.example.com").await,
1388 placeholder_leaf
1389 );
1390 assert_eq!(
1392 served_leaf_cert_sni(addr, "other.example.com").await,
1393 placeholder_leaf
1394 );
1395
1396 let issued = rcgen::generate_simple_self_signed(vec!["m.example.com".to_string()]).unwrap();
1398 let (key, leaf) = crate::acme::load_certified_key(
1399 &issued.cert.pem(),
1400 &issued.signing_key.serialize_pem(),
1401 )
1402 .unwrap();
1403 crate::acme::update(&hooks.certs, &id, |c| {
1404 c.key = key;
1405 c.leaf_der = leaf.clone();
1406 c.state = crate::acme::CertState::Issued;
1407 });
1408 assert_eq!(served_leaf_cert_sni(addr, "m.example.com").await, leaf);
1409 }
1410
1411 #[tokio::test]
1412 async fn test_acme_tls_alpn_serves_challenge_cert_and_refuses_without_one() {
1413 let (empty_hooks, _, placeholder_leaf) = hooks_with_placeholder(&["m.example.com"]);
1416 let challenge =
1417 crate::acme::challenge::build_challenge_cert("m.example.com", "ka").unwrap();
1418 let challenge_leaf = challenge.end_entity_cert().unwrap().as_ref().to_vec();
1419 let pending = Arc::new(FakeSolver(std::sync::Mutex::new(Default::default())));
1420 pending
1421 .0
1422 .lock()
1423 .unwrap()
1424 .insert("m.example.com".to_string(), challenge.clone());
1425 let pending_hooks = AcmeHooks {
1426 certs: empty_hooks.certs.clone(),
1427 solver: pending,
1428 };
1429 let tls = managed_tls(&["m.example.com"]);
1430
1431 let addr_pending =
1432 spawn_reload_server(build_reloadable(&tls, true, Some(&pending_hooks)).unwrap()).await;
1433 let addr_empty =
1434 spawn_reload_server(build_reloadable(&tls, true, Some(&empty_hooks)).unwrap()).await;
1435
1436 let (leaf, alpn) =
1438 handshake_with_alpn(addr_pending, "m.example.com", vec![b"acme-tls/1".to_vec()])
1439 .await
1440 .unwrap();
1441 assert_eq!(leaf, challenge_leaf);
1442 assert_eq!(alpn.as_deref(), Some(&b"acme-tls/1"[..]));
1443
1444 let (leaf, alpn) = handshake_with_alpn(
1446 addr_pending,
1447 "m.example.com",
1448 vec![b"h2".to_vec(), b"http/1.1".to_vec()],
1449 )
1450 .await
1451 .unwrap();
1452 assert_eq!(leaf, placeholder_leaf);
1453 assert_eq!(alpn.as_deref(), Some(&b"h2"[..]));
1454
1455 assert!(
1457 handshake_with_alpn(addr_empty, "m.example.com", vec![b"acme-tls/1".to_vec()])
1458 .await
1459 .is_err()
1460 );
1461 }
1462
1463 async fn spawn_acme_aware_server(shared: SharedTlsConfig) -> std::net::SocketAddr {
1467 use tokio::io::AsyncWriteExt;
1468 use tokio::net::TcpListener;
1469 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1470 let addr = listener.local_addr().unwrap();
1471 tokio::spawn(async move {
1472 while let Ok((stream, _)) = listener.accept().await {
1473 let acceptor = current_acceptor(&shared);
1474 tokio::spawn(async move {
1475 let Ok(mut tls) = acceptor.accept(stream).await else {
1476 return;
1477 };
1478 if negotiated_acme_challenge(&tls) {
1479 return;
1480 }
1481 let _ = tls
1482 .write_all(
1483 b"HTTP/1.1 200 OK
1484content-length: 2
1485
1486ok",
1487 )
1488 .await;
1489 let _ = tls.shutdown().await;
1490 });
1491 }
1492 });
1493 addr
1494 }
1495
1496 async fn read_response_over_alpn(
1499 addr: std::net::SocketAddr,
1500 sni: &str,
1501 alpn: Vec<Vec<u8>>,
1502 ) -> Vec<u8> {
1503 use tokio::io::{AsyncReadExt, AsyncWriteExt};
1504 use tokio::net::TcpStream;
1505 install_crypto_provider();
1506 let verifier = std::sync::Arc::new(CapturingVerifier {
1507 captured: std::sync::Arc::new(std::sync::Mutex::new(None)),
1508 provider: rustls::crypto::ring::default_provider(),
1509 });
1510 let mut config = rustls::ClientConfig::builder()
1511 .dangerous()
1512 .with_custom_certificate_verifier(verifier)
1513 .with_no_client_auth();
1514 config.alpn_protocols = alpn;
1515 let connector = tokio_rustls::TlsConnector::from(Arc::new(config));
1516 let tcp = TcpStream::connect(addr).await.unwrap();
1517 let name = rustls::pki_types::ServerName::try_from(sni.to_string()).unwrap();
1518 let mut stream = connector.connect(name, tcp).await.unwrap();
1519 let _ = stream
1520 .write_all(
1521 format!(
1522 "GET / HTTP/1.1
1523host: {sni}
1524
1525"
1526 )
1527 .as_bytes(),
1528 )
1529 .await;
1530 let mut buf = Vec::new();
1531 let _ = tokio::time::timeout(
1532 std::time::Duration::from_secs(5),
1533 stream.read_to_end(&mut buf),
1534 )
1535 .await
1536 .expect("server neither answered nor closed the connection");
1537 buf
1538 }
1539
1540 #[tokio::test]
1544 async fn test_acme_challenge_connection_is_closed_without_a_response() {
1545 let (empty_hooks, _, _) = hooks_with_placeholder(&["m.example.com"]);
1546 let challenge =
1547 crate::acme::challenge::build_challenge_cert("m.example.com", "ka").unwrap();
1548 let pending = Arc::new(FakeSolver(std::sync::Mutex::new(Default::default())));
1549 pending
1550 .0
1551 .lock()
1552 .unwrap()
1553 .insert("m.example.com".to_string(), challenge);
1554 let hooks = AcmeHooks {
1555 certs: empty_hooks.certs.clone(),
1556 solver: pending,
1557 };
1558 let tls = managed_tls(&["m.example.com"]);
1559 let addr =
1560 spawn_acme_aware_server(build_reloadable(&tls, false, Some(&hooks)).unwrap()).await;
1561
1562 let answered =
1563 read_response_over_alpn(addr, "m.example.com", vec![b"http/1.1".to_vec()]).await;
1564 assert!(
1565 answered.starts_with(b"HTTP/1.1 200"),
1566 "{:?}",
1567 String::from_utf8_lossy(&answered)
1568 );
1569
1570 let validation =
1571 read_response_over_alpn(addr, "m.example.com", vec![b"acme-tls/1".to_vec()]).await;
1572 assert!(
1573 validation.is_empty(),
1574 "acme-tls/1 connections must be closed without a response, got {:?}",
1575 String::from_utf8_lossy(&validation)
1576 );
1577 }
1578
1579 #[tokio::test]
1580 async fn test_acme_alpn_not_advertised_without_acme() {
1581 let (tls, cert, key) = self_signed("noacme", "1.2");
1582 let shared = build_reloadable(&tls, true, None).unwrap();
1583 let addr = spawn_reload_server(shared).await;
1584 assert!(
1587 handshake_with_alpn(addr, "localhost", vec![b"acme-tls/1".to_vec()])
1588 .await
1589 .is_err()
1590 );
1591 let _ = std::fs::remove_file(cert);
1592 let _ = std::fs::remove_file(key);
1593 }
1594
1595 #[test]
1596 fn test_managed_slot_without_hooks_is_a_wiring_error() {
1597 let tls = managed_tls(&["m.example.com"]);
1598 let err = build_server_config(&tls, true, None).unwrap_err();
1599 assert!(matches!(err, TlsError::AcmeNotWired(_)), "{err}");
1600 }
1601}