mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-12 01:07:35 +02:00
* s3api: resolve volume data encryption in CopyObject SSE flows (#11646) * s3api: honor bucket-default KMS key on copy and fix transformed-upload metadata - Synthesize the destination bucket's default encryption as request headers before any SSE evaluation, so the configured KMS key ID and bucket-key setting reach the copy paths instead of only a boolean. - uploadTransformedChunkData returns the upload result so callers record the uploader's cipher key AND compression decision; a wrongly cleared IsCompressed made transformed copies unreadable. - decompressChunkVolumeCipher fails loudly when a compressed chunk does not decompress, instead of uploading still-compressed bytes marked uncompressed. - copyMultipartSSECChunk now strips the volume cipher and re-encrypts on upload like the other transform paths. - The ciphered inner upload now uses the caller's private BytesBuffer. * s3api: extract copy bucket-default header synthesis for coverage Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3api: test bucket-default encryption header synthesis on copy Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3api: validate copy encryption headers before bucket defaults and resolve empty KMS key * s3api: name the AWS-managed SSE-KMS default key once --------- Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com> Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
8 files changed
+364
-6
No files matched your search
@@ -268,3 +268,109 @@ func TestGetEncryptedStreamFromVolumesRangesInlineContent(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetermineUnifiedCopyStrategySSES3(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
state *EncryptionState
|
||||
expected UnifiedCopyStrategy
|
||||
}{
|
||||
{
|
||||
name: "SSE-S3 to SSE-S3 direct copy",
|
||||
state: &EncryptionState{
|
||||
SrcSSES3: true,
|
||||
DstSSES3: true,
|
||||
},
|
||||
expected: CopyStrategyDirect,
|
||||
},
|
||||
{
|
||||
name: "SSE-S3 to plain decrypt copy",
|
||||
state: &EncryptionState{
|
||||
SrcSSES3: true,
|
||||
DstSSES3: false,
|
||||
},
|
||||
expected: CopyStrategyDecrypt,
|
||||
},
|
||||
{
|
||||
name: "Plain to SSE-S3 encrypt copy",
|
||||
state: &EncryptionState{
|
||||
SrcSSES3: false,
|
||||
DstSSES3: true,
|
||||
},
|
||||
expected: CopyStrategyEncrypt,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
strategy, err := DetermineUnifiedCopyStrategy(tc.state, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("DetermineUnifiedCopyStrategy failed: %v", err)
|
||||
}
|
||||
if strategy != tc.expected {
|
||||
t.Errorf("expected strategy %v, got %v", tc.expected, strategy)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecryptChunkVolumeCipher(t *testing.T) {
|
||||
s3a := &S3ApiServer{}
|
||||
plainData := []byte("hello-seaweedfs-encrypted-volume-data-verification-test")
|
||||
|
||||
t.Run("unencrypted chunk passes through", func(t *testing.T) {
|
||||
chunk := &filer_pb.FileChunk{
|
||||
CipherKey: nil,
|
||||
}
|
||||
got, err := s3a.decryptChunkVolumeCipher(plainData, chunk)
|
||||
if err != nil {
|
||||
t.Fatalf("decryptChunkVolumeCipher failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, plainData) {
|
||||
t.Fatalf("expected %q, got %q", plainData, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("volume cipher encrypted chunk decrypts correctly", func(t *testing.T) {
|
||||
cipherKey := util.GenCipherKey()
|
||||
encrypted, err := util.Encrypt(plainData, cipherKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Encrypt failed: %v", err)
|
||||
}
|
||||
|
||||
chunk := &filer_pb.FileChunk{
|
||||
CipherKey: cipherKey,
|
||||
}
|
||||
got, err := s3a.decryptChunkVolumeCipher(encrypted, chunk)
|
||||
if err != nil {
|
||||
t.Fatalf("decryptChunkVolumeCipher failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, plainData) {
|
||||
t.Fatalf("expected %q, got %q", plainData, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("volume cipher encrypted and compressed chunk decrypts correctly", func(t *testing.T) {
|
||||
cipherKey := util.GenCipherKey()
|
||||
compressed, err := util.GzipData(plainData)
|
||||
if err != nil {
|
||||
t.Fatalf("GzipData failed: %v", err)
|
||||
}
|
||||
encrypted, err := util.Encrypt(compressed, cipherKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Encrypt failed: %v", err)
|
||||
}
|
||||
|
||||
chunk := &filer_pb.FileChunk{
|
||||
CipherKey: cipherKey,
|
||||
IsCompressed: true,
|
||||
}
|
||||
got, err := s3a.decryptChunkVolumeCipher(encrypted, chunk)
|
||||
if err != nil {
|
||||
t.Fatalf("decryptChunkVolumeCipher failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, plainData) {
|
||||
t.Fatalf("expected %q, got %q", plainData, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user