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
35
dist/INSTALL.md
vendored
35
dist/INSTALL.md
vendored
|
|
@ -200,6 +200,41 @@ See `config.toml` for all available options. Key settings:
|
|||
- `max_concurrent_requests`: Connection limit
|
||||
- `log_level`: Logging verbosity
|
||||
|
||||
## Certificate Management
|
||||
|
||||
The server supports automatic certificate reloading via SIGHUP signals.
|
||||
|
||||
### Let's Encrypt Integration
|
||||
|
||||
For automatic certificate renewal with certbot:
|
||||
|
||||
```bash
|
||||
# Create post-renewal hook
|
||||
sudo tee /etc/letsencrypt/renewal-hooks/post/reload-pollux.sh > /dev/null << 'EOF'
|
||||
#!/bin/bash
|
||||
# Reload Pollux after Let's Encrypt certificate renewal
|
||||
|
||||
systemctl reload pollux
|
||||
logger -t certbot-pollux-reload "Reloaded pollux after certificate renewal"
|
||||
EOF
|
||||
|
||||
# Make it executable
|
||||
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/reload-pollux.sh
|
||||
|
||||
# Test the hook
|
||||
sudo /etc/letsencrypt/renewal-hooks/post/reload-pollux.sh
|
||||
```
|
||||
|
||||
### Manual Certificate Reload
|
||||
|
||||
```bash
|
||||
# Reload certificates without restarting
|
||||
sudo systemctl reload pollux
|
||||
|
||||
# Check reload in logs
|
||||
sudo journalctl -u pollux -f
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
```bash
|
||||
|
|
|
|||
2
dist/pollux.service
vendored
2
dist/pollux.service
vendored
|
|
@ -15,6 +15,8 @@ NoNewPrivileges=yes
|
|||
ProtectHome=yes
|
||||
ProtectSystem=strict
|
||||
ReadOnlyPaths=/etc/pollux /etc/letsencrypt/live/example.com /var/www/example.com
|
||||
# NOTE: Adjust /etc/letsencrypt/live/example.com and /var/www/example.com to match your config
|
||||
# The server needs read access to config, certificates, and content files
|
||||
# NOTE: Adjust paths to match your config:
|
||||
# - /etc/letsencrypt/live/example.com for Let's Encrypt certs
|
||||
# - /var/www/example.com for your content root
|
||||
|
|
|
|||
61
src/main.rs
61
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();
|
||||
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 = acceptor.clone();
|
||||
let acceptor = Arc::clone(&acceptor);
|
||||
let dir = root.clone();
|
||||
let expected_hostname = hostname.clone(); // Use configured hostname
|
||||
let expected_hostname = hostname.clone();
|
||||
let max_concurrent = max_concurrent_requests;
|
||||
let test_delay = test_processing_delay;
|
||||
tokio::spawn(async move {
|
||||
if let Ok(stream) = acceptor.accept(stream).await {
|
||||
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