This is an automated email from the ASF dual-hosted git repository. piotr pushed a commit to branch improve_http_secret in repository https://gitbox.apache.org/repos/asf/iggy.git
commit 66b379231055623ab0a2a6849801197a182d816c Author: spetz <[email protected]> AuthorDate: Wed Mar 18 21:11:04 2026 +0100 feat(security): generate random JWT secrets when not configured --- Cargo.toml | 55 ++++--------------------------------- core/ai/mcp/Cargo.toml | 6 +--- core/integration/Cargo.toml | 7 +---- core/server/config.toml | 6 ++-- core/server/src/http/http_server.rs | 29 +++++++++++++++---- 5 files changed, 36 insertions(+), 67 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 539251436..e6ebfb7f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,19 +97,7 @@ clap_complete = "4.5.66" clock = { path = "core/clock" } colored = "3.1.1" comfy-table = "7.2.2" -compio = { version = "0.18.0", features = [ - "runtime", - "macros", - "io-uring", - "time", - "rustls", - "ring", - "net", - "quic", - "tls", - "ws", - "fs", -] } +compio = { version = "0.18.0", features = ["runtime", "macros", "io-uring", "time", "rustls", "ring", "net", "quic", "tls", "ws", "fs"] } # Pin compio-driver >= 0.11.2 to fix musl compilation (compio-rs/compio#668) compio-driver = "0.11.3" configs = { path = "core/configs", version = "0.1.0" } @@ -188,22 +176,9 @@ octocrab = "0.49.5" once_cell = "1.21.3" opentelemetry = { version = "0.31.0", features = ["trace", "logs"] } opentelemetry-appender-tracing = { version = "0.31.1", features = ["log"] } -opentelemetry-otlp = { version = "0.31.0", features = [ - "logs", - "trace", - "grpc-tonic", - "http", - "http-proto", - "reqwest-client", -] } +opentelemetry-otlp = { version = "0.31.0", features = ["logs", "trace", "grpc-tonic", "http", "http-proto", "reqwest-client"] } opentelemetry-semantic-conventions = "0.31.0" -opentelemetry_sdk = { version = "0.31.0", features = [ - "logs", - "trace", - "experimental_async_runtime", - "experimental_logs_batch_log_processor_with_async_runtime", - "experimental_trace_batch_span_processor_with_async_runtime", -] } +opentelemetry_sdk = { version = "0.31.0", features = ["logs", "trace", "experimental_async_runtime", "experimental_logs_batch_log_processor_with_async_runtime", "experimental_trace_batch_span_processor_with_async_runtime"] } papaya = "0.2.3" parquet = "57.3.0" partitions = { path = "core/partitions" } @@ -248,13 +223,7 @@ server = { path = "core/server" } simd-json = { version = "0.17.0", features = ["serde_impl"] } slab = "0.4.12" socket2 = "0.6.3" -sqlx = { version = "0.8.6", features = [ - "runtime-tokio-rustls", - "postgres", - "chrono", - "uuid", - "json", -] } +sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid", "json"] } static-toml = "1.3.0" strum = { version = "0.28.0", features = ["derive"] } strum_macros = "0.28.0" @@ -273,11 +242,7 @@ tower-http = { version = "0.6.8", features = ["add-extension", "cors", "trace"] tracing = "0.1.44" tracing-appender = "0.2.4" tracing-opentelemetry = "0.32.1" -tracing-subscriber = { version = "0.3.22", default-features = false, features = [ - "fmt", - "env-filter", - "ansi", -] } +tracing-subscriber = { version = "0.3.22", default-features = false, features = ["fmt", "env-filter", "ansi"] } trait-variant = "0.1.2" tungstenite = "0.28.0" twox-hash = { version = "2.1.2", features = ["xxhash32"] } @@ -286,15 +251,7 @@ uuid = { version = "1.22.0", features = ["v4", "v7", "fast-rng", "serde", "zeroc vergen-git2 = { version = "9.1.0", features = ["build", "cargo", "rustc", "si"] } walkdir = "2.5.0" wasm-bindgen = "0.2" -web-sys = { version = "0.3", features = [ - "Window", - "Location", - "HtmlSelectElement", - "Clipboard", - "Navigator", - "ResizeObserver", - "ResizeObserverEntry", -] } +web-sys = { version = "0.3", features = ["Window", "Location", "HtmlSelectElement", "Clipboard", "Navigator", "ResizeObserver", "ResizeObserverEntry"] } webpki-roots = "1.0.6" yew = { version = "0.22", features = ["csr"] } yew-router = "0.19" diff --git a/core/ai/mcp/Cargo.toml b/core/ai/mcp/Cargo.toml index 071e8ae36..97a2d76fb 100644 --- a/core/ai/mcp/Cargo.toml +++ b/core/ai/mcp/Cargo.toml @@ -41,11 +41,7 @@ opentelemetry-appender-tracing = { workspace = true } opentelemetry-otlp = { workspace = true } opentelemetry-semantic-conventions = { workspace = true } opentelemetry_sdk = { workspace = true, features = ["rt-tokio"] } -rmcp = { workspace = true, features = [ - "server", - "transport-io", - "transport-streamable-http-server", -] } +rmcp = { workspace = true, features = ["server", "transport-io", "transport-streamable-http-server"] } serde = { workspace = true } serde_json = { workspace = true } socket2 = { workspace = true } diff --git a/core/integration/Cargo.toml b/core/integration/Cargo.toml index 9851e5be0..59c24537d 100644 --- a/core/integration/Cargo.toml +++ b/core/integration/Cargo.toml @@ -55,12 +55,7 @@ rcgen = { workspace = true } reqwest = { workspace = true } reqwest-middleware = { workspace = true } reqwest-retry = { workspace = true } -rmcp = { workspace = true, features = [ - "client", - "reqwest", - "transport-streamable-http-client", - "transport-streamable-http-client-reqwest", -] } +rmcp = { workspace = true, features = ["client", "reqwest", "transport-streamable-http-client", "transport-streamable-http-client-reqwest"] } secrecy = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/core/server/config.toml b/core/server/config.toml index 03d0135f8..b3be68e58 100644 --- a/core/server/config.toml +++ b/core/server/config.toml @@ -114,10 +114,12 @@ clock_skew = "5 s" not_before = "0 s" # Secret key for encoding JWTs. -encoding_secret = "top_secret$iggy123$_jwt_HS256_key#!" +# If left empty, a secure random secret will be generated on each server start. +encoding_secret = "" # Secret key for decoding JWTs. -decoding_secret = "top_secret$iggy123$_jwt_HS256_key#!" +# If left empty, a secure random secret will be generated on each server start. +decoding_secret = "" # Indicates if the secret key is base64 encoded. # `true` means the secret is base64 encoded. diff --git a/core/server/src/http/http_server.rs b/core/server/src/http/http_server.rs index 4e71f3fe1..3ab781e13 100644 --- a/core/server/src/http/http_server.rs +++ b/core/server/src/http/http_server.rs @@ -30,6 +30,7 @@ use crate::shard::task_registry::ShutdownToken; use crate::shard::tasks::periodic::spawn_jwt_token_cleaner; use crate::shard::transmission::event::ShardEvent; use crate::streaming::persistence::persister::PersisterKind; +use crate::streaming::utils::crypto; use axum::extract::DefaultBodyLimit; use axum::extract::connect_info::Connected; use axum::http::Method; @@ -45,7 +46,7 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; use tower_http::cors::{AllowOrigin, CorsLayer}; -use tracing::{error, info}; +use tracing::{error, info, warn}; #[derive(Debug, Clone, Copy)] pub struct CompioSocketAddr(pub SocketAddr); @@ -268,12 +269,30 @@ async fn build_app_state( tokens_path = shard.config.system.get_state_tokens_path(); } - let jwt_manager = JwtManager::from_config(persister, &tokens_path, &config.jwt); - if let Err(e) = jwt_manager { - panic!("Failed to initialize JWT manager: {e}"); + let mut jwt_config = config.jwt.clone(); + let encoding_empty = jwt_config.encoding_secret.is_empty(); + let decoding_empty = jwt_config.decoding_secret.is_empty(); + if encoding_empty || decoding_empty { + let secret = crypto::generate_secret(32..64); + let redacted = secret.chars().take(3).collect::<String>(); + if encoding_empty { + jwt_config.encoding_secret = secret.clone(); + warn!( + "JWT encoding secret is not configured - generated a random secret: {redacted}***. JWT tokens will be invalidated on server restart. Set 'encoding_secret' in the config to use a persistent secret." + ); + } + if decoding_empty { + jwt_config.decoding_secret = secret; + warn!( + "JWT decoding secret is not configured - generated a random secret: {redacted}***. JWT tokens will be invalidated on server restart. Set 'decoding_secret' in the config to use a persistent secret." + ); + } } - let jwt_manager = jwt_manager.unwrap(); + let jwt_manager = match JwtManager::from_config(persister, &tokens_path, &jwt_config) { + Ok(manager) => manager, + Err(error) => panic!("Failed to initialize JWT manager: {error}"), + }; if jwt_manager.load_revoked_tokens().await.is_err() { panic!("Failed to load revoked access tokens"); }
