aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api/auto_signature_v4_test.go
diff options
context:
space:
mode:
authorChris Lu <chrislusf@users.noreply.github.com>2025-08-05 22:54:54 -0700
committerGitHub <noreply@github.com>2025-08-05 22:54:54 -0700
commitc6d97569331eae8e6038b6280f309a80e3a8450f (patch)
tree1aba63614ff4d36eb734cc8d24f52fcf308cc141 /weed/s3api/auto_signature_v4_test.go
parentb01b5e0f34fbacdbcf6dc785aa942d3db80172e2 (diff)
downloadseaweedfs-c6d97569331eae8e6038b6280f309a80e3a8450f.tar.xz
seaweedfs-c6d97569331eae8e6038b6280f309a80e3a8450f.zip
fix signature hashing for iam (#7100)
* fix signature hashing for iam * add tests * address comments * Update weed/s3api/auto_signature_v4_test.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * indention * fix test --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Diffstat (limited to 'weed/s3api/auto_signature_v4_test.go')
-rw-r--r--weed/s3api/auto_signature_v4_test.go103
1 files changed, 103 insertions, 0 deletions
diff --git a/weed/s3api/auto_signature_v4_test.go b/weed/s3api/auto_signature_v4_test.go
index 29b6df968..7a9599583 100644
--- a/weed/s3api/auto_signature_v4_test.go
+++ b/weed/s3api/auto_signature_v4_test.go
@@ -1198,6 +1198,109 @@ func TestGitHubIssue7080Scenario(t *testing.T) {
assert.Equal(t, testPayload, string(bodyBytes))
}
+// TestIAMSignatureServiceMatching tests that IAM requests use the correct service in signature computation
+// This reproduces the bug described in GitHub issue #7080 where the service was hardcoded to "s3"
+func TestIAMSignatureServiceMatching(t *testing.T) {
+ // Create test IAM instance
+ iam := &IdentityAccessManagement{}
+
+ // Load test configuration with credentials that match the logs
+ err := iam.loadS3ApiConfiguration(&iam_pb.S3ApiConfiguration{
+ Identities: []*iam_pb.Identity{
+ {
+ Name: "power_user",
+ Credentials: []*iam_pb.Credential{
+ {
+ AccessKey: "power_user_key",
+ SecretKey: "power_user_secret",
+ },
+ },
+ Actions: []string{"Admin"},
+ },
+ },
+ })
+ assert.NoError(t, err)
+
+ // Use the exact payload and headers from the failing logs
+ testPayload := "Action=CreateAccessKey&UserName=admin&Version=2010-05-08"
+
+ // Create request exactly as shown in logs
+ req, err := http.NewRequest("POST", "http://localhost:8111/", strings.NewReader(testPayload))
+ assert.NoError(t, err)
+
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
+ req.Header.Set("Host", "localhost:8111")
+ req.Header.Set("X-Amz-Date", "20250805T082934Z")
+
+ // Calculate the expected signature using the correct IAM service
+ // This simulates what botocore/AWS SDK would calculate
+ credentialScope := "20250805/us-east-1/iam/aws4_request"
+
+ // Calculate the actual payload hash for our test payload
+ actualPayloadHash := getSHA256Hash([]byte(testPayload))
+
+ // Build the canonical request with the actual payload hash
+ canonicalRequest := "POST\n/\n\ncontent-type:application/x-www-form-urlencoded; charset=utf-8\nhost:localhost:8111\nx-amz-date:20250805T082934Z\n\ncontent-type;host;x-amz-date\n" + actualPayloadHash
+
+ // Calculate the canonical request hash
+ canonicalRequestHash := getSHA256Hash([]byte(canonicalRequest))
+
+ // Build the string to sign
+ stringToSign := "AWS4-HMAC-SHA256\n20250805T082934Z\n" + credentialScope + "\n" + canonicalRequestHash
+
+ // Calculate expected signature using IAM service (what client sends)
+ expectedSigningKey := getSigningKey("power_user_secret", "20250805", "us-east-1", "iam")
+ expectedSignature := getSignature(expectedSigningKey, stringToSign)
+
+ // Create authorization header with the correct signature
+ authHeader := "AWS4-HMAC-SHA256 Credential=power_user_key/" + credentialScope +
+ ", SignedHeaders=content-type;host;x-amz-date, Signature=" + expectedSignature
+ req.Header.Set("Authorization", authHeader)
+
+ // Now test that SeaweedFS computes the same signature with our fix
+ identity, errCode := iam.doesSignatureMatch(actualPayloadHash, req)
+
+ // With the fix, the signatures should match and we should get a successful authentication
+ assert.Equal(t, s3err.ErrNone, errCode)
+ assert.NotNil(t, identity)
+ assert.Equal(t, "power_user", identity.Name)
+}
+
+// TestStreamingSignatureServiceField tests that the s3ChunkedReader struct correctly stores the service
+// This verifies the fix for streaming uploads where getChunkSignature was hardcoding "s3"
+func TestStreamingSignatureServiceField(t *testing.T) {
+ // Test that the s3ChunkedReader correctly uses the service field
+ // Create a mock s3ChunkedReader with IAM service
+ chunkedReader := &s3ChunkedReader{
+ seedDate: time.Now(),
+ region: "us-east-1",
+ service: "iam", // This should be used instead of hardcoded "s3"
+ seedSignature: "testsignature",
+ cred: &Credential{
+ AccessKey: "testkey",
+ SecretKey: "testsecret",
+ },
+ }
+
+ // Test that getScope is called with the correct service
+ scope := getScope(chunkedReader.seedDate, chunkedReader.region, chunkedReader.service)
+ assert.Contains(t, scope, "/iam/aws4_request")
+ assert.NotContains(t, scope, "/s3/aws4_request")
+
+ // Test that getSigningKey would be called with the correct service
+ signingKey := getSigningKey(
+ chunkedReader.cred.SecretKey,
+ chunkedReader.seedDate.Format(yyyymmdd),
+ chunkedReader.region,
+ chunkedReader.service,
+ )
+ assert.NotNil(t, signingKey)
+
+ // The main point is that chunkedReader.service is "iam" and gets used correctly
+ // This ensures that IAM streaming uploads will use "iam" service instead of hardcoded "s3"
+ assert.Equal(t, "iam", chunkedReader.service)
+}
+
// Test that large IAM request bodies are truncated for security (DoS prevention)
func TestIAMLargeBodySecurityLimit(t *testing.T) {
// Create test IAM instance