mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-16 17:20:33 +00:00
Signed-off-by: loverustfs <github@rustfs.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -82,7 +82,7 @@ tokio-stream.workspace = true
|
||||
tokio-util.workspace = true
|
||||
tonic = { workspace = true }
|
||||
tower.workspace = true
|
||||
tower-http = { workspace = true, features = ["trace", "compression-full", "cors", "catch-panic", "timeout", "limit", "request-id"] }
|
||||
tower-http = { workspace = true, features = ["trace", "compression-full", "cors", "catch-panic", "timeout", "limit", "request-id", "add-extension"] }
|
||||
|
||||
# Serialization and Data Formats
|
||||
bytes = { workspace = true }
|
||||
|
||||
@@ -30,12 +30,13 @@ pub async fn validate_admin_request(
|
||||
is_owner: bool,
|
||||
deny_only: bool,
|
||||
actions: Vec<Action>,
|
||||
remote_addr: Option<std::net::SocketAddr>,
|
||||
) -> S3Result<()> {
|
||||
let Ok(iam_store) = rustfs_iam::get() else {
|
||||
return Err(s3_error!(InternalError, "iam not init"));
|
||||
};
|
||||
for action in actions {
|
||||
match check_admin_request_auth(iam_store.clone(), headers, cred, is_owner, deny_only, action).await {
|
||||
match check_admin_request_auth(iam_store.clone(), headers, cred, is_owner, deny_only, action, remote_addr).await {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(_) => {
|
||||
continue;
|
||||
@@ -53,8 +54,9 @@ async fn check_admin_request_auth(
|
||||
is_owner: bool,
|
||||
deny_only: bool,
|
||||
action: Action,
|
||||
remote_addr: Option<std::net::SocketAddr>,
|
||||
) -> S3Result<()> {
|
||||
let conditions = get_condition_values(headers, cred, None, None);
|
||||
let conditions = get_condition_values(headers, cred, None, None, remote_addr);
|
||||
|
||||
if !iam_store
|
||||
.is_allowed(&Args {
|
||||
|
||||
@@ -18,6 +18,7 @@ use crate::auth::check_key_valid;
|
||||
use crate::auth::get_condition_values;
|
||||
use crate::auth::get_session_token;
|
||||
use crate::error::ApiError;
|
||||
use crate::server::RemoteAddr;
|
||||
use bytes::Bytes;
|
||||
use futures::{Stream, StreamExt};
|
||||
use http::{HeaderMap, HeaderValue, Uri};
|
||||
@@ -210,7 +211,8 @@ impl Operation for AccountInfoHandler {
|
||||
let claims = cred.claims.as_ref().unwrap_or(&default_claims);
|
||||
|
||||
let cred_clone = cred.clone();
|
||||
let conditions = get_condition_values(&req.headers, &cred_clone, None, None);
|
||||
let remote_addr = req.extensions.get::<RemoteAddr>().map(|a| a.0);
|
||||
let conditions = get_condition_values(&req.headers, &cred_clone, None, None, remote_addr);
|
||||
let cred_clone = Arc::new(cred_clone);
|
||||
let conditions = Arc::new(conditions);
|
||||
|
||||
@@ -405,12 +407,14 @@ impl Operation for ServerInfoHandler {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
let remote_addr = req.extensions.get::<RemoteAddr>().map(|a| a.0);
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
remote_addr,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -451,12 +455,14 @@ impl Operation for StorageInfoHandler {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
let remote_addr = req.extensions.get::<RemoteAddr>().map(|a| a.0);
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::StorageInfoAdminAction)],
|
||||
remote_addr,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -492,6 +498,7 @@ impl Operation for DataUsageInfoHandler {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
let remote_addr = req.extensions.get::<RemoteAddr>().map(|a| a.0);
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
@@ -501,6 +508,7 @@ impl Operation for DataUsageInfoHandler {
|
||||
Action::AdminAction(AdminAction::DataUsageInfoAdminAction),
|
||||
Action::S3Action(S3Action::ListBucketAction),
|
||||
],
|
||||
remote_addr,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::{
|
||||
use crate::{
|
||||
admin::{auth::validate_admin_request, router::Operation},
|
||||
auth::{check_key_valid, get_session_token},
|
||||
server::RemoteAddr,
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use matchit::Params;
|
||||
@@ -97,6 +98,7 @@ impl Operation for ExportBucketMetadata {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ExportBucketMetadataAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -389,6 +391,7 @@ impl Operation for ImportBucketMetadata {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ImportBucketMetadataAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
use crate::{
|
||||
admin::{auth::validate_admin_request, router::Operation, utils::has_space_be},
|
||||
auth::{check_key_valid, constant_time_eq, get_session_token},
|
||||
server::RemoteAddr,
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use matchit::Params;
|
||||
@@ -57,6 +58,7 @@ impl Operation for ListGroups {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ListGroupsAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -95,6 +97,7 @@ impl Operation for GetGroup {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::GetGroupAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -142,6 +145,7 @@ impl Operation for SetGroupStatus {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::EnableGroupAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -209,6 +213,7 @@ impl Operation for UpdateGroupMembers {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::AddUserToGroupAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use super::Operation;
|
||||
use crate::admin::auth::validate_admin_request;
|
||||
use crate::auth::{check_key_valid, get_session_token};
|
||||
use crate::server::RemoteAddr;
|
||||
use base64::Engine;
|
||||
use hyper::{HeaderMap, StatusCode};
|
||||
use matchit::Params;
|
||||
@@ -127,6 +128,7 @@ impl Operation for CreateKeyHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)], // TODO: Add specific KMS action
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -205,6 +207,7 @@ impl Operation for DescribeKeyHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -260,6 +263,7 @@ impl Operation for ListKeysHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -321,6 +325,7 @@ impl Operation for GenerateDataKeyHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -386,6 +391,7 @@ impl Operation for KmsStatusHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -443,6 +449,7 @@ impl Operation for KmsConfigHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -487,6 +494,7 @@ impl Operation for KmsClearCacheHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use super::Operation;
|
||||
use crate::admin::auth::validate_admin_request;
|
||||
use crate::auth::{check_key_valid, get_session_token};
|
||||
use crate::server::RemoteAddr;
|
||||
use hyper::StatusCode;
|
||||
use matchit::Params;
|
||||
use rustfs_config::MAX_ADMIN_REQUEST_BODY_SIZE;
|
||||
@@ -98,6 +99,7 @@ impl Operation for ConfigureKmsHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -196,6 +198,7 @@ impl Operation for StartKmsHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -329,6 +332,7 @@ impl Operation for StopKmsHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -394,6 +398,7 @@ impl Operation for GetKmsStatusHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -465,6 +470,7 @@ impl Operation for ReconfigureKmsHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use super::Operation;
|
||||
use crate::admin::auth::validate_admin_request;
|
||||
use crate::auth::{check_key_valid, get_session_token};
|
||||
use crate::server::RemoteAddr;
|
||||
use hyper::{HeaderMap, StatusCode};
|
||||
use matchit::Params;
|
||||
use rustfs_config::MAX_ADMIN_REQUEST_BODY_SIZE;
|
||||
@@ -79,6 +80,7 @@ impl Operation for CreateKmsKeyHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -212,6 +214,7 @@ impl Operation for DeleteKmsKeyHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -360,6 +363,7 @@ impl Operation for CancelKmsKeyDeletionHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -488,6 +492,7 @@ impl Operation for ListKmsKeysHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -599,6 +604,7 @@ impl Operation for DescribeKmsKeyHandler {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ServerInfoAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
use crate::{
|
||||
admin::{auth::validate_admin_request, router::Operation, utils::has_space_be},
|
||||
auth::{check_key_valid, get_session_token},
|
||||
server::RemoteAddr,
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use matchit::Params;
|
||||
@@ -60,6 +61,7 @@ impl Operation for ListCannedPolicies {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ListUserPoliciesAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -118,6 +120,7 @@ impl Operation for AddCannedPolicy {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::CreatePolicyAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -190,6 +193,7 @@ impl Operation for InfoCannedPolicy {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::GetPolicyAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -247,6 +251,7 @@ impl Operation for RemoveCannedPolicy {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::DeletePolicyAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -307,6 +312,7 @@ impl Operation for SetPolicyForUserOrGroup {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::AttachPolicyAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ use crate::{
|
||||
admin::{auth::validate_admin_request, router::Operation},
|
||||
auth::{check_key_valid, get_session_token},
|
||||
error::ApiError,
|
||||
server::RemoteAddr,
|
||||
};
|
||||
|
||||
pub struct ListPools {}
|
||||
@@ -53,6 +54,7 @@ impl Operation for ListPools {
|
||||
Action::AdminAction(AdminAction::ServerInfoAdminAction),
|
||||
Action::AdminAction(AdminAction::DecommissionAdminAction),
|
||||
],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -119,6 +121,7 @@ impl Operation for StatusPool {
|
||||
Action::AdminAction(AdminAction::ServerInfoAdminAction),
|
||||
Action::AdminAction(AdminAction::DecommissionAdminAction),
|
||||
],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -194,6 +197,7 @@ impl Operation for StartDecommission {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::DecommissionAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -292,6 +296,7 @@ impl Operation for CancelDecommission {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::DecommissionAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
use crate::{
|
||||
admin::{auth::validate_admin_request, router::Operation},
|
||||
auth::{check_key_valid, get_session_token},
|
||||
server::RemoteAddr,
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use matchit::Params;
|
||||
@@ -103,6 +104,7 @@ impl Operation for RebalanceStart {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::RebalanceAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -180,6 +182,7 @@ impl Operation for RebalanceStatus {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::RebalanceAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -297,6 +300,7 @@ impl Operation for RebalanceStop {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::RebalanceAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
use crate::admin::utils::has_space_be;
|
||||
use crate::auth::{constant_time_eq, get_condition_values, get_session_token};
|
||||
use crate::server::RemoteAddr;
|
||||
use crate::{admin::router::Operation, auth::check_key_valid};
|
||||
use http::HeaderMap;
|
||||
use hyper::StatusCode;
|
||||
@@ -119,7 +120,13 @@ impl Operation for AddServiceAccount {
|
||||
groups: &cred.groups,
|
||||
action: Action::AdminAction(AdminAction::CreateServiceAccountAdminAction),
|
||||
bucket: "",
|
||||
conditions: &get_condition_values(&req.headers, &cred, None, None),
|
||||
conditions: &get_condition_values(
|
||||
&req.headers,
|
||||
&cred,
|
||||
None,
|
||||
None,
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
),
|
||||
is_owner: owner,
|
||||
object: "",
|
||||
claims: cred.claims.as_ref().unwrap_or(&HashMap::new()),
|
||||
@@ -270,7 +277,13 @@ impl Operation for UpdateServiceAccount {
|
||||
groups: &cred.groups,
|
||||
action: Action::AdminAction(AdminAction::UpdateServiceAccountAdminAction),
|
||||
bucket: "",
|
||||
conditions: &get_condition_values(&req.headers, &cred, None, None),
|
||||
conditions: &get_condition_values(
|
||||
&req.headers,
|
||||
&cred,
|
||||
None,
|
||||
None,
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
),
|
||||
is_owner: owner,
|
||||
object: "",
|
||||
claims: cred.claims.as_ref().unwrap_or(&HashMap::new()),
|
||||
@@ -363,7 +376,13 @@ impl Operation for InfoServiceAccount {
|
||||
groups: &cred.groups,
|
||||
action: Action::AdminAction(AdminAction::ListServiceAccountsAdminAction),
|
||||
bucket: "",
|
||||
conditions: &get_condition_values(&req.headers, &cred, None, None),
|
||||
conditions: &get_condition_values(
|
||||
&req.headers,
|
||||
&cred,
|
||||
None,
|
||||
None,
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
),
|
||||
is_owner: owner,
|
||||
object: "",
|
||||
claims: cred.claims.as_ref().unwrap_or(&HashMap::new()),
|
||||
@@ -491,7 +510,13 @@ impl Operation for ListServiceAccount {
|
||||
groups: &cred.groups,
|
||||
action: Action::AdminAction(AdminAction::UpdateServiceAccountAdminAction),
|
||||
bucket: "",
|
||||
conditions: &get_condition_values(&req.headers, &cred, None, None),
|
||||
conditions: &get_condition_values(
|
||||
&req.headers,
|
||||
&cred,
|
||||
None,
|
||||
None,
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
),
|
||||
is_owner: owner,
|
||||
object: "",
|
||||
claims: cred.claims.as_ref().unwrap_or(&HashMap::new()),
|
||||
@@ -589,7 +614,13 @@ impl Operation for DeleteServiceAccount {
|
||||
groups: &cred.groups,
|
||||
action: Action::AdminAction(AdminAction::RemoveServiceAccountAdminAction),
|
||||
bucket: "",
|
||||
conditions: &get_condition_values(&req.headers, &cred, None, None),
|
||||
conditions: &get_condition_values(
|
||||
&req.headers,
|
||||
&cred,
|
||||
None,
|
||||
None,
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
),
|
||||
is_owner: owner,
|
||||
object: "",
|
||||
claims: cred.claims.as_ref().unwrap_or(&HashMap::new()),
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
use crate::{
|
||||
admin::{auth::validate_admin_request, router::Operation},
|
||||
auth::{check_key_valid, get_session_token},
|
||||
server::RemoteAddr,
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use matchit::Params;
|
||||
@@ -90,7 +91,15 @@ impl Operation for AddTier {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::SetTierAction)]).await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::SetTierAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut input = req.input;
|
||||
let body = match input.store_all_limited(MAX_ADMIN_REQUEST_BODY_SIZE).await {
|
||||
@@ -218,7 +227,15 @@ impl Operation for EditTier {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::SetTierAction)]).await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::SetTierAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut input = req.input;
|
||||
let body = match input.store_all_limited(MAX_ADMIN_REQUEST_BODY_SIZE).await {
|
||||
@@ -293,7 +310,15 @@ impl Operation for ListTiers {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ListTierAction)]).await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ListTierAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut tier_config_mgr = GLOBAL_TierConfigMgr.read().await;
|
||||
let tiers = tier_config_mgr.list_tiers();
|
||||
@@ -329,7 +354,15 @@ impl Operation for RemoveTier {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::SetTierAction)]).await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::SetTierAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut force: bool = false;
|
||||
let force_str = query.force.clone().unwrap_or_default();
|
||||
@@ -392,7 +425,15 @@ impl Operation for VerifyTier {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ListTierAction)]).await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ListTierAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut tier_config_mgr = GLOBAL_TierConfigMgr.write().await;
|
||||
tier_config_mgr.verify(&query.tier.unwrap()).await;
|
||||
@@ -415,7 +456,15 @@ impl Operation for GetTierInfo {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ListTierAction)]).await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ListTierAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let query = {
|
||||
if let Some(query) = req.uri.query() {
|
||||
@@ -467,7 +516,15 @@ impl Operation for ClearTier {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::SetTierAction)]).await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::SetTierAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut force: bool = false;
|
||||
let force_str = query.force;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
use crate::{
|
||||
admin::{auth::validate_admin_request, router::Operation, utils::has_space_be},
|
||||
auth::{check_key_valid, constant_time_eq, get_session_token},
|
||||
server::RemoteAddr,
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use matchit::Params;
|
||||
@@ -124,6 +125,7 @@ impl Operation for AddUser {
|
||||
owner,
|
||||
deny_only,
|
||||
vec![Action::AdminAction(AdminAction::CreateUserAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -176,6 +178,7 @@ impl Operation for SetUserStatus {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::EnableUserAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -220,6 +223,7 @@ impl Operation for ListUsers {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ListUsersAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -278,6 +282,7 @@ impl Operation for RemoveUser {
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::DeleteUserAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -377,6 +382,7 @@ impl Operation for GetUserInfo {
|
||||
owner,
|
||||
deny_only,
|
||||
vec![Action::AdminAction(AdminAction::GetUserAdminAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -426,8 +432,15 @@ impl Operation for ExportIam {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ExportIAMAction)])
|
||||
.await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ExportIAMAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Ok(iam_store) = rustfs_iam::get() else {
|
||||
return Err(s3_error!(InvalidRequest, "iam not init"));
|
||||
@@ -633,8 +646,15 @@ impl Operation for ImportIam {
|
||||
let (cred, owner) =
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(AdminAction::ExportIAMAction)])
|
||||
.await?;
|
||||
validate_admin_request(
|
||||
&req.headers,
|
||||
&cred,
|
||||
owner,
|
||||
false,
|
||||
vec![Action::AdminAction(AdminAction::ExportIAMAction)],
|
||||
req.extensions.get::<RemoteAddr>().map(|a| a.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut input = req.input;
|
||||
let body = match input.store_all_limited(MAX_IAM_IMPORT_SIZE).await {
|
||||
|
||||
@@ -241,6 +241,7 @@ pub fn get_session_token<'a>(uri: &'a Uri, hds: &'a HeaderMap) -> Option<&'a str
|
||||
/// * `cred` - User credentials
|
||||
/// * `version_id` - Optional version ID of the object
|
||||
/// * `region` - Optional region/location constraint
|
||||
/// * `remote_addr` - Optional remote address of the connection
|
||||
///
|
||||
/// # Returns
|
||||
/// * `HashMap<String, Vec<String>>` - Condition values for policy evaluation
|
||||
@@ -250,6 +251,7 @@ pub fn get_condition_values(
|
||||
cred: &Credentials,
|
||||
version_id: Option<&str>,
|
||||
region: Option<&str>,
|
||||
remote_addr: Option<std::net::SocketAddr>,
|
||||
) -> HashMap<String, Vec<String>> {
|
||||
let username = if cred.is_temp() || cred.is_service_account() {
|
||||
cred.parent_user.clone()
|
||||
@@ -297,12 +299,7 @@ pub fn get_condition_values(
|
||||
.unwrap_or(false);
|
||||
|
||||
// Get remote address from header or use default
|
||||
let remote_addr = header
|
||||
.get("x-forwarded-for")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|s| s.split(',').next())
|
||||
.or_else(|| header.get("x-real-ip").and_then(|v| v.to_str().ok()))
|
||||
.unwrap_or("127.0.0.1");
|
||||
let remote_addr_s = remote_addr.map(|a| a.ip().to_string()).unwrap_or_default();
|
||||
|
||||
let mut args = HashMap::new();
|
||||
|
||||
@@ -310,7 +307,7 @@ pub fn get_condition_values(
|
||||
args.insert("CurrentTime".to_owned(), vec![curr_time.format(&Rfc3339).unwrap_or_default()]);
|
||||
args.insert("EpochTime".to_owned(), vec![epoch_time.to_string()]);
|
||||
args.insert("SecureTransport".to_owned(), vec![is_tls.to_string()]);
|
||||
args.insert("SourceIp".to_owned(), vec![get_source_ip_raw(header, remote_addr)]);
|
||||
args.insert("SourceIp".to_owned(), vec![get_source_ip_raw(header, &remote_addr_s)]);
|
||||
|
||||
// Add user agent and referer
|
||||
if let Some(user_agent) = header.get("user-agent") {
|
||||
@@ -848,7 +845,7 @@ mod tests {
|
||||
let cred = create_test_credentials();
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
let conditions = get_condition_values(&headers, &cred, None, None);
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, None);
|
||||
|
||||
assert_eq!(conditions.get("userid"), Some(&vec!["test-access-key".to_string()]));
|
||||
assert_eq!(conditions.get("username"), Some(&vec!["test-access-key".to_string()]));
|
||||
@@ -860,7 +857,7 @@ mod tests {
|
||||
let cred = create_temp_credentials();
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
let conditions = get_condition_values(&headers, &cred, None, None);
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, None);
|
||||
|
||||
assert_eq!(conditions.get("userid"), Some(&vec!["parent-user".to_string()]));
|
||||
assert_eq!(conditions.get("username"), Some(&vec!["parent-user".to_string()]));
|
||||
@@ -872,7 +869,7 @@ mod tests {
|
||||
let cred = create_service_account_credentials();
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
let conditions = get_condition_values(&headers, &cred, None, None);
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, None);
|
||||
|
||||
assert_eq!(conditions.get("userid"), Some(&vec!["service-parent".to_string()]));
|
||||
assert_eq!(conditions.get("username"), Some(&vec!["service-parent".to_string()]));
|
||||
@@ -887,7 +884,7 @@ mod tests {
|
||||
headers.insert("x-amz-object-lock-mode", HeaderValue::from_static("GOVERNANCE"));
|
||||
headers.insert("x-amz-object-lock-retain-until-date", HeaderValue::from_static("2024-12-31T23:59:59Z"));
|
||||
|
||||
let conditions = get_condition_values(&headers, &cred, None, None);
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, None);
|
||||
|
||||
assert_eq!(conditions.get("object-lock-mode"), Some(&vec!["GOVERNANCE".to_string()]));
|
||||
assert_eq!(
|
||||
@@ -902,7 +899,7 @@ mod tests {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-amz-signature-age", HeaderValue::from_static("300"));
|
||||
|
||||
let conditions = get_condition_values(&headers, &cred, None, None);
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, None);
|
||||
|
||||
assert_eq!(conditions.get("signatureAge"), Some(&vec!["300".to_string()]));
|
||||
// Verify the header is removed after processing
|
||||
@@ -919,7 +916,7 @@ mod tests {
|
||||
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
let conditions = get_condition_values(&headers, &cred, None, None);
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, None);
|
||||
|
||||
assert_eq!(conditions.get("username"), Some(&vec!["ldap-user".to_string()]));
|
||||
assert_eq!(conditions.get("groups"), Some(&vec!["group1".to_string(), "group2".to_string()]));
|
||||
@@ -932,7 +929,7 @@ mod tests {
|
||||
|
||||
let headers = HeaderMap::new();
|
||||
|
||||
let conditions = get_condition_values(&headers, &cred, None, None);
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, None);
|
||||
|
||||
assert_eq!(
|
||||
conditions.get("groups"),
|
||||
@@ -1208,4 +1205,159 @@ mod tests {
|
||||
assert!(constant_time_eq(key1, key2));
|
||||
assert!(!constant_time_eq(key1, key3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_condition_values_source_ip() {
|
||||
let mut headers = HeaderMap::new();
|
||||
let cred = Credentials::default();
|
||||
|
||||
// Case 1: No headers, no remote addr -> empty string
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, None);
|
||||
assert_eq!(conditions.get("SourceIp").unwrap()[0], "");
|
||||
|
||||
// Case 2: No headers, with remote addr -> remote addr
|
||||
let remote_addr: std::net::SocketAddr = "192.168.0.10:12345".parse().unwrap();
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, Some(remote_addr));
|
||||
assert_eq!(conditions.get("SourceIp").unwrap()[0], "192.168.0.10");
|
||||
|
||||
// Case 3: X-Forwarded-For present -> XFF (takes precedence over remote_addr)
|
||||
headers.insert("x-forwarded-for", HeaderValue::from_static("10.0.0.1"));
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, Some(remote_addr));
|
||||
assert_eq!(conditions.get("SourceIp").unwrap()[0], "10.0.0.1");
|
||||
|
||||
// Case 4: X-Forwarded-For with multiple IPs -> First IP
|
||||
headers.insert("x-forwarded-for", HeaderValue::from_static("10.0.0.3, 10.0.0.4"));
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, Some(remote_addr));
|
||||
assert_eq!(conditions.get("SourceIp").unwrap()[0], "10.0.0.3");
|
||||
|
||||
// Case 5: X-Real-IP present (XFF removed) -> X-Real-IP
|
||||
headers.remove("x-forwarded-for");
|
||||
headers.insert("x-real-ip", HeaderValue::from_static("10.0.0.2"));
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, Some(remote_addr));
|
||||
assert_eq!(conditions.get("SourceIp").unwrap()[0], "10.0.0.2");
|
||||
|
||||
// Case 6: Forwarded header present (X-Real-IP removed) -> Forwarded
|
||||
headers.remove("x-real-ip");
|
||||
headers.insert("forwarded", HeaderValue::from_static("for=10.0.0.5;proto=http"));
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, Some(remote_addr));
|
||||
assert_eq!(conditions.get("SourceIp").unwrap()[0], "10.0.0.5");
|
||||
|
||||
// Case 7: Forwarded header with quotes and multiple values
|
||||
headers.insert("forwarded", HeaderValue::from_static("for=\"10.0.0.6\", for=10.0.0.7"));
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, Some(remote_addr));
|
||||
assert_eq!(conditions.get("SourceIp").unwrap()[0], "10.0.0.6");
|
||||
|
||||
// Case 8: IPv6 Remote Addr
|
||||
let remote_addr_v6: std::net::SocketAddr = "[2001:db8::1]:8080".parse().unwrap();
|
||||
headers.clear();
|
||||
let conditions = get_condition_values(&headers, &cred, None, None, Some(remote_addr_v6));
|
||||
assert_eq!(conditions.get("SourceIp").unwrap()[0], "2001:db8::1");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests_policy {
|
||||
use rustfs_policy::policy::action::{Action, S3Action};
|
||||
use rustfs_policy::policy::{Args, BucketPolicy, BucketPolicyArgs, Policy};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_iam_policy_source_ip() {
|
||||
let policy_json = r#"{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:GetObject"],
|
||||
"Resource": ["arn:aws:s3:::mybucket/*"],
|
||||
"Condition": {
|
||||
"IpAddress": {
|
||||
"aws:SourceIp": "192.168.1.0/24"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}"#;
|
||||
|
||||
let policy: Policy = serde_json::from_str(policy_json).expect("Failed to parse IAM policy");
|
||||
|
||||
// Case 1: Matching IP
|
||||
let mut conditions = HashMap::new();
|
||||
conditions.insert("SourceIp".to_string(), vec!["192.168.1.10".to_string()]);
|
||||
|
||||
let claims = HashMap::new();
|
||||
let args = Args {
|
||||
account: "test-account",
|
||||
groups: &None,
|
||||
action: Action::S3Action(S3Action::GetObjectAction),
|
||||
bucket: "mybucket",
|
||||
conditions: &conditions,
|
||||
is_owner: false,
|
||||
object: "myobject",
|
||||
claims: &claims,
|
||||
deny_only: false,
|
||||
};
|
||||
|
||||
assert!(policy.is_allowed(&args).await, "IAM Policy should allow matching IP");
|
||||
|
||||
// Case 2: Non-matching IP
|
||||
let mut conditions_fail = HashMap::new();
|
||||
conditions_fail.insert("SourceIp".to_string(), vec!["10.0.0.1".to_string()]);
|
||||
|
||||
let args_fail = Args {
|
||||
conditions: &conditions_fail,
|
||||
..args
|
||||
};
|
||||
|
||||
assert!(!policy.is_allowed(&args_fail).await, "IAM Policy should deny non-matching IP");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bucket_policy_source_ip() {
|
||||
let policy_json = r#"{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {"AWS": ["*"]},
|
||||
"Action": ["s3:GetObject"],
|
||||
"Resource": ["arn:aws:s3:::mybucket/*"],
|
||||
"Condition": {
|
||||
"IpAddress": {
|
||||
"aws:SourceIp": "192.168.1.0/24"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}"#;
|
||||
|
||||
let policy: BucketPolicy = serde_json::from_str(policy_json).expect("Failed to parse Bucket policy");
|
||||
|
||||
// Case 1: Matching IP
|
||||
let mut conditions = HashMap::new();
|
||||
conditions.insert("SourceIp".to_string(), vec!["192.168.1.10".to_string()]);
|
||||
|
||||
let args = BucketPolicyArgs {
|
||||
account: "test-account",
|
||||
groups: &None,
|
||||
action: Action::S3Action(S3Action::GetObjectAction),
|
||||
bucket: "mybucket",
|
||||
conditions: &conditions,
|
||||
is_owner: false,
|
||||
object: "myobject",
|
||||
};
|
||||
|
||||
assert!(policy.is_allowed(&args).await, "Bucket Policy should allow matching IP");
|
||||
|
||||
// Case 2: Non-matching IP
|
||||
let mut conditions_fail = HashMap::new();
|
||||
conditions_fail.insert("SourceIp".to_string(), vec!["10.0.0.1".to_string()]);
|
||||
|
||||
let args_fail = BucketPolicyArgs {
|
||||
conditions: &conditions_fail,
|
||||
..args
|
||||
};
|
||||
|
||||
assert!(!policy.is_allowed(&args_fail).await, "Bucket Policy should deny non-matching IP");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use super::compress::{CompressionConfig, CompressionPredicate};
|
||||
use crate::admin;
|
||||
use crate::auth::IAMAuth;
|
||||
use crate::config;
|
||||
use crate::server::{ReadinessGateLayer, ServiceState, ServiceStateManager, hybrid::hybrid, layer::RedirectLayer};
|
||||
use crate::server::{ReadinessGateLayer, RemoteAddr, ServiceState, ServiceStateManager, hybrid::hybrid, layer::RedirectLayer};
|
||||
use crate::storage;
|
||||
use crate::storage::tonic_service::make_server;
|
||||
use bytes::Bytes;
|
||||
@@ -44,6 +44,7 @@ use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
use tonic::{Request, Status, metadata::MetadataValue};
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::add_extension::AddExtensionLayer;
|
||||
use tower_http::catch_panic::CatchPanicLayer;
|
||||
use tower_http::compression::CompressionLayer;
|
||||
use tower_http::cors::{AllowOrigin, Any, CorsLayer};
|
||||
@@ -528,9 +529,21 @@ fn process_connection(
|
||||
let rpc_service = NodeServiceServer::with_interceptor(make_server(), check_auth);
|
||||
let service = hybrid(s3_service, rpc_service);
|
||||
|
||||
let remote_addr = match socket.peer_addr() {
|
||||
Ok(addr) => Some(RemoteAddr(addr)),
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
error = %e,
|
||||
"Failed to obtain peer address; policy evaluation may fall back to a default source IP"
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let hybrid_service = ServiceBuilder::new()
|
||||
.layer(SetRequestIdLayer::x_request_id(MakeRequestUuid))
|
||||
.layer(CatchPanicLayer::new())
|
||||
.layer(AddExtensionLayer::new(remote_addr))
|
||||
// CRITICAL: Insert ReadinessGateLayer before business logic
|
||||
// This stops requests from hitting IAMAuth or Storage if they are not ready.
|
||||
.layer(ReadinessGateLayer::new(readiness))
|
||||
|
||||
@@ -36,3 +36,6 @@ pub(crate) use service_state::ServiceState;
|
||||
pub(crate) use service_state::ServiceStateManager;
|
||||
pub(crate) use service_state::ShutdownSignal;
|
||||
pub(crate) use service_state::wait_for_shutdown;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct RemoteAddr(pub std::net::SocketAddr);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
use super::ecfs::FS;
|
||||
use crate::auth::{check_key_valid, get_condition_values, get_session_token};
|
||||
use crate::license::license_check;
|
||||
use crate::server::RemoteAddr;
|
||||
use rustfs_ecstore::bucket::policy_sys::PolicySys;
|
||||
use rustfs_iam::error::Error as IamError;
|
||||
use rustfs_policy::policy::action::{Action, S3Action};
|
||||
@@ -36,6 +37,7 @@ pub(crate) struct ReqInfo {
|
||||
|
||||
/// Authorizes the request based on the action and credentials.
|
||||
pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3Result<()> {
|
||||
let remote_addr = req.extensions.get::<RemoteAddr>().map(|a| a.0);
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
|
||||
if let Some(cred) = &req_info.cred {
|
||||
@@ -48,7 +50,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
||||
|
||||
let default_claims = HashMap::new();
|
||||
let claims = cred.claims.as_ref().unwrap_or(&default_claims);
|
||||
let conditions = get_condition_values(&req.headers, cred, req_info.version_id.as_deref(), None);
|
||||
let conditions = get_condition_values(&req.headers, cred, req_info.version_id.as_deref(), None, remote_addr);
|
||||
|
||||
if action == Action::S3Action(S3Action::DeleteObjectAction)
|
||||
&& req_info.version_id.is_some()
|
||||
@@ -109,6 +111,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
||||
&rustfs_credentials::Credentials::default(),
|
||||
req_info.version_id.as_deref(),
|
||||
req.region.as_deref(),
|
||||
remote_addr,
|
||||
);
|
||||
|
||||
if action != Action::S3Action(S3Action::ListAllMyBucketsAction) {
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::config::workload_profiles::{
|
||||
RustFSBufferConfig, WorkloadProfile, get_global_buffer_config, is_buffer_profile_enabled,
|
||||
};
|
||||
use crate::error::ApiError;
|
||||
use crate::server::RemoteAddr;
|
||||
use crate::storage::concurrency::{
|
||||
CachedGetObject, ConcurrencyManager, GetObjectGuard, get_concurrency_aware_buffer_size, get_concurrency_manager,
|
||||
};
|
||||
@@ -4689,7 +4690,8 @@ impl S3 for FS {
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
|
||||
let conditions = get_condition_values(&req.headers, &rustfs_credentials::Credentials::default(), None, None);
|
||||
let remote_addr = req.extensions.get::<RemoteAddr>().map(|a| a.0);
|
||||
let conditions = get_condition_values(&req.headers, &rustfs_credentials::Credentials::default(), None, None, remote_addr);
|
||||
|
||||
let read_only = PolicySys::is_allowed(&BucketPolicyArgs {
|
||||
bucket: &bucket,
|
||||
|
||||
Reference in New Issue
Block a user