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 48fa292f6ee17d419908f7f4e0d1e01e9ed9e5f6
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/configs/src/server_config/http.rs |  8 -----
 core/integration/Cargo.toml            |  7 +----
 core/server/config.toml                |  6 ++--
 core/server/src/http/http_server.rs    | 25 ++++++++++++++--
 6 files changed, 35 insertions(+), 72 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/configs/src/server_config/http.rs 
b/core/configs/src/server_config/http.rs
index b19b53cae..f3c2956f7 100644
--- a/core/configs/src/server_config/http.rs
+++ b/core/configs/src/server_config/http.rs
@@ -115,10 +115,6 @@ impl HttpJwtConfig {
     }
 
     pub fn get_decoding_key(&self) -> Result<DecodingKey, IggyError> {
-        if self.decoding_secret.is_empty() {
-            return Err(IggyError::InvalidJwtSecret);
-        }
-
         Ok(match self.get_decoding_secret() {
             JwtSecret::Default(ref secret) => 
DecodingKey::from_secret(secret.as_ref()),
             JwtSecret::Base64(ref secret) => {
@@ -128,10 +124,6 @@ impl HttpJwtConfig {
     }
 
     pub fn get_encoding_key(&self) -> Result<EncodingKey, IggyError> {
-        if self.encoding_secret.is_empty() {
-            return Err(IggyError::InvalidJwtSecret);
-        }
-
         Ok(match self.get_encoding_secret() {
             JwtSecret::Default(ref secret) => 
EncodingKey::from_secret(secret.as_ref()),
             JwtSecret::Base64(ref secret) => {
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..d83e401e3 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,7 +269,27 @@ 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);
+    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 = JwtManager::from_config(persister, &tokens_path, 
&jwt_config);
     if let Err(e) = jwt_manager {
         panic!("Failed to initialize JWT manager: {e}");
     }

Reply via email to