mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-21 13:34:26 +02:00
s3: strip internal metadata headers on auth entry (#11850)
* s3: strip internal object-lock and version headers on auth entry putToFiler persists Seaweed-X-Amz-Object-Lock-Mode, -Retention-Until-Date, -Legal-Hold and -Version-Id request headers into authoritative entry metadata without a permission check, so an s3:PutObject-only writer could pin COMPLIANCE retention and legal hold on its own uploads, bypassing the dedicated-permission gate on the standard x-amz-object-lock-* headers. The server only sets these headers from validated inputs, so drop them in authenticateRequestInternal alongside the identity headers already scrubbed there. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3: strip internal SSE headers on auth entry X-SeaweedFS-SSE-S3-Key-Data, -SSE-S3-Base-IV and -SSE-KMS-Base-IV are read from the request to drive multipart encryption, so a client could inject its own key material and IVs. The server sets them itself between handler stages of a request; drop inbound values in authenticateRequestInternal. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3: scrub internal headers before the auth-enabled early return Auth and AuthPostPolicy skip authenticateRequestInternal when IAM is disabled, so client-supplied internal headers still reached putToFiler. Hoist the scrub into scrubInternalHeaders and run it unconditionally in both wrappers as well as the shared auth entry point. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3: test internal header scrub through the auth-disabled wrapper Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
8b09c96cec
commit
7e348951c2
2 files changed
+87
-7
No files matched your search
@@ -227,3 +227,67 @@ func TestObjectLockActionsRoundTrip(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Internal Seaweed-X-Amz-* headers carry server-validated object lock values
|
||||
// into entry metadata; client-supplied ones must be dropped on entry or the
|
||||
// dedicated-permission check on x-amz-object-lock-* is bypassed.
|
||||
func TestInternalObjectLockHeadersStrippedOnAuth(t *testing.T) {
|
||||
iam := newTestIAM()
|
||||
|
||||
internal := []string{
|
||||
s3_constants.ExtObjectLockModeKey,
|
||||
s3_constants.ExtRetentionUntilDateKey,
|
||||
s3_constants.ExtLegalHoldKey,
|
||||
s3_constants.ExtVersionIdKey,
|
||||
s3_constants.SeaweedFSSSEKMSBaseIVHeader,
|
||||
s3_constants.SeaweedFSSSES3BaseIVHeader,
|
||||
s3_constants.SeaweedFSSSES3KeyDataHeader,
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodPut, "http://localhost:8333/worm/doc.txt", nil)
|
||||
for _, k := range internal {
|
||||
req.Header.Set(k, "injected")
|
||||
}
|
||||
require.NoError(t, signRawHTTPRequest(context.Background(), req,
|
||||
"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "us-east-1"))
|
||||
|
||||
_, _, _ = iam.authenticateRequestInternal(req)
|
||||
|
||||
for _, k := range internal {
|
||||
if got := req.Header.Get(k); got != "" {
|
||||
t.Errorf("client-supplied %s survived authentication: %q", k, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// With IAM disabled the Auth wrapper calls the handler directly; internal
|
||||
// headers must still be stripped so they never reach putToFiler.
|
||||
func TestInternalHeadersStrippedWithoutAuth(t *testing.T) {
|
||||
iam := &IdentityAccessManagement{}
|
||||
|
||||
internal := []string{
|
||||
s3_constants.ExtObjectLockModeKey,
|
||||
s3_constants.ExtRetentionUntilDateKey,
|
||||
s3_constants.ExtLegalHoldKey,
|
||||
s3_constants.ExtVersionIdKey,
|
||||
s3_constants.SeaweedFSSSEKMSBaseIVHeader,
|
||||
s3_constants.SeaweedFSSSES3BaseIVHeader,
|
||||
s3_constants.SeaweedFSSSES3KeyDataHeader,
|
||||
}
|
||||
var leaked []string
|
||||
handler := iam.Auth(func(w http.ResponseWriter, r *http.Request) {
|
||||
for _, k := range internal {
|
||||
if v := r.Header.Get(k); v != "" {
|
||||
leaked = append(leaked, k)
|
||||
}
|
||||
}
|
||||
}, s3_constants.ACTION_WRITE)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "http://localhost:8333/worm/doc.txt", nil)
|
||||
for _, k := range internal {
|
||||
req.Header.Set(k, "injected")
|
||||
}
|
||||
handler(httptest.NewRecorder(), req)
|
||||
if len(leaked) > 0 {
|
||||
t.Errorf("client-supplied internal headers reached handler with auth disabled: %v", leaked)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user