diff --git a/CHANGELOG.md b/CHANGELOG.md index 40f9b78..7cd655c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ #### Added * Add support for reporting peer client information +* Reload TLS certificate and key on SIGUSR1 #### Changed diff --git a/Cargo.lock b/Cargo.lock index 3240b92..a85f803 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,6 +311,7 @@ dependencies = [ "aquatic_peer_id", "aquatic_toml_config", "aquatic_ws_protocol", + "arc-swap", "async-tungstenite", "cfg-if", "futures", diff --git a/crates/http/src/config.rs b/crates/http/src/config.rs index f7cd045..c3cbd32 100644 --- a/crates/http/src/config.rs +++ b/crates/http/src/config.rs @@ -32,7 +32,7 @@ pub struct Config { pub cleaning: CleaningConfig, pub privileges: PrivilegeConfig, /// Access list configuration - /// + /// /// The file is read on start and when the program receives `SIGUSR1`. If /// initial parsing fails, the program exits. Later failures result in in /// emitting of an error-level log message, while successful updates of the diff --git a/crates/udp/src/config.rs b/crates/udp/src/config.rs index e8f68b8..f2438f0 100644 --- a/crates/udp/src/config.rs +++ b/crates/udp/src/config.rs @@ -43,7 +43,7 @@ pub struct Config { pub privileges: PrivilegeConfig, /// Access list configuration - /// + /// /// The file is read on start and when the program receives `SIGUSR1`. If /// initial parsing fails, the program exits. Later failures result in in /// emitting of an error-level log message, while successful updates of the diff --git a/crates/ws/Cargo.toml b/crates/ws/Cargo.toml index 7de91e4..a05ee15 100644 --- a/crates/ws/Cargo.toml +++ b/crates/ws/Cargo.toml @@ -30,6 +30,7 @@ aquatic_ws_protocol.workspace = true anyhow = "1" async-tungstenite = "0.23" +arc-swap = "1" cfg-if = "1" futures = "0.3" futures-lite = "1" diff --git a/crates/ws/src/config.rs b/crates/ws/src/config.rs index f033e19..8e935ee 100644 --- a/crates/ws/src/config.rs +++ b/crates/ws/src/config.rs @@ -16,7 +16,7 @@ use aquatic_toml_config::TomlConfig; #[serde(default, deny_unknown_fields)] pub struct Config { /// Number of socket workers. - /// + /// /// On servers with 1-7 physical cores, using a worker per core is /// recommended. With more cores, using two workers less than the /// number of cores is recommended. @@ -26,7 +26,7 @@ pub struct Config { /// swarm workers, encode them and send them back over the socket. pub socket_workers: usize, /// Number of swarm workers. - /// + /// /// A single worker is recommended for servers with 1-7 physical cores. /// With more cores, using two workers is recommended. /// @@ -39,7 +39,7 @@ pub struct Config { pub cleaning: CleaningConfig, pub privileges: PrivilegeConfig, /// Access list configuration - /// + /// /// The file is read on start and when the program receives `SIGUSR1`. If /// initial parsing fails, the program exits. Later failures result in in /// emitting of an error-level log message, while successful updates of the @@ -85,6 +85,11 @@ pub struct NetworkConfig { pub tcp_backlog: i32, /// Enable TLS + /// + /// The TLS files are read on start and when the program receives `SIGUSR1`. + /// If initial parsing fails, the program exits. Later failures result in + /// in emitting of an error-level log message, while a successful update + /// results in emitting of an info-level log message. pub enable_tls: bool, /// Path to TLS certificate (DER-encoded X.509) pub tls_certificate_path: PathBuf, diff --git a/crates/ws/src/lib.rs b/crates/ws/src/lib.rs index 423b006..3eb1d8b 100644 --- a/crates/ws/src/lib.rs +++ b/crates/ws/src/lib.rs @@ -10,6 +10,7 @@ use aquatic_common::cpu_pinning::glommio::{get_worker_placement, set_affinity_fo use aquatic_common::cpu_pinning::WorkerIndex; use aquatic_common::rustls_config::create_rustls_config; use aquatic_common::{PanicSentinelWatcher, ServerStartInstant}; +use arc_swap::ArcSwap; use glommio::{channels::channel_mesh::MeshBuilder, prelude::*}; use signal_hook::{ consts::{SIGTERM, SIGUSR1}, @@ -76,13 +77,13 @@ pub fn run(config: Config) -> ::anyhow::Result<()> { let priv_dropper = PrivilegeDropper::new(config.privileges.clone(), config.socket_workers); let opt_tls_config = if config.network.enable_tls { - Some(Arc::new( + Some(Arc::new(ArcSwap::from_pointee( create_rustls_config( &config.network.tls_certificate_path, &config.network.tls_private_key_path, ) .with_context(|| "create rustls config")?, - )) + ))) } else { None }; @@ -181,6 +182,20 @@ pub fn run(config: Config) -> ::anyhow::Result<()> { match signal { SIGUSR1 => { let _ = update_access_list(&config.access_list, &state.access_list); + + if let Some(tls_config) = opt_tls_config.as_ref() { + match create_rustls_config( + &config.network.tls_certificate_path, + &config.network.tls_private_key_path, + ) { + Ok(config) => { + tls_config.store(Arc::new(config)); + + ::log::info!("successfully updated tls config"); + } + Err(err) => ::log::error!("could not update tls config: {:#}", err), + } + } } SIGTERM => { if sentinel_watcher.panic_was_triggered() { diff --git a/crates/ws/src/workers/socket.rs b/crates/ws/src/workers/socket.rs index 58bfaa3..1b03e2b 100644 --- a/crates/ws/src/workers/socket.rs +++ b/crates/ws/src/workers/socket.rs @@ -13,6 +13,7 @@ use aquatic_common::rustls_config::RustlsConfig; use aquatic_common::{PanicSentinel, ServerStartInstant}; use aquatic_peer_id::PeerClient; use aquatic_ws_protocol::*; +use arc_swap::ArcSwap; use async_tungstenite::WebSocketStream; use futures::stream::{SplitSink, SplitStream}; use futures::{AsyncWriteExt, StreamExt}; @@ -59,7 +60,7 @@ pub async fn run_socket_worker( _sentinel: PanicSentinel, config: Config, state: State, - opt_tls_config: Option>, + opt_tls_config: Option>>, control_message_mesh_builder: MeshBuilder, in_message_mesh_builder: MeshBuilder<(InMessageMeta, InMessage), Partial>, out_message_mesh_builder: MeshBuilder<(OutMessageMeta, OutMessage), Partial>, @@ -370,12 +371,12 @@ async fn run_connection( server_start_instant: ServerStartInstant, out_message_consumer_id: ConsumerId, connection_id: ConnectionId, - opt_tls_config: Option>, + opt_tls_config: Option>>, ip_version: IpVersion, mut stream: TcpStream, ) -> anyhow::Result<()> { if let Some(tls_config) = opt_tls_config { - let tls_acceptor: TlsAcceptor = tls_config.into(); + let tls_acceptor: TlsAcceptor = tls_config.load_full().into(); let stream = tls_acceptor.accept(stream).await?;