From ba32fd9d96a6ecf4e5fb9a6ae822359ff827cd83 Mon Sep 17 00:00:00 2001 From: GatewayJ <835269233@qq.com> Date: Mon, 2 Mar 2026 11:37:00 +0800 Subject: [PATCH] =?UTF-8?q?fix(s3):=20allow=20anonymous=20access=20when=20?= =?UTF-8?q?PublicAccessBlock=20config=20is=20miss=E2=80=A6=20(#2039)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GatewayJ <8352692332qq.com> --- crates/e2e_test/src/anonymous_access_test.rs | 172 +++++++++++++++++++ crates/e2e_test/src/lib.rs | 4 + rustfs/src/storage/access.rs | 2 +- 3 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 crates/e2e_test/src/anonymous_access_test.rs diff --git a/crates/e2e_test/src/anonymous_access_test.rs b/crates/e2e_test/src/anonymous_access_test.rs new file mode 100644 index 000000000..4d12557c9 --- /dev/null +++ b/crates/e2e_test/src/anonymous_access_test.rs @@ -0,0 +1,172 @@ +// Copyright 2024 RustFS Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Regression tests for Issue #2036 +//! Verifies that anonymous access works correctly with bucket policies +//! when PublicAccessBlock configuration is missing or explicitly set. + +use crate::common::{RustFSTestEnvironment, init_logging}; +use aws_sdk_s3::types::PublicAccessBlockConfiguration; +use serial_test::serial; +use tracing::info; + +async fn setup_public_bucket( + env: &RustFSTestEnvironment, + bucket_name: &str, +) -> Result> { + let admin_client = env.create_s3_client(); + + admin_client.create_bucket().bucket(bucket_name).send().await?; + + let policy_json = serde_json::json!({ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowAnonymousGetObject", + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject"], + "Resource": [format!("arn:aws:s3:::{}/*", bucket_name)] + } + ] + }) + .to_string(); + + admin_client + .put_bucket_policy() + .bucket(bucket_name) + .policy(&policy_json) + .send() + .await?; + + admin_client + .put_object() + .bucket(bucket_name) + .key("test.txt") + .body(aws_sdk_s3::primitives::ByteStream::from_static(b"hello anonymous")) + .send() + .await?; + + Ok(admin_client) +} + +async fn anonymous_get_object( + env: &RustFSTestEnvironment, + bucket_name: &str, + key: &str, +) -> Result { + let url = format!("{}/{}/{}", env.url, bucket_name, key); + reqwest::Client::new().get(&url).send().await +} + +/// Issue #2036: Anonymous GetObject should succeed when bucket policy allows it +/// and no PublicAccessBlock configuration exists (ConfigNotFound). +#[tokio::test] +#[serial] +async fn test_anonymous_access_allowed_when_public_access_block_missing() -> Result<(), Box> +{ + init_logging(); + info!("Starting test: anonymous access with missing PublicAccessBlock config..."); + + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; + + let bucket_name = "anon-test-no-pab"; + let admin_client = setup_public_bucket(&env, bucket_name).await?; + + let _ = admin_client.delete_public_access_block().bucket(bucket_name).send().await; + + let resp = anonymous_get_object(&env, bucket_name, "test.txt").await?; + assert_eq!( + resp.status().as_u16(), + 200, + "Anonymous GetObject should succeed when no PublicAccessBlock config exists" + ); + + info!("Test passed: anonymous access allowed with missing PublicAccessBlock config"); + Ok(()) +} + +/// Anonymous GetObject should be denied when RestrictPublicBuckets is true. +#[tokio::test] +#[serial] +async fn test_anonymous_access_denied_when_restrict_public_buckets_enabled() +-> Result<(), Box> { + init_logging(); + info!("Starting test: anonymous access denied with RestrictPublicBuckets=true..."); + + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; + + let bucket_name = "anon-test-restrict"; + let admin_client = setup_public_bucket(&env, bucket_name).await?; + + admin_client + .put_public_access_block() + .bucket(bucket_name) + .public_access_block_configuration( + PublicAccessBlockConfiguration::builder() + .restrict_public_buckets(true) + .build(), + ) + .send() + .await?; + + let resp = anonymous_get_object(&env, bucket_name, "test.txt").await?; + assert_eq!( + resp.status().as_u16(), + 403, + "Anonymous GetObject should be denied when RestrictPublicBuckets is true" + ); + + info!("Test passed: anonymous access denied with RestrictPublicBuckets=true"); + Ok(()) +} + +/// Anonymous GetObject should succeed when PublicAccessBlock exists but +/// RestrictPublicBuckets is explicitly false. +#[tokio::test] +#[serial] +async fn test_anonymous_access_allowed_when_restrict_public_buckets_disabled() +-> Result<(), Box> { + init_logging(); + info!("Starting test: anonymous access allowed with RestrictPublicBuckets=false..."); + + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(vec![]).await?; + + let bucket_name = "anon-test-allow"; + let admin_client = setup_public_bucket(&env, bucket_name).await?; + + admin_client + .put_public_access_block() + .bucket(bucket_name) + .public_access_block_configuration( + PublicAccessBlockConfiguration::builder() + .restrict_public_buckets(false) + .build(), + ) + .send() + .await?; + + let resp = anonymous_get_object(&env, bucket_name, "test.txt").await?; + assert_eq!( + resp.status().as_u16(), + 200, + "Anonymous GetObject should succeed when RestrictPublicBuckets is false" + ); + + info!("Test passed: anonymous access allowed with RestrictPublicBuckets=false"); + Ok(()) +} diff --git a/crates/e2e_test/src/lib.rs b/crates/e2e_test/src/lib.rs index 0fc6e0ccb..f39fa7ae7 100644 --- a/crates/e2e_test/src/lib.rs +++ b/crates/e2e_test/src/lib.rs @@ -40,6 +40,10 @@ mod quota_test; #[cfg(test)] mod bucket_policy_check_test; +// Regression tests for Issue #2036: anonymous access with PublicAccessBlock +#[cfg(test)] +mod anonymous_access_test; + // Special characters in path test modules #[cfg(test)] mod special_chars_test; diff --git a/rustfs/src/storage/access.rs b/rustfs/src/storage/access.rs index 52ef65d71..fc4f2cf1e 100644 --- a/rustfs/src/storage/access.rs +++ b/rustfs/src/storage/access.rs @@ -473,13 +473,13 @@ pub async fn authorize_request(req: &mut S3Request, action: Action) -> S3R if policy_allowed { // RestrictPublicBuckets: when true, deny public access even if bucket policy allows it. - // Fail closed: if we cannot read the config, do not allow public access. match metadata_sys::get_public_access_block_config(bucket_name).await { Ok((config, _)) => { if config.restrict_public_buckets.unwrap_or(false) { return Err(s3_error!(AccessDenied, "Access Denied")); } } + Err(StorageError::ConfigNotFound) => {} Err(_) => { return Err(s3_error!(AccessDenied, "Access Denied")); }