Implement SIGHUP certificate reloading for Let's Encrypt
- Add tokio signal handling for SIGHUP - Implement thread-safe TLS acceptor reloading with Mutex - Modify main loop to handle signals alongside connections - Update systemd service (already had ExecReload) - Add certbot hook script documentation to INSTALL.md - Enable zero-downtime certificate renewal support
This commit is contained in:
parent
ea8083fe1f
commit
caf9d0984f
3 changed files with 102 additions and 14 deletions
79
src/main.rs
79
src/main.rs
|
|
@ -8,10 +8,32 @@ use clap::Parser;
|
|||
use rustls::ServerConfig;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
use logging::init_logging;
|
||||
|
||||
async fn reload_tls_acceptor(
|
||||
cert_path: &str,
|
||||
key_path: &str,
|
||||
) -> Result<TlsAcceptor, Box<dyn std::error::Error>> {
|
||||
tracing::info!("Reloading TLS certificates");
|
||||
|
||||
let certs = tls::load_certs(cert_path)?;
|
||||
let key = tls::load_private_key(key_path)?;
|
||||
|
||||
let config = ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(certs, key)?;
|
||||
|
||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
|
||||
tracing::info!("TLS certificates reloaded successfully");
|
||||
Ok(acceptor)
|
||||
}
|
||||
|
||||
fn print_startup_info(host: &str, port: u16, root: &str, cert: &str, key: &str, log_level: Option<&str>, max_concurrent: usize) {
|
||||
println!("Pollux Gemini Server");
|
||||
println!("Listening on: {}:{}", host, port);
|
||||
|
|
@ -104,27 +126,56 @@ async fn main() {
|
|||
.with_no_client_auth()
|
||||
.with_single_cert(certs, key).unwrap();
|
||||
|
||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
let initial_acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
let acceptor = Arc::new(Mutex::new(initial_acceptor));
|
||||
|
||||
let listener = TcpListener::bind(format!("{}:{}", bind_host, port)).await.unwrap();
|
||||
|
||||
|
||||
// Create SIGHUP signal handler for certificate reload
|
||||
let mut sighup = signal(SignalKind::hangup())
|
||||
.map_err(|e| format!("Failed to create SIGHUP handler: {}", e))
|
||||
.unwrap();
|
||||
|
||||
// Print startup information
|
||||
print_startup_info(&bind_host, port, &root, &cert_path, &key_path, Some(log_level), max_concurrent_requests);
|
||||
|
||||
loop {
|
||||
let (stream, _) = listener.accept().await.unwrap();
|
||||
tracing::debug!("Accepted connection from {}", stream.peer_addr().unwrap_or_else(|_| "unknown".parse().unwrap()));
|
||||
let acceptor = acceptor.clone();
|
||||
let dir = root.clone();
|
||||
let expected_hostname = hostname.clone(); // Use configured hostname
|
||||
let max_concurrent = max_concurrent_requests;
|
||||
let test_delay = test_processing_delay;
|
||||
tokio::spawn(async move {
|
||||
if let Ok(stream) = acceptor.accept(stream).await {
|
||||
if let Err(e) = server::handle_connection(stream, &dir, &expected_hostname, port, max_concurrent, test_delay).await {
|
||||
tracing::error!("Error handling connection: {}", e);
|
||||
tokio::select! {
|
||||
// Handle new connections
|
||||
result = listener.accept() => {
|
||||
let (stream, _) = result.unwrap();
|
||||
tracing::debug!("Accepted connection from {}", stream.peer_addr().unwrap_or_else(|_| "unknown".parse().unwrap()));
|
||||
let acceptor = Arc::clone(&acceptor);
|
||||
let dir = root.clone();
|
||||
let expected_hostname = hostname.clone();
|
||||
let max_concurrent = max_concurrent_requests;
|
||||
let test_delay = test_processing_delay;
|
||||
tokio::spawn(async move {
|
||||
let acceptor_guard = acceptor.lock().await;
|
||||
if let Ok(stream) = acceptor_guard.accept(stream).await {
|
||||
drop(acceptor_guard); // Release lock before long-running handler
|
||||
if let Err(e) = server::handle_connection(stream, &dir, &expected_hostname, port, max_concurrent, test_delay).await {
|
||||
tracing::error!("Error handling connection: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle SIGHUP for certificate reload
|
||||
_ = sighup.recv() => {
|
||||
tracing::info!("Received SIGHUP, reloading certificates");
|
||||
match reload_tls_acceptor(&cert_path, &key_path).await {
|
||||
Ok(new_acceptor) => {
|
||||
let mut acceptor_guard = acceptor.lock().await;
|
||||
*acceptor_guard = new_acceptor;
|
||||
tracing::info!("TLS certificates reloaded successfully");
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to reload TLS certificates: {}", e);
|
||||
// Continue with old certificates
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue