Question: Any production ready code for TLS certificate auto update to TlsSettings::intermediate?
Closed this issue · 5 comments
I would like to have production ready code for TLS certificate update? I currently use below code to add TLS certificate but it will expire every 60 days. Then I need to restart server in every 60 days. Any solution for this?
let cert_path = format!("{}/certs/cert.pem", env!("CARGO_MANIFEST_DIR"));
let key_path = format!("{}/certs/key.pem", env!("CARGO_MANIFEST_DIR"));
let mut tls_settings = TlsSettings::intermediate(&cert_path, &key_path).unwrap();
tls_settings.enable_h2(); // This work HTTP/2.0 and HTTP/1.1 (Check it)
lb.add_tls_with_settings("0.0.0.0:6189", None, tls_settings);I use the following logic to renew the certificate regularly.
#[async_trait]
impl pingora::listeners::TlsAccept for GlobalCertificate {
async fn certificate_callback(&self, ssl: &mut SslRef) {
// get certificate from ArcSwap<AHashMap<String, Arc<TlsCertificate>>>
}
}Use background service to regularly update ArcSwap.
@vicanso thank you for your code. is this full code correct method?
use std::collections::HashMap;
use std::sync::Arc;
use ahash::AHashMap;
use arc_swap::ArcSwap;
use pingora::listeners::{TlsAccept, TlsCertificate};
use openssl::ssl::SslRef;
use async_trait::async_trait;
pub struct GlobalCertificate {
pub cert_map: Arc<ArcSwap<AHashMap<String, Arc<TlsCertificate>>>>,
}
#[async_trait]
impl TlsAccept for GlobalCertificate {
async fn certificate_callback(&self, ssl: &mut SslRef) {
if let Some(server_name) = ssl.servername(|_| true) {
let certs = self.cert_map.load();
if let Some(cert) = certs.get(server_name) {
ssl.set_certificate(&cert.cert).ok();
ssl.set_private_key(&cert.key).ok();
}
}
}
}
async fn renew_certificates(cert_store: Arc<ArcSwap<AHashMap<String, Arc<TlsCertificate>>>>) {
loop {
// 1. Fetch or renew certificates using ACME or custom logic
// 2. Parse and create new TlsCertificate
// 3. Replace old map with updated one
let mut new_map = AHashMap::new();
let domains = vec!["example.com", "www.example.com"];
for domain in domains {
// Load from disk, generate or renew certificate here
let cert = TlsCertificate::load_pem("cert.pem", "key.pem").unwrap();
new_map.insert(domain.to_string(), Arc::new(cert));
}
// Swap in the new map atomically
cert_store.store(Arc::new(new_map));
// Sleep until next renewal (e.g., every 12h)
tokio::time::sleep(std::time::Duration::from_secs(43200)).await;
}
}
#[tokio::main]
async fn main() {
let cert_store = Arc::new(ArcSwap::from_pointee(AHashMap::new()));
// Start auto-renewal task
let certs_clone = cert_store.clone();
tokio::spawn(async move {
renew_certificates(certs_clone).await;
});
let tls_acceptor = GlobalCertificate {
cert_map: cert_store.clone(),
};
// Use `tls_acceptor` in your Pingora server setup
}I use pingora background serivce, not tokio::span. https://github.com/cloudflare/pingora/blob/main/pingora-core/src/services/background.rs#L89.
And new tls settings from GlobalCertificate: https://github.com/vicanso/pingap/blob/main/src/proxy/server.rs#L397
This seems like a good feature, but out of scope for the pingora core project. I think we would have to appeal to the community to provide this feature - https://github.com/vicanso/pingap - Seems to be the place to look.