Skip to main content

waf_tls_managed/
lib.rs

1// SPDX-FileCopyrightText: 2026 0x00spor3
2// SPDX-License-Identifier: Apache-2.0
3
4//! Managed TLS for [Light WAF](https://github.com/0x00spor3/Light-WAF): an implementation of the
5//! core's `waf_proxy::tls::TlsCertSource` trait, injected with `Proxy::builder(&cfg).cert_source(..)`
6//! without forking the core (which itself only ships a static certificate from disk).
7//!
8//! - **ACME, hitless** (`resolver()`): server certificates via TLS-ALPN-01 ([`acme`]) or DNS-01 with
9//!   Cloudflare/Route53 ([`acme_dns01`], [`dns`]), with background renewal that rotates the live
10//!   certificate without rebuilding the acceptor. In cluster mode one node issues and renews and every
11//!   node serves the same certificate from a shared store (`waf-state-redis`).
12//! - **mTLS** (`client_verifier()`): require and verify client certificates against a configured CA
13//!   bundle ([`mtls`]).
14//!
15//! A source may carry either, both, or neither (with a file fallback for the server certificate when
16//! ACME is off but mTLS is on).
17
18pub mod acme;
19pub mod acme_dns01;
20pub mod dns;
21pub mod mtls;
22
23use std::path::Path;
24use std::sync::Arc;
25
26use tokio_rustls::rustls::server::danger::ClientCertVerifier;
27use tokio_rustls::rustls::server::ResolvesServerCert;
28use waf_proxy::tls::{FileCertSource, TlsCertSource, TlsError, TlsMaterial};
29
30pub use acme::{AcmeError, AcmeParams, ChallengeStrategy};
31pub use mtls::{verifier_from_ca_file, MtlsError};
32
33/// A `TlsCertSource` that provides managed server certs (ACME resolver) and/or mTLS client
34/// verification, composed from the pieces in [`acme`]/[`mtls`]. Build it with
35/// [`ManagedTlsCertSource::builder`].
36pub struct ManagedTlsCertSource {
37    resolver: Option<Arc<dyn ResolvesServerCert>>,
38    verifier: Option<Arc<dyn ClientCertVerifier>>,
39    /// Server-cert fallback for the mTLS-only case (ACME off): the core's resolver-less
40    /// path still calls `load()`, so we serve the configured file cert there.
41    file_fallback: Option<FileCertSource>,
42}
43
44impl ManagedTlsCertSource {
45    pub fn builder() -> ManagedTlsBuilder {
46        ManagedTlsBuilder::default()
47    }
48}
49
50impl TlsCertSource for ManagedTlsCertSource {
51    fn load(&self) -> Result<TlsMaterial, TlsError> {
52        // With a resolver set the core never calls this (it uses `resolver()`); it is only
53        // reached in the mTLS-only case, where we serve the configured file cert.
54        match &self.file_fallback {
55            Some(fs) => fs.load(),
56            None => Err(TlsError::NoCertificates(Path::new(
57                "<managed TLS: a server cert requires either ACME ([tls.acme]) or a file cert>",
58            )
59            .to_path_buf())),
60        }
61    }
62
63    fn resolver(&self) -> Option<Arc<dyn ResolvesServerCert>> {
64        self.resolver.clone()
65    }
66
67    fn client_verifier(&self) -> Option<Arc<dyn ClientCertVerifier>> {
68        self.verifier.clone()
69    }
70}
71
72/// Builder for [`ManagedTlsCertSource`].
73#[derive(Default)]
74pub struct ManagedTlsBuilder {
75    resolver: Option<Arc<dyn ResolvesServerCert>>,
76    verifier: Option<Arc<dyn ClientCertVerifier>>,
77    file_fallback: Option<FileCertSource>,
78}
79
80impl ManagedTlsBuilder {
81    /// ACME server-cert resolver (hitless rotation). See [`acme::start_resolver`].
82    pub fn resolver(mut self, resolver: Arc<dyn ResolvesServerCert>) -> Self {
83        self.resolver = Some(resolver);
84        self
85    }
86
87    /// mTLS client-cert verifier. See [`mtls::verifier_from_ca_file`].
88    pub fn client_verifier(mut self, verifier: Arc<dyn ClientCertVerifier>) -> Self {
89        self.verifier = Some(verifier);
90        self
91    }
92
93    /// Server-cert file fallback, used by `load()` when no ACME resolver is set (mTLS-only).
94    pub fn file_fallback(mut self, source: FileCertSource) -> Self {
95        self.file_fallback = Some(source);
96        self
97    }
98
99    pub fn build(self) -> ManagedTlsCertSource {
100        ManagedTlsCertSource {
101            resolver: self.resolver,
102            verifier: self.verifier,
103            file_fallback: self.file_fallback,
104        }
105    }
106}