Skip to main content

tls_hot_reload/
lib.rs

1// Copyright 2026 Sebastian Dobe <sebastiandobe@mailbox.org>
2
3#![forbid(unsafe_code)]
4
5use rustls::crypto::CryptoProvider;
6use std::time::Duration;
7
8#[cfg(all(not(feature = "ring"), not(feature = "aws_lc_rs")))]
9compile_error!("You must activate either `ring` or `aws_lc_rs`");
10
11#[cfg(all(feature = "ring", feature = "aws_lc_rs"))]
12compile_error!("You must activate only one of `ring` or `aws_lc_rs`");
13
14pub use certified_key::CertifiedKeyWatched;
15pub use certified_keys::{BundleCert, CertifiedKeysWatched};
16
17mod certified_key;
18mod certified_keys;
19pub mod error;
20
21/// Creates a simple `rustls::ServerConfig` with automatic hot-reloading of TLS certificates
22/// whenever the given `key_path` / `cert_path` are updated.
23///
24/// If you want to create a custom config:
25///
26/// ```rust,notest
27/// let ck = CertifiedKeyWatched::new(key_path, cert_path).await?;
28/// rustls::ServerConfig::builder()
29///     .with_no_client_auth()
30///     .with_cert_resolver(ck)
31/// ```
32///
33/// # Panics
34///
35/// If the given paths or key + cert combination is invalid.
36pub async fn load_server_config<K, C>(key_path: K, cert_path: C) -> rustls::ServerConfig
37where
38    K: Into<String>,
39    C: Into<String>,
40{
41    install_crypto_provider();
42
43    let ck = match CertifiedKeyWatched::new(key_path, cert_path).await {
44        Ok(ck) => ck,
45        Err(err) => {
46            panic!("Cannot load TLS certificates: {:?}", err)
47        }
48    };
49
50    rustls::ServerConfig::builder()
51        .with_no_client_auth()
52        .with_cert_resolver(ck)
53}
54
55/// The same as `load_server_config`, but will use a polling file watcher as the backend. This will
56/// detect changes not instantly, but fix issues on filesystems like NFS, where `inotify` event do
57/// not work.
58pub async fn load_server_config_polling<K, C>(
59    key_path: K,
60    cert_path: C,
61    poll_interval: Duration,
62) -> rustls::ServerConfig
63where
64    K: Into<String>,
65    C: Into<String>,
66{
67    install_crypto_provider();
68
69    let ck = match CertifiedKeyWatched::new_polling(key_path, cert_path, poll_interval).await {
70        Ok(ck) => ck,
71        Err(err) => {
72            panic!("Cannot load TLS certificates: {:?}", err)
73        }
74    };
75
76    rustls::ServerConfig::builder()
77        .with_no_client_auth()
78        .with_cert_resolver(ck)
79}
80
81/// Checks if the `rustls::CryptoProvider` is already installed and installs it, if not.
82pub fn install_crypto_provider() {
83    if CryptoProvider::get_default().is_none() {
84        // A failure here can only happen during a race condition, e.g. from inside tests, or when
85        // a user initializes multiple single keys concurrently. We don't care if that fails, since
86        // they would all be the same provider anyway.
87        #[cfg(feature = "ring")]
88        let _ = rustls::crypto::ring::default_provider().install_default();
89        #[cfg(feature = "aws_lc_rs")]
90        let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
91    }
92}