From c957d023a2a55c3750210803d31c56f7c00cb95e Mon Sep 17 00:00:00 2001 From: JanKaul Date: Thu, 6 Nov 2025 14:56:08 +0100 Subject: [PATCH 1/2] add bearer token authorization check --- Cargo.lock | 12 +++++++++ crates/api-iceberg-rest/Cargo.toml | 1 + crates/api-iceberg-rest/src/auth.rs | 39 ++++++++++++++++++++++++++++ crates/api-iceberg-rest/src/lib.rs | 1 + crates/api-iceberg-rest/src/state.rs | 13 ++++++++-- crates/embucketd/src/cli.rs | 8 ++++++ crates/embucketd/src/main.rs | 12 +++++++-- 7 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 crates/api-iceberg-rest/src/auth.rs diff --git a/Cargo.lock b/Cargo.lock index 14537ff19..64b9886af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,6 +228,7 @@ name = "api-iceberg-rest" version = "0.1.0" dependencies = [ "axum 0.8.6", + "axum-auth", "core-metastore", "core-utils", "error-stack", @@ -1554,6 +1555,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-auth" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93495037c01c639b198ecb926b58f7f1c0d61ae663edcd61b2dd679f2a0bffe6" +dependencies = [ + "axum-core 0.5.5", + "base64 0.22.1", + "http 1.3.1", +] + [[package]] name = "axum-core" version = "0.4.5" diff --git a/crates/api-iceberg-rest/Cargo.toml b/crates/api-iceberg-rest/Cargo.toml index f54ca1570..fd17b3028 100644 --- a/crates/api-iceberg-rest/Cargo.toml +++ b/crates/api-iceberg-rest/Cargo.toml @@ -11,6 +11,7 @@ error-stack-trace = { path = "../error-stack-trace" } error-stack = { path = "../error-stack" } axum = { workspace = true } +axum-auth = "0.8" http = { workspace = true } iceberg-rest-catalog = { workspace = true } iceberg-rust = { workspace = true } diff --git a/crates/api-iceberg-rest/src/auth.rs b/crates/api-iceberg-rest/src/auth.rs new file mode 100644 index 000000000..bf33d0698 --- /dev/null +++ b/crates/api-iceberg-rest/src/auth.rs @@ -0,0 +1,39 @@ +use axum::{ + extract::{Request, State}, + http::StatusCode, + middleware::Next, + response::{IntoResponse, Response}, +}; + +use crate::state::State as AppState; + +/// Middleware to validate Bearer token authorization for Iceberg REST API +/// +/// If a bearer token is configured in the state, validates incoming requests +/// against it. Requests without proper authorization return 401 Unauthorized. +/// If no bearer token is configured, all requests are allowed through. +pub async fn require_auth(State(state): State, req: Request, next: Next) -> Response { + // If no bearer token is configured, allow all requests + let Some(configured_token) = &state.bearer_token else { + return next.run(req).await; + }; + + // Extract the bearer token from the Authorization header + let auth_header = req.headers().get(axum::http::header::AUTHORIZATION); + + let provided_token = if let Some(auth_header) = auth_header { + if let Ok(auth_str) = auth_header.to_str() { + auth_str.strip_prefix("Bearer ").map(|s| s.to_string()) + } else { + None + } + } else { + None + }; + + match provided_token { + Some(token) if &token == configured_token => next.run(req).await, + Some(_) => (StatusCode::UNAUTHORIZED, "Invalid bearer token").into_response(), + None => (StatusCode::UNAUTHORIZED, "Missing Authorization header").into_response(), + } +} diff --git a/crates/api-iceberg-rest/src/lib.rs b/crates/api-iceberg-rest/src/lib.rs index e89367a8a..470678d85 100644 --- a/crates/api-iceberg-rest/src/lib.rs +++ b/crates/api-iceberg-rest/src/lib.rs @@ -1,3 +1,4 @@ +pub mod auth; pub mod error; pub mod handlers; pub mod router; diff --git a/crates/api-iceberg-rest/src/state.rs b/crates/api-iceberg-rest/src/state.rs index fa3831a7a..1d1fecfd2 100644 --- a/crates/api-iceberg-rest/src/state.rs +++ b/crates/api-iceberg-rest/src/state.rs @@ -12,11 +12,20 @@ pub struct Config { pub struct State { pub metastore: Arc, pub config: Arc, + pub bearer_token: Option, } impl State { // You can add helper methods for state initialization if needed - pub fn new(metastore: Arc, config: Arc) -> Self { - Self { metastore, config } + pub fn new( + metastore: Arc, + config: Arc, + bearer_token: Option, + ) -> Self { + Self { + metastore, + config, + bearer_token, + } } } diff --git a/crates/embucketd/src/cli.rs b/crates/embucketd/src/cli.rs index 6163fcf23..9db5256cb 100644 --- a/crates/embucketd/src/cli.rs +++ b/crates/embucketd/src/cli.rs @@ -241,6 +241,14 @@ pub struct CliOpts { )] jwt_secret: Option, + #[arg( + long, + env = "ICEBERG_REST_BEARER_TOKEN", + hide_env_values = true, + help = "Bearer token for Iceberg REST API authorization" + )] + pub iceberg_rest_bearer_token: Option, + #[arg( long, env = "AUTH_DEMO_USER", diff --git a/crates/embucketd/src/main.rs b/crates/embucketd/src/main.rs index 2f8635043..1e3a3b420 100644 --- a/crates/embucketd/src/main.rs +++ b/crates/embucketd/src/main.rs @@ -5,6 +5,7 @@ pub(crate) mod cli; pub(crate) mod helpers; pub(crate) mod layers; +use api_iceberg_rest::auth::require_auth as iceberg_require_auth; use api_iceberg_rest::router::create_router as create_iceberg_router; use api_iceberg_rest::state::Config as IcebergConfig; use api_iceberg_rest::state::State as IcebergAppState; @@ -258,10 +259,17 @@ async fn async_main( .with_state(snowflake_state.clone()) .layer(compression_layer); let snowflake_router = snowflake_router.merge(snowflake_auth_router); - let iceberg_router = create_iceberg_router().with_state(IcebergAppState { + let iceberg_state = IcebergAppState { metastore: metastore.clone(), config: Arc::new(iceberg_config), - }); + bearer_token: opts.iceberg_rest_bearer_token.clone(), + }; + let iceberg_router = create_iceberg_router() + .with_state(iceberg_state.clone()) + .layer(middleware::from_fn_with_state( + iceberg_state, + iceberg_require_auth, + )); // --- OpenAPI specs --- let mut spec = ApiDoc::openapi(); From 28d5d7594c80772699839092605e6681e1533364 Mon Sep 17 00:00:00 2001 From: JanKaul Date: Fri, 7 Nov 2025 16:15:19 +0100 Subject: [PATCH 2/2] remove acum auth --- Cargo.lock | 12 ------------ crates/api-iceberg-rest/Cargo.toml | 1 - 2 files changed, 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64b9886af..14537ff19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,7 +228,6 @@ name = "api-iceberg-rest" version = "0.1.0" dependencies = [ "axum 0.8.6", - "axum-auth", "core-metastore", "core-utils", "error-stack", @@ -1555,17 +1554,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-auth" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93495037c01c639b198ecb926b58f7f1c0d61ae663edcd61b2dd679f2a0bffe6" -dependencies = [ - "axum-core 0.5.5", - "base64 0.22.1", - "http 1.3.1", -] - [[package]] name = "axum-core" version = "0.4.5" diff --git a/crates/api-iceberg-rest/Cargo.toml b/crates/api-iceberg-rest/Cargo.toml index fd17b3028..f54ca1570 100644 --- a/crates/api-iceberg-rest/Cargo.toml +++ b/crates/api-iceberg-rest/Cargo.toml @@ -11,7 +11,6 @@ error-stack-trace = { path = "../error-stack-trace" } error-stack = { path = "../error-stack" } axum = { workspace = true } -axum-auth = "0.8" http = { workspace = true } iceberg-rest-catalog = { workspace = true } iceberg-rust = { workspace = true }