diff --git a/config/quickwit.yaml b/config/quickwit.yaml index 7072c569fc0..1d03988b737 100644 --- a/config/quickwit.yaml +++ b/config/quickwit.yaml @@ -150,3 +150,9 @@ indexer: jaeger: enable_endpoint: ${QW_ENABLE_JAEGER_ENDPOINT:-true} + +license: ${QW_LICENSE} + +# authorization: +# root_key: ${QW_ROOT_KEY} +# node_token: ${QW_NODE_TOKEN} diff --git a/quickwit/Cargo.lock b/quickwit/Cargo.lock index 905e999110a..d1df21bb5e7 100644 --- a/quickwit/Cargo.lock +++ b/quickwit/Cargo.lock @@ -1684,6 +1684,26 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "tiny-keccak", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -5951,6 +5971,7 @@ dependencies = [ name = "quickwit-authorize" version = "0.8.0" dependencies = [ + "anyhow", "biscuit-auth", "futures", "http 0.2.12", @@ -5959,6 +5980,7 @@ dependencies = [ "serde", "thiserror", "tokio", + "tokio-inherit-task-local", "tonic", "tower", "tracing", @@ -6134,6 +6156,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", + "tokio-inherit-task-local", "tokio-metrics", "tokio-stream", "tonic", @@ -6158,6 +6181,7 @@ dependencies = [ "json_comments", "new_string_template", "once_cell", + "quickwit-authorize", "quickwit-common", "quickwit-doc-mapper", "quickwit-license", @@ -6621,6 +6645,7 @@ version = "0.8.0" dependencies = [ "anyhow", "async-trait", + "biscuit-auth", "bytes", "bytesize", "bytestring", @@ -6772,6 +6797,7 @@ dependencies = [ "prost 0.11.9", "prost-types 0.11.9", "quickwit-actors", + "quickwit-authorize", "quickwit-cluster", "quickwit-common", "quickwit-config", @@ -8888,6 +8914,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tokio-inherit-task-local" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d42db185acdff44279cff7f8765608129ae4a01a2f955008a4f96054c75e77ac" +dependencies = [ + "const-random", + "tokio", +] + [[package]] name = "tokio-io-timeout" version = "1.2.0" diff --git a/quickwit/Cargo.toml b/quickwit/Cargo.toml index c8878d3c515..f2578232b1e 100644 --- a/quickwit/Cargo.toml +++ b/quickwit/Cargo.toml @@ -240,6 +240,7 @@ tikv-jemalloc-ctl = "0.5" tikv-jemallocator = "0.5" time = { version = "0.3", features = ["std", "formatting", "macros"] } tokio = { version = "1.40", features = ["full"] } +tokio-inherit-task-local = "0.2" tokio-metrics = { version = "0.3.1", features = ["rt"] } tokio-stream = { version = "0.1", features = ["sync"] } tokio-util = { version = "0.7", features = ["full"] } diff --git a/quickwit/quickwit-authorize/Cargo.toml b/quickwit/quickwit-authorize/Cargo.toml index 4c3985fcff3..1b21761a408 100644 --- a/quickwit/quickwit-authorize/Cargo.toml +++ b/quickwit/quickwit-authorize/Cargo.toml @@ -9,10 +9,12 @@ authors.workspace = true license.workspace = true [dependencies] +anyhow = { workspace = true, optional = true } tower = { workspace = true} biscuit-auth = { workspace = true, optional=true } futures = { workspace = true } http = { workspace = true } +tokio-inherit-task-local = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } tonic = { workspace = true } @@ -23,4 +25,4 @@ pin-project = { workspace = true } quickwit-common = { workspace = true } [features] -enterprise = ["biscuit-auth"] +enterprise = ["dep:biscuit-auth", "dep:anyhow"] diff --git a/quickwit/quickwit-authorize/src/community.rs b/quickwit/quickwit-authorize/src/community/mod.rs similarity index 92% rename from quickwit/quickwit-authorize/src/community.rs rename to quickwit/quickwit-authorize/src/community/mod.rs index 48576639c7f..0fd7c0b85ca 100644 --- a/quickwit/quickwit-authorize/src/community.rs +++ b/quickwit/quickwit-authorize/src/community/mod.rs @@ -44,7 +44,7 @@ pub trait StreamAuthorization { impl StreamAuthorization for T {} -pub fn get_auth_token( +pub fn extract_auth_token( _req_metadata: &tonic::metadata::MetadataMap, ) -> Result { Ok(()) @@ -63,12 +63,6 @@ pub fn authorize( Ok(()) } -pub fn build_tonic_stream_request_with_auth_token( - req: R, -) -> Result, AuthorizationError> { - Ok(tonic::Request::new(req)) -} - pub fn build_tonic_request_with_auth_token( req: R, ) -> Result, AuthorizationError> { diff --git a/quickwit/quickwit-authorize/src/authorization_layer.rs b/quickwit/quickwit-authorize/src/enterprise/authorization_layer.rs similarity index 63% rename from quickwit/quickwit-authorize/src/authorization_layer.rs rename to quickwit/quickwit-authorize/src/enterprise/authorization_layer.rs index 3131bef4715..ae29555ee02 100644 --- a/quickwit/quickwit-authorize/src/authorization_layer.rs +++ b/quickwit/quickwit-authorize/src/enterprise/authorization_layer.rs @@ -1,3 +1,22 @@ +// Copyright (C) 2024 Quickwit, Inc. +// +// Quickwit is offered under the AGPL v3.0 and as commercial software. +// For commercial licensing, contact us at hello@quickwit.io. +// +// AGPL: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + use std::fmt; use std::task::{Context, Poll}; @@ -7,6 +26,7 @@ use tower::{Layer, Service}; use crate::AuthorizationError; +#[derive(Clone, Copy, Debug)] pub struct AuthorizationLayer; impl Layer for AuthorizationLayer { diff --git a/quickwit/quickwit-authorize/src/enterprise/authorization_token_extraction_layer.rs b/quickwit/quickwit-authorize/src/enterprise/authorization_token_extraction_layer.rs new file mode 100644 index 00000000000..a2a9b08bfef --- /dev/null +++ b/quickwit/quickwit-authorize/src/enterprise/authorization_token_extraction_layer.rs @@ -0,0 +1,74 @@ +// Copyright (C) 2024 Quickwit, Inc. +// +// Quickwit is offered under the AGPL v3.0 and as commercial software. +// For commercial licensing, contact us at hello@quickwit.io. +// +// AGPL: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use std::task::{Context, Poll}; + +use futures::future::Either; +use http::Request; +use tokio::task::futures::TaskLocalFuture; +use tokio_inherit_task_local::TaskLocalInheritableTable; +use tower::{Layer, Service}; +use tracing::debug; + +use super::AuthorizationToken; + +#[derive(Clone, Copy, Debug)] +pub struct AuthorizationTokenExtractionLayer; + +impl Layer for AuthorizationTokenExtractionLayer { + type Service = AuthorizationTokenExtractionService; + + fn layer(&self, service: S) -> Self::Service { + AuthorizationTokenExtractionService { service } + } +} + +#[derive(Clone)] +pub struct AuthorizationTokenExtractionService { + service: S, +} + +fn get_authorization_token_opt(headers: &http::HeaderMap) -> Option { + let authorization_header_value = headers.get("Authorization")?; + let authorization_header_str = authorization_header_value.to_str().ok()?; + crate::get_auth_token_from_str(authorization_header_str).ok() +} + +impl Service> for AuthorizationTokenExtractionService +where S: Service> +{ + type Response = S::Response; + type Error = S::Error; + type Future = Either>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) + } + + fn call(&mut self, request: Request) -> Self::Future { + let authorization_token_opt = get_authorization_token_opt(request.headers()); + debug!(authorization_token_opt = ?authorization_token_opt, "Authorization token extracted"); + let fut = self.service.call(request); + if let Some(authorization_token) = authorization_token_opt { + Either::Right(crate::execute_with_authorization(authorization_token, fut)) + } else { + Either::Left(fut) + } + } +} diff --git a/quickwit/quickwit-authorize/src/enterprise.rs b/quickwit/quickwit-authorize/src/enterprise/mod.rs similarity index 76% rename from quickwit/quickwit-authorize/src/enterprise.rs rename to quickwit/quickwit-authorize/src/enterprise/mod.rs index e1aa02e4436..b8c64a5abf1 100644 --- a/quickwit/quickwit-authorize/src/enterprise.rs +++ b/quickwit/quickwit-authorize/src/enterprise/mod.rs @@ -19,26 +19,43 @@ // components are licensed under the original license provided by the owner of the // applicable component. +mod authorization_layer; +mod authorization_token_extraction_layer; + use std::future::Future; use std::str::FromStr; use std::sync::{Arc, OnceLock}; +use anyhow::Context; +pub use authorization_layer::AuthorizationLayer; +pub use authorization_token_extraction_layer::AuthorizationTokenExtractionLayer; use biscuit_auth::macros::authorizer; use biscuit_auth::{Authorizer, Biscuit, RootKeyProvider}; +use tokio::task::futures::TaskLocalFuture; +use tokio_inherit_task_local::TaskLocalInheritableTable; +use tracing::info; use crate::AuthorizationError; -pub struct AuthorizationToken(Biscuit); +tokio_inherit_task_local::inheritable_task_local! { + pub static AUTHORIZATION_TOKEN: AuthorizationToken; +} + +static ROOT_KEY_PROVIDER: OnceLock> = OnceLock::new(); +static NODE_TOKEN: OnceLock = OnceLock::new(); + +#[derive(Clone)] +pub struct AuthorizationToken(Arc); impl AuthorizationToken { - pub fn into_biscuit(self) -> Biscuit { - self.0 + pub fn into_biscuit(self) -> Arc { + self.0.clone() } } impl From for AuthorizationToken { fn from(biscuit: Biscuit) -> Self { - AuthorizationToken(biscuit) + AuthorizationToken(Arc::new(biscuit)) } } @@ -54,7 +71,22 @@ impl std::fmt::Debug for AuthorizationToken { } } -static ROOT_KEY_PROVIDER: OnceLock> = OnceLock::new(); +pub fn set_node_token_hex(node_token_hex: &str) -> anyhow::Result<()> { + let node_token = + AuthorizationToken::from_str(node_token_hex).context("failed to set node token")?; + if NODE_TOKEN.set(node_token).is_err() { + tracing::error!("node token was already initialized"); + } + Ok(()) +} + +pub fn set_root_public_key(root_key_hex: &str) -> anyhow::Result<()> { + let public_key = biscuit_auth::PublicKey::from_bytes_hex(root_key_hex) + .context("failed to parse root public key")?; + let key_provider: Arc = Arc::new(public_key); + set_root_key_provider(key_provider); + Ok(()) +} pub fn set_root_key_provider(key_provider: Arc) { if ROOT_KEY_PROVIDER.set(key_provider).is_err() { @@ -75,14 +107,10 @@ impl FromStr for AuthorizationToken { fn from_str(token_base64: &str) -> Result { let root_key_provider = get_root_key_provider(); let biscuit = Biscuit::from_base64(token_base64, root_key_provider)?; - Ok(AuthorizationToken(biscuit)) + Ok(AuthorizationToken::from(biscuit)) } } -tokio::task_local! { - pub static AUTHORIZATION_TOKEN: AuthorizationToken; -} - const AUTHORIZATION_VALUE_PREFIX: &str = "Bearer "; fn default_operation_authorizer( @@ -146,7 +174,17 @@ impl From for AuthorizationError { } } -pub fn get_auth_token( +pub fn get_auth_token_from_str( + authorization_header_value: &str, +) -> Result { + let authorization_token_str: &str = authorization_header_value + .strip_prefix(AUTHORIZATION_VALUE_PREFIX) + .ok_or(AuthorizationError::InvalidToken)?; + let biscuit: Biscuit = Biscuit::from_base64(authorization_token_str, get_root_key_provider())?; + Ok(AuthorizationToken::from(biscuit)) +} + +pub fn extract_auth_token( req_metadata: &tonic::metadata::MetadataMap, ) -> Result { let authorization_header_value: &str = req_metadata @@ -154,11 +192,7 @@ pub fn get_auth_token( .ok_or(AuthorizationError::AuthorizationTokenMissing)? .to_str() .map_err(|_| AuthorizationError::InvalidToken)?; - let authorization_token_str: &str = authorization_header_value - .strip_prefix(AUTHORIZATION_VALUE_PREFIX) - .ok_or(AuthorizationError::InvalidToken)?; - let biscuit: Biscuit = Biscuit::from_base64(authorization_token_str, get_root_key_provider())?; - Ok(AuthorizationToken(biscuit)) + get_auth_token_from_str(authorization_header_value) } pub fn set_auth_token( @@ -182,28 +216,22 @@ pub fn authorize( Ok(()) } -pub fn build_tonic_stream_request_with_auth_token( - req: R, -) -> Result, AuthorizationError> { +fn get_auth_token() -> Option { AUTHORIZATION_TOKEN - .try_with(|token| { - let mut request = tonic::Request::new(req); - set_auth_token(token, request.metadata_mut()); - Ok(request) - }) - .unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing)) + .try_with(|auth_token| auth_token.clone()) + .ok() + .or_else(|| NODE_TOKEN.get().cloned()) } -pub fn build_tonic_request_with_auth_token( +pub fn build_tonic_request_with_auth_token( req: R, ) -> Result, AuthorizationError> { - AUTHORIZATION_TOKEN - .try_with(|token| { - let mut request = tonic::Request::new(req); - set_auth_token(token, request.metadata_mut()); - Ok(request) - }) - .unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing)) + let Some(authorization_token) = get_auth_token() else { + return Err(AuthorizationError::AuthorizationTokenMissing); + }; + let mut tonic_request = tonic::Request::new(req); + set_auth_token(&authorization_token, tonic_request.metadata_mut()); + Ok(tonic_request) } pub fn authorize_stream( @@ -216,15 +244,17 @@ pub fn authorize_stream( } pub fn authorize_request(req: &R) -> Result<(), AuthorizationError> { - AUTHORIZATION_TOKEN + let res = AUTHORIZATION_TOKEN .try_with(|auth_token| authorize(req, auth_token)) - .unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing)) + .unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing)); + info!("request authorization"); + res } pub fn execute_with_authorization( token: AuthorizationToken, f: F, -) -> impl Future +) -> TaskLocalFuture where F: Future, { @@ -248,7 +278,7 @@ mod tests { #[test] fn test_auth_token_missing() { let req_metadata = tonic::metadata::MetadataMap::new(); - let missing_error = get_auth_token(&req_metadata).unwrap_err(); + let missing_error = extract_auth_token(&req_metadata).unwrap_err(); assert!(matches!( missing_error, AuthorizationError::AuthorizationTokenMissing @@ -262,7 +292,7 @@ mod tests { http::header::AUTHORIZATION.as_str(), "some_token".parse().unwrap(), ); - let missing_error = get_auth_token(&req_metadata).unwrap_err(); + let missing_error = extract_auth_token(&req_metadata).unwrap_err(); assert!(matches!(missing_error, AuthorizationError::InvalidToken)); } } diff --git a/quickwit/quickwit-authorize/src/lib.rs b/quickwit/quickwit-authorize/src/lib.rs index 23206c0b434..3e0a7bb5ca4 100644 --- a/quickwit/quickwit-authorize/src/lib.rs +++ b/quickwit/quickwit-authorize/src/lib.rs @@ -17,14 +17,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -mod authorization_layer; - #[cfg(not(feature = "enterprise"))] -#[path = "community.rs"] +#[path = "community/mod.rs"] mod implementation; #[cfg(feature = "enterprise")] -#[path = "enterprise.rs"] +#[path = "enterprise/mod.rs"] mod implementation; pub use implementation::*; diff --git a/quickwit/quickwit-cli/Cargo.toml b/quickwit/quickwit-cli/Cargo.toml index 64bf88dabda..ec528e64ea9 100644 --- a/quickwit/quickwit-cli/Cargo.toml +++ b/quickwit/quickwit-cli/Cargo.toml @@ -79,7 +79,7 @@ quickwit-metastore = { workspace = true, features = ["testsuite"] } quickwit-storage = { workspace = true, features = ["testsuite"] } [features] -enterprise = ["quickwit-config/enterprise", "quickwit-ingest/enterprise", "quickwit-proto/enterprise"] +enterprise = ["quickwit-config/enterprise", "quickwit-ingest/enterprise", "quickwit-proto/enterprise", "quickwit-serve/enterprise"] jemalloc = ["dep:tikv-jemalloc-ctl", "dep:tikv-jemallocator"] ci-test = [] pprof = ["quickwit-serve/pprof"] diff --git a/quickwit/quickwit-codegen/example/src/authorization.rs b/quickwit/quickwit-codegen/example/src/authorization.rs index 509fed82f0f..1d0a000066a 100644 --- a/quickwit/quickwit-codegen/example/src/authorization.rs +++ b/quickwit/quickwit-codegen/example/src/authorization.rs @@ -15,7 +15,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use quickwit_authorize::{Authorization, AuthorizationError, AuthorizationToken, StreamAuthorization}; +use quickwit_authorize::{ + Authorization, AuthorizationError, AuthorizationToken, StreamAuthorization, +}; use crate::{GoodbyeRequest, HelloRequest, PingRequest}; @@ -38,9 +40,7 @@ impl Authorization for GoodbyeRequest { } impl StreamAuthorization for PingRequest { - fn attenuate( - auth_token: AuthorizationToken, - ) -> Result { + fn attenuate(auth_token: AuthorizationToken) -> Result { Ok(auth_token) } } diff --git a/quickwit/quickwit-codegen/example/src/codegen/hello.rs b/quickwit/quickwit-codegen/example/src/codegen/hello.rs index 4eb3a2dc8c7..04cf2d4dabf 100644 --- a/quickwit/quickwit-codegen/example/src/codegen/hello.rs +++ b/quickwit/quickwit-codegen/example/src/codegen/hello.rs @@ -757,7 +757,7 @@ where &self, request: quickwit_common::ServiceStream, ) -> crate::HelloResult> { - let tonic_request = quickwit_authorize::build_tonic_stream_request_with_auth_token( + let tonic_request = quickwit_authorize::build_tonic_request_with_auth_token( request, )?; self.inner @@ -814,7 +814,7 @@ impl hello_grpc_server::HelloGrpc for HelloGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.hello(request.into_inner()), @@ -827,7 +827,7 @@ impl hello_grpc_server::HelloGrpc for HelloGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.goodbye(request.into_inner()), @@ -841,7 +841,7 @@ impl hello_grpc_server::HelloGrpc for HelloGrpcServerAdapter { &self, request: tonic::Request>, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self diff --git a/quickwit/quickwit-codegen/src/codegen.rs b/quickwit/quickwit-codegen/src/codegen.rs index 4143cafd15b..5db1f10ac4b 100644 --- a/quickwit/quickwit-codegen/src/codegen.rs +++ b/quickwit/quickwit-codegen/src/codegen.rs @@ -1170,7 +1170,7 @@ fn generate_grpc_client_adapter_methods(context: &CodegenContext) -> TokenStream let method = if syn_method.client_streaming { quote! { async fn #method_name(&self, request: #request_type) -> #result_type<#response_type> { - let tonic_request = quickwit_authorize::build_tonic_stream_request_with_auth_token(request)?; + let tonic_request = quickwit_authorize::build_tonic_request_with_auth_token(request)?; self.inner .clone() .#method_name(tonic_request) @@ -1273,7 +1273,7 @@ fn generate_grpc_server_adapter_methods(context: &CodegenContext) -> TokenStream #associated_type async fn #method_name(&self, request: tonic::Request<#request_type>) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization(auth_token, self.inner.0.#method_name(#method_arg)).await .map(#into_response_type) .map_err(crate::error::grpc_error_to_grpc_status) diff --git a/quickwit/quickwit-common/Cargo.toml b/quickwit/quickwit-common/Cargo.toml index 83170a8ec56..fc2d579e0f4 100644 --- a/quickwit/quickwit-common/Cargo.toml +++ b/quickwit/quickwit-common/Cargo.toml @@ -39,6 +39,7 @@ thiserror = { workspace = true } tokio = { workspace = true } tokio-metrics = { workspace = true } tokio-stream = { workspace = true } +tokio-inherit-task-local = { workspace = true } tonic = { workspace = true } tower = { workspace = true } tracing = { workspace = true } diff --git a/quickwit/quickwit-common/src/lib.rs b/quickwit/quickwit-common/src/lib.rs index dff26829584..a1712a4105e 100644 --- a/quickwit/quickwit-common/src/lib.rs +++ b/quickwit/quickwit-common/src/lib.rs @@ -213,6 +213,15 @@ pub fn num_cpus() -> usize { } } +pub fn spawn_inherit_task_local(future: F) -> tokio::task::JoinHandle +where + F: Future + Send + 'static, + F::Output: Send + 'static, +{ + use tokio_inherit_task_local::FutureInheritTaskLocal; + tokio::task::spawn(future.inherit_task_local()) +} + // The following are helpers to build named tasks. // // Named tasks require the tokio feature `tracing` to be enabled. diff --git a/quickwit/quickwit-common/src/tower/one_task_per_call_layer.rs b/quickwit/quickwit-common/src/tower/one_task_per_call_layer.rs index caf7ca3cdec..a4bfa33825c 100644 --- a/quickwit/quickwit-common/src/tower/one_task_per_call_layer.rs +++ b/quickwit/quickwit-common/src/tower/one_task_per_call_layer.rs @@ -77,7 +77,7 @@ where fn call(&mut self, request: Request) -> Self::Future { let request_name: &'static str = Request::rpc_name(); let future = self.service.call(request); - let join_handle = tokio::spawn(future); + let join_handle = crate::spawn_inherit_task_local(future); UnwrapOrElseFuture { request_name, join_handle, diff --git a/quickwit/quickwit-config/Cargo.toml b/quickwit/quickwit-config/Cargo.toml index 67309baf13c..0f51a309f9d 100644 --- a/quickwit/quickwit-config/Cargo.toml +++ b/quickwit/quickwit-config/Cargo.toml @@ -35,6 +35,7 @@ tracing = { workspace = true } utoipa = { workspace = true } vrl = { workspace = true, optional = true } +quickwit-authorize = { workspace = true, optional = true } quickwit-common = { workspace = true } quickwit-doc-mapper = { workspace = true } quickwit-license = { workspace = true, optional = true } @@ -49,4 +50,4 @@ quickwit-proto = { workspace = true, features = ["testsuite"] } [features] testsuite = [] vrl = ["dep:vrl"] -enterprise = ["quickwit-license"] +enterprise = ["dep:quickwit-authorize", "dep:quickwit-license", ] diff --git a/quickwit/quickwit-config/src/node_config/mod.rs b/quickwit/quickwit-config/src/node_config/mod.rs index 3eef1f10428..c9a8fab1e06 100644 --- a/quickwit/quickwit-config/src/node_config/mod.rs +++ b/quickwit/quickwit-config/src/node_config/mod.rs @@ -313,6 +313,13 @@ impl SearcherConfig { } } +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct AuthorizationConfigBuilder { + pub root_key: String, + pub node_token: String, +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(deny_unknown_fields, default)] pub struct IngestApiConfig { diff --git a/quickwit/quickwit-config/src/node_config/serialize.rs b/quickwit/quickwit-config/src/node_config/serialize.rs index 2a435ebed01..b56026c0de2 100644 --- a/quickwit/quickwit-config/src/node_config/serialize.rs +++ b/quickwit/quickwit-config/src/node_config/serialize.rs @@ -164,6 +164,15 @@ impl From for NodeConfigBuilder { } } +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct AuthorizationConfigBuilder { + #[serde(default)] + pub root_public_key: Option, + #[serde(default)] + pub node_token: Option, +} + #[serde_with::serde_as] #[derive(Debug, Deserialize, PartialEq)] #[serde(deny_unknown_fields)] @@ -215,6 +224,8 @@ struct NodeConfigBuilder { jaeger_config: JaegerConfig, #[serde(default)] license: Option, + #[serde(default)] + authorization: AuthorizationConfigBuilder, } impl NodeConfigBuilder { @@ -222,6 +233,12 @@ impl NodeConfigBuilder { mut self, env_vars: &HashMap, ) -> anyhow::Result { + #[cfg(feature = "enterprise")] + { + self.set_license(env_vars)?; + self.set_authorization_keys(env_vars)?; + } + let node_id = self.node_id.resolve(env_vars).map(NodeId::new)?; let enabled_services = self @@ -307,15 +324,6 @@ impl NodeConfigBuilder { .map(|gossip_interval_ms| Duration::from_millis(gossip_interval_ms as u64)) .unwrap_or(DEFAULT_GOSSIP_INTERVAL); - // Environment variable takes precedence for license too. - #[cfg(feature = "enterprise")] - if let Some(license_str) = env_vars.get("QW_LICENSE").or(self.license.as_ref()) { - if let Err(error) = quickwit_license::set_license(license_str) { - tracing::error!(error=?error, "invalid license"); - std::process::exit(1); - } - } - let node_config = NodeConfig { cluster_id: self.cluster_id.resolve(env_vars)?, node_id, @@ -344,6 +352,35 @@ impl NodeConfigBuilder { } } +#[cfg(feature = "enterprise")] +impl NodeConfigBuilder { + fn set_license(&self, env_vars: &HashMap) -> anyhow::Result<()> { + // Environment variable takes precedence for license too. + let Some(license_str) = env_vars.get("QW_LICENSE").or(self.license.as_ref()) else { + return Ok(()); + }; + if let Err(error) = quickwit_license::set_license(license_str) { + tracing::error!(error=?error, "invalid license"); + std::process::exit(1); + } + Ok(()) + } + + fn set_authorization_keys(&self, env_vars: &HashMap) -> anyhow::Result<()> { + let root_public_key = env_vars + .get("QW_AUTH_ROOT_PUBLIC_KEY") + .or(self.authorization.root_public_key.as_ref()) + .context("root key undefined")?; + quickwit_authorize::set_root_public_key(root_public_key)?; + let node_token_hex = env_vars + .get("QW_AUTH_NODE_TOKEN") + .or(self.authorization.node_token.as_ref()) + .context("root key undefined")?; + quickwit_authorize::set_node_token_hex(node_token_hex)?; + Ok(()) + } +} + fn validate(node_config: &NodeConfig) -> anyhow::Result<()> { validate_identifier("cluster", &node_config.cluster_id)?; validate_node_id(&node_config.node_id)?; diff --git a/quickwit/quickwit-ingest/src/authorize.rs b/quickwit/quickwit-ingest/src/authorize.rs index 57ad079867a..5e4470b9ee1 100644 --- a/quickwit/quickwit-ingest/src/authorize.rs +++ b/quickwit/quickwit-ingest/src/authorize.rs @@ -15,7 +15,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use quickwit_authorize::::{Authorization, AuthorizationError, AuthorizationToken}; +use quickwit_authorize::{Authorization, AuthorizationError, AuthorizationToken}; use crate::{FetchRequest, IngestRequest, TailRequest}; diff --git a/quickwit/quickwit-ingest/src/codegen/ingest_service.rs b/quickwit/quickwit-ingest/src/codegen/ingest_service.rs index 8b7e4b4c7ea..87dee751452 100644 --- a/quickwit/quickwit-ingest/src/codegen/ingest_service.rs +++ b/quickwit/quickwit-ingest/src/codegen/ingest_service.rs @@ -881,7 +881,7 @@ impl ingest_service_grpc_server::IngestServiceGrpc for IngestServiceGrpcServerAd &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.ingest(request.into_inner()), @@ -894,7 +894,7 @@ impl ingest_service_grpc_server::IngestServiceGrpc for IngestServiceGrpcServerAd &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.fetch(request.into_inner()), @@ -907,7 +907,7 @@ impl ingest_service_grpc_server::IngestServiceGrpc for IngestServiceGrpcServerAd &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.tail(request.into_inner()), diff --git a/quickwit/quickwit-proto/Cargo.toml b/quickwit/quickwit-proto/Cargo.toml index e76a7a539af..e6035ac7d55 100644 --- a/quickwit/quickwit-proto/Cargo.toml +++ b/quickwit/quickwit-proto/Cargo.toml @@ -12,6 +12,7 @@ license.workspace = true [dependencies] anyhow = { workspace = true } async-trait = { workspace = true } +biscuit-auth = { workspace = true, optional = true } bytes = { workspace = true } bytesize = { workspace = true } bytestring = { workspace = true } @@ -53,4 +54,4 @@ quickwit-codegen = { workspace = true } [features] postgres = ["sea-query", "sqlx"] testsuite = ["mockall", "futures"] -enterprise = [ "quickwit-authorize/enterprise"] +enterprise = [ "quickwit-authorize/enterprise", "dep:biscuit-auth"] diff --git a/quickwit/quickwit-proto/src/authorization.rs b/quickwit/quickwit-proto/src/authorization.rs index 54882b7cfbc..edf0ac68b7b 100644 --- a/quickwit/quickwit-proto/src/authorization.rs +++ b/quickwit/quickwit-proto/src/authorization.rs @@ -1,8 +1,11 @@ use std::time::{Duration, SystemTime}; -use biscuit_auth::builder_ext::BuilderExt; -use biscuit_auth::macros::*; -use quickwit_authorize::::{Authorization, AuthorizationError, AuthorizationToken, StreamAuthorization}; +pub use biscuit_auth; +pub use biscuit_auth::builder_ext::BuilderExt; +pub use biscuit_auth::macros::*; +use quickwit_authorize::{ + Authorization, AuthorizationError, AuthorizationToken, StreamAuthorization, +}; use crate::cluster::FetchClusterStateRequest; use crate::control_plane::{AdviseResetShardsRequest, GetOrCreateOpenShardsRequest}; diff --git a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.cluster.rs b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.cluster.rs index 28762ff59e5..1854e89acda 100644 --- a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.cluster.rs +++ b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.cluster.rs @@ -545,7 +545,7 @@ for ClusterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.fetch_cluster_state(request.into_inner()), diff --git a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.control_plane.rs b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.control_plane.rs index f1ddb3925fa..16cbae89527 100644 --- a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.control_plane.rs +++ b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.control_plane.rs @@ -1806,7 +1806,7 @@ for ControlPlaneServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.create_index(request.into_inner()), @@ -1822,7 +1822,7 @@ for ControlPlaneServiceGrpcServerAdapter { tonic::Response, tonic::Status, > { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.update_index(request.into_inner()), @@ -1835,7 +1835,7 @@ for ControlPlaneServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.delete_index(request.into_inner()), @@ -1848,7 +1848,7 @@ for ControlPlaneServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.add_source(request.into_inner()), @@ -1861,7 +1861,7 @@ for ControlPlaneServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.toggle_source(request.into_inner()), @@ -1874,7 +1874,7 @@ for ControlPlaneServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.delete_source(request.into_inner()), @@ -1887,7 +1887,7 @@ for ControlPlaneServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.get_or_create_open_shards(request.into_inner()), @@ -1900,7 +1900,7 @@ for ControlPlaneServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.advise_reset_shards(request.into_inner()), @@ -1913,7 +1913,7 @@ for ControlPlaneServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.prune_shards(request.into_inner()), diff --git a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.developer.rs b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.developer.rs index 5d99aaa7532..8c63b430a84 100644 --- a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.developer.rs +++ b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.developer.rs @@ -481,7 +481,7 @@ for DeveloperServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.get_debug_info(request.into_inner()), diff --git a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.indexing.rs b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.indexing.rs index 46c66feec09..f811651d007 100644 --- a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.indexing.rs +++ b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.indexing.rs @@ -494,7 +494,7 @@ for IndexingServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.apply_indexing_plan(request.into_inner()), diff --git a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.ingest.ingester.rs b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.ingest.ingester.rs index 1d8294510ec..1058fc155f0 100644 --- a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.ingest.ingester.rs +++ b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.ingest.ingester.rs @@ -2061,7 +2061,7 @@ where &self, request: quickwit_common::ServiceStream, ) -> crate::ingest::IngestV2Result> { - let tonic_request = quickwit_authorize::build_tonic_stream_request_with_auth_token( + let tonic_request = quickwit_authorize::build_tonic_request_with_auth_token( request, )?; self.inner @@ -2239,7 +2239,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.persist(request.into_inner()), @@ -2255,7 +2255,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request>, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self @@ -2279,7 +2279,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.open_fetch_stream(request.into_inner()), @@ -2297,7 +2297,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.open_observation_stream(request.into_inner()), @@ -2312,7 +2312,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.init_shards(request.into_inner()), @@ -2325,7 +2325,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.retain_shards(request.into_inner()), @@ -2338,7 +2338,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.truncate_shards(request.into_inner()), @@ -2351,7 +2351,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.close_shards(request.into_inner()), @@ -2364,7 +2364,7 @@ for IngesterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.decommission(request.into_inner()), diff --git a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.ingest.router.rs b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.ingest.router.rs index 3c99756ac7f..ddbe6ed5d6b 100644 --- a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.ingest.router.rs +++ b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.ingest.router.rs @@ -607,7 +607,7 @@ for IngestRouterServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.ingest(request.into_inner()), diff --git a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.metastore.rs b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.metastore.rs index 9b32e28bd91..aadd37c961c 100644 --- a/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.metastore.rs +++ b/quickwit/quickwit-proto/src/codegen/quickwit/quickwit.metastore.rs @@ -5417,7 +5417,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.create_index(request.into_inner()), @@ -5430,7 +5430,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.update_index(request.into_inner()), @@ -5443,7 +5443,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.index_metadata(request.into_inner()), @@ -5456,7 +5456,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.indexes_metadata(request.into_inner()), @@ -5469,7 +5469,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.list_indexes_metadata(request.into_inner()), @@ -5482,7 +5482,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.delete_index(request.into_inner()), @@ -5498,7 +5498,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.list_splits(request.into_inner()), @@ -5513,7 +5513,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.stage_splits(request.into_inner()), @@ -5526,7 +5526,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.publish_splits(request.into_inner()), @@ -5539,7 +5539,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.mark_splits_for_deletion(request.into_inner()), @@ -5552,7 +5552,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.delete_splits(request.into_inner()), @@ -5565,7 +5565,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.add_source(request.into_inner()), @@ -5578,7 +5578,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.toggle_source(request.into_inner()), @@ -5591,7 +5591,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.delete_source(request.into_inner()), @@ -5604,7 +5604,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.reset_source_checkpoint(request.into_inner()), @@ -5617,7 +5617,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.last_delete_opstamp(request.into_inner()), @@ -5630,7 +5630,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.create_delete_task(request.into_inner()), @@ -5643,7 +5643,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.update_splits_delete_opstamp(request.into_inner()), @@ -5656,7 +5656,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.list_delete_tasks(request.into_inner()), @@ -5669,7 +5669,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.list_stale_splits(request.into_inner()), @@ -5682,7 +5682,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.open_shards(request.into_inner()), @@ -5695,7 +5695,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.acquire_shards(request.into_inner()), @@ -5708,7 +5708,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.delete_shards(request.into_inner()), @@ -5721,7 +5721,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.prune_shards(request.into_inner()), @@ -5734,7 +5734,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.list_shards(request.into_inner()), @@ -5747,7 +5747,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.create_index_template(request.into_inner()), @@ -5760,7 +5760,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.get_index_template(request.into_inner()), @@ -5773,7 +5773,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.find_index_template_matches(request.into_inner()), @@ -5786,7 +5786,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.list_index_templates(request.into_inner()), @@ -5799,7 +5799,7 @@ for MetastoreServiceGrpcServerAdapter { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let auth_token = quickwit_authorize::get_auth_token(request.metadata())?; + let auth_token = quickwit_authorize::extract_auth_token(request.metadata())?; quickwit_authorize::execute_with_authorization( auth_token, self.inner.0.delete_index_templates(request.into_inner()), diff --git a/quickwit/quickwit-serve/Cargo.toml b/quickwit/quickwit-serve/Cargo.toml index b82db775761..f86ade32293 100644 --- a/quickwit/quickwit-serve/Cargo.toml +++ b/quickwit/quickwit-serve/Cargo.toml @@ -50,6 +50,7 @@ warp = { workspace = true } zstd = { workspace = true } quickwit-actors = { workspace = true } +quickwit-authorize = { workspace = true, features = ["enterprise"], optional = true } quickwit-cluster = { workspace = true } quickwit-common = { workspace = true } quickwit-config = { workspace = true } @@ -97,4 +98,5 @@ quickwit-storage = { workspace = true, features = ["testsuite"] } pprof = [ "dep:pprof" ] +enterprise = ["dep:quickwit-authorize"] testsuite = [] diff --git a/quickwit/quickwit-serve/src/lib.rs b/quickwit/quickwit-serve/src/lib.rs index 6a7a252a0cd..62a89fee7ab 100644 --- a/quickwit/quickwit-serve/src/lib.rs +++ b/quickwit/quickwit-serve/src/lib.rs @@ -429,10 +429,23 @@ pub async fn serve_quickwit( 100 }; // These layers apply to all the RPCs of the metastore. - let shared_layer = ServiceBuilder::new() + let shared_layer_builder = ServiceBuilder::new() .layer(METASTORE_GRPC_SERVER_METRICS_LAYER.clone()) - .layer(LoadShedLayer::new(max_in_flight_requests)) - .into_inner(); + .layer(LoadShedLayer::new(max_in_flight_requests)); + + let shared_layer; + + #[cfg(feature = "enterprise")] + { + use quickwit_authorize::AuthorizationLayer; + shared_layer = shared_layer_builder.layer(AuthorizationLayer).into_inner(); + } + + #[cfg(not(feature = "enterprise"))] + { + shared_layer = shared_layer_builder.into_inner(); + } + let broker_layer = EventListenerLayer::new(event_broker.clone()); let metastore = MetastoreServiceClient::tower() .stack_layer(shared_layer) diff --git a/quickwit/quickwit-serve/src/rest.rs b/quickwit/quickwit-serve/src/rest.rs index 3c83c2d84f1..2c79513e0f5 100644 --- a/quickwit/quickwit-serve/src/rest.rs +++ b/quickwit/quickwit-serve/src/rest.rs @@ -198,7 +198,7 @@ pub(crate) async fn start_rest_server( let compression_predicate = CompressionPredicate::from_env().and(NotForContentType::IMAGES); let cors = build_cors(&quickwit_services.node_config.rest_config.cors_allow_origins); - let service = ServiceBuilder::new() + let service_builder = ServiceBuilder::new() .layer( CompressionLayer::new() .zstd(true) @@ -206,8 +206,21 @@ pub(crate) async fn start_rest_server( .quality(tower_http::CompressionLevel::Fastest) .compress_when(compression_predicate), ) - .layer(cors) - .service(warp_service); + .layer(cors); + + let service; + + #[cfg(feature = "enterprise")] + { + service = service_builder + .layer(quickwit_authorize::AuthorizationTokenExtractionLayer) + .service(warp_service); + } + + #[cfg(not(feature = "enterprise"))] + { + service = service_builder.service(warp_service); + } let rest_listen_addr = tcp_listener.local_addr()?; info!(