diff --git a/src/api/admin/api_server.rs b/src/api/admin/api_server.rs index d2e2e40d..5d071bbf 100644 --- a/src/api/admin/api_server.rs +++ b/src/api/admin/api_server.rs @@ -217,6 +217,13 @@ impl ApiHandler for ArcAdminApiServer { ) -> Result, Error> { self.0.handle_http_api(req, endpoint).await } + + fn key_id_from_request(&self, req: &Request) -> Option { + let auth_header = req.headers().get(AUTHORIZATION)?; + let token = parse_authorization(auth_header).ok()?; + let key_id = token.split_once('.')?.0; + Some(key_id.to_string()) + } } impl ApiEndpoint for HttpEndpoint { @@ -244,6 +251,15 @@ fn hash_bearer_token(token: &str) -> String { .to_string() } +fn parse_authorization(auth_header: &hyper::http::HeaderValue) -> Result<&str, Error> { + let token = auth_header + .to_str()? + .strip_prefix("Bearer ") + .ok_or_else(|| Error::forbidden("Invalid Authorization header"))? + .trim(); + Ok(token) +} + fn verify_authorization( garage: &Garage, global_token_hash: Option<&str>, @@ -260,11 +276,7 @@ fn verify_authorization( "Bearer token must be provided in Authorization header", )) } - Some(authorization) => authorization - .to_str()? - .strip_prefix("Bearer ") - .ok_or_else(|| Error::forbidden("Invalid Authorization header"))? - .trim(), + Some(authorization) => parse_authorization(authorization)?, }; let token_hash_string = if let Some((prefix, _)) = token.split_once('.') { diff --git a/src/api/common/generic_server.rs b/src/api/common/generic_server.rs index d7ee5692..1c2af146 100644 --- a/src/api/common/generic_server.rs +++ b/src/api/common/generic_server.rs @@ -34,6 +34,7 @@ use garage_util::metrics::{gen_trace_id, RecordDuration}; use garage_util::socket_address::UnixOrTCPSocketAddress; use crate::helpers::{BoxBody, ErrorBody}; +use crate::signature::payload::Authorization; pub trait ApiEndpoint: Send + Sync + 'static { fn name(&self) -> Cow<'static, str>; @@ -59,6 +60,12 @@ pub trait ApiHandler: Send + Sync + 'static { req: Request, endpoint: Self::Endpoint, ) -> impl Future>, Self::Error>> + Send; + + /// Returns the key id used to authenticate this request. The ID returned must be safe to + /// log. + fn key_id_from_request(&self, req: &Request) -> Option { + None + } } pub struct ApiServer { @@ -143,19 +150,20 @@ impl ApiServer { ) -> Result>, http::Error> { let uri = req.uri().clone(); - if let Ok(forwarded_for_ip_addr) = + let source = if let Ok(forwarded_for_ip_addr) = forwarded_headers::handle_forwarded_for_headers(req.headers()) { - info!( - "{} (via {}) {} {}", - forwarded_for_ip_addr, - addr, - req.method(), - uri - ); + format!("{forwarded_for_ip_addr} (via {addr})") } else { - info!("{} {} {}", addr, req.method(), uri); - } + format!("{addr}") + }; + // we only do this to log the access key, so we can discard any error + let key = self + .api_handler + .key_id_from_request(&req) + .map(|k| format!("(key {k}) ")) + .unwrap_or_default(); + info!("{source} {key}{} {uri}", req.method()); debug!("{:?}", req); let tracer = opentelemetry::global::tracer("garage"); diff --git a/src/api/common/signature/payload.rs b/src/api/common/signature/payload.rs index 88269ba0..b7ffc599 100644 --- a/src/api/common/signature/payload.rs +++ b/src/api/common/signature/payload.rs @@ -424,7 +424,7 @@ pub fn verify_v4( // ============ Authorization header, or X-Amz-* query params ========= pub struct Authorization { - key_id: String, + pub key_id: String, scope: String, signed_headers: String, signature: String, @@ -433,7 +433,7 @@ pub struct Authorization { } impl Authorization { - fn parse_header(headers: &HeaderMap) -> Result { + pub fn parse_header(headers: &HeaderMap) -> Result { let authorization = headers .get(AUTHORIZATION) .ok_or_bad_request("Missing authorization header")? diff --git a/src/api/k2v/api_server.rs b/src/api/k2v/api_server.rs index 8ace37d4..8c89c35d 100644 --- a/src/api/k2v/api_server.rs +++ b/src/api/k2v/api_server.rs @@ -171,6 +171,12 @@ impl ApiHandler for K2VApiServer { Ok(resp_ok) } + + fn key_id_from_request(&self, req: &Request) -> Option { + garage_api_common::signature::payload::Authorization::parse_header(req.headers()) + .map(|auth| auth.key_id) + .ok() + } } impl ApiEndpoint for K2VApiEndpoint { diff --git a/src/api/s3/api_server.rs b/src/api/s3/api_server.rs index cc961dde..fba32ec9 100644 --- a/src/api/s3/api_server.rs +++ b/src/api/s3/api_server.rs @@ -340,6 +340,12 @@ impl ApiHandler for S3ApiServer { Ok(resp_ok) } + + fn key_id_from_request(&self, req: &Request) -> Option { + garage_api_common::signature::payload::Authorization::parse_header(req.headers()) + .map(|auth| auth.key_id) + .ok() + } } impl ApiEndpoint for S3ApiEndpoint {