s3: require their own actions to change a bucket's CORS or versioning (#12433)

* s3: require their own actions to change a bucket's CORS or versioning

PUT/DELETE ?cors and PUT ?versioning were authorized as Write, so an
identity granted only object writes on a bucket could replace its CORS
rules or suspend versioning. They now need PutBucketCors,
DeleteBucketCors and PutBucketVersioning (or Admin), and inline policies
granting s3:PutBucketCors, s3:DeleteBucketCors or s3:PutBucketVersioning
map to those actions instead of Write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* s3: test that object Write cannot change bucket CORS or versioning

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* admin: offer the bucket CORS and versioning permissions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* s3: test each bucket config action with its own identity

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chris LuandClaude Opus 5.5 authored and GitHub committed 2026-11-06 09:12:00 -08:00
1 parent 3a579ece3b
commit 3e64326f2b
7 files changed
+109 -7

No files matched your search

+3 -3
View File
@@ -1099,9 +1099,9 @@ func (s3a *S3ApiServer) registerRouter(router *mux.Router) {
// GetBucketCors
bucket.Methods(http.MethodGet).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.GetBucketCorsHandler, ACTION_READ)), "GET")).Queries("cors", "")
// PutBucketCors
bucket.Methods(http.MethodPut).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.PutBucketCorsHandler, ACTION_WRITE)), "PUT")).Queries("cors", "")
bucket.Methods(http.MethodPut).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.PutBucketCorsHandler, ACTION_PUT_BUCKET_CORS)), "PUT")).Queries("cors", "")
// DeleteBucketCors
bucket.Methods(http.MethodDelete).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.DeleteBucketCorsHandler, ACTION_WRITE)), "DELETE")).Queries("cors", "")
bucket.Methods(http.MethodDelete).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.DeleteBucketCorsHandler, ACTION_DELETE_BUCKET_CORS)), "DELETE")).Queries("cors", "")
// GetBucketLifecycleConfiguration
bucket.Methods(http.MethodGet).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.GetBucketLifecycleConfigurationHandler, ACTION_READ)), "GET")).Queries("lifecycle", "")
@@ -1134,7 +1134,7 @@ func (s3a *S3ApiServer) registerRouter(router *mux.Router) {
// GetBucketVersioning
bucket.Methods(http.MethodGet).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.GetBucketVersioningHandler, ACTION_READ)), "GET")).Queries("versioning", "")
bucket.Methods(http.MethodPut).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.PutBucketVersioningHandler, ACTION_WRITE)), "PUT")).Queries("versioning", "")
bucket.Methods(http.MethodPut).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.PutBucketVersioningHandler, ACTION_PUT_BUCKET_VERSIONING)), "PUT")).Queries("versioning", "")
// GetObjectLockConfiguration / PutObjectLockConfiguration (bucket-level operations)
bucket.Methods(http.MethodGet).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.GetObjectLockConfigurationHandler, ACTION_GET_BUCKET_OBJECT_LOCK_CONFIG)), "GET")).Queries("object-lock", "")