mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-11 16:57:45 +02:00
* s3api: apply bucket default encryption when volume data encryption is enabled When -s3.encryptVolumeData (s3a.cipher) is enabled, putToFiler skipped checking and applying bucket default encryption due to a '!s3a.cipher' guard. Volume-level data encryption and object-level Server-Side Encryption (SSE-S3 / SSE-KMS) operate at different layers, and explicit SSE headers already work alongside volume encryption. Remove the '!s3a.cipher' guard so PutObject without explicit SSE headers inherits bucket default encryption regardless of volume data encryption. Add regression test TestPutObjectAppliesBucketDefaultEncryptionWithVolumeCipher. Signed-off-by: Tyagiquamar <mohdquamartyagi@gmail.com> * s3api: decrypt the volume cipher on direct SSE chunk reads fetchFullChunk, fetchChunkViewData, and createEncryptedChunkReader fetched raw bytes over HTTP, so volume-encrypted chunks reached SSE-S3/KMS/C decryptors still ciphered. Route them through fetchChunkData: encrypted or compressed chunks go through RetriedFetchChunkData (cipher-aware, slices plaintext space for views); plain chunks keep the streaming range read. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3api: cover cipher-aware chunk reads with a fake volume server The fake volume now serves stored GETs with Range support, and TestFetchChunkDataDecryptsVolumeCipher verifies full-chunk and view reads return plaintext for ciphered chunks while plain chunks still slice via HTTP ranges. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3api: guard fake volume server stored map with its mutex The HTTP handler goroutine read v.stored while test goroutines wrote it, a data race go test -race can flag. Lock v.mu around the map read and the test writes. --------- Signed-off-by: Tyagiquamar <mohdquamartyagi@gmail.com> Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com> Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Chris Lu <chris.lu@gmail.com>
117 lines
3.5 KiB
Go
117 lines
3.5 KiB
Go
package s3api
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/s3_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
|
)
|
|
|
|
// Issue 11647: Bucket default encryption (SSE-S3) must be applied when
|
|
// -s3.encryptVolumeData (s3a.cipher) is enabled, matching explicit SSE headers.
|
|
func TestPutObjectAppliesBucketDefaultEncryptionWithVolumeCipher(t *testing.T) {
|
|
// Configure test key manager with a super key for SSE-S3 encryption.
|
|
km := GetSSES3KeyManager()
|
|
oldSuperKey := km.superKey
|
|
km.superKey = make([]byte, 32)
|
|
for i := range km.superKey {
|
|
km.superKey[i] = byte(i + 1)
|
|
}
|
|
t.Cleanup(func() {
|
|
km.superKey = oldSuperKey
|
|
})
|
|
|
|
testCases := []struct {
|
|
name string
|
|
enableCipher bool
|
|
}{
|
|
{
|
|
name: "volume encryption enabled (s3a.cipher=true)",
|
|
enableCipher: true,
|
|
},
|
|
{
|
|
name: "volume encryption disabled (s3a.cipher=false)",
|
|
enableCipher: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
volume := startFakeVolumeServer(t)
|
|
filerImpl := &ambiguousPutFiler{
|
|
volume: volume,
|
|
entries: map[string]*filer_pb.Entry{},
|
|
apply: true,
|
|
}
|
|
s3a := newPutTestServer(t, startFakeFiler(t, filerImpl))
|
|
s3a.cipher = tc.enableCipher
|
|
|
|
// Configure bucket "b" with default AES256 (SSE-S3) encryption.
|
|
s3a.bucketConfigCache = NewBucketConfigCache(time.Minute)
|
|
s3a.bucketConfigCache.Set("b", &BucketConfig{
|
|
Name: "b",
|
|
Encryption: &s3_pb.EncryptionConfiguration{
|
|
SseAlgorithm: "AES256",
|
|
},
|
|
})
|
|
|
|
// PUT request without explicit SSE headers.
|
|
r := httptest.NewRequest(http.MethodPut, "/b/plain.txt", nil)
|
|
filePath := "/buckets/b/plain.txt"
|
|
etag, code, sseMeta := s3a.putToFiler(r, filePath, strings.NewReader("hello seaweedfs"), "b", "plain.txt", 1, 0, nil, false, "")
|
|
if code != s3err.ErrNone {
|
|
t.Fatalf("putToFiler returned error code %v, want %v", code, s3err.ErrNone)
|
|
}
|
|
if etag == "" {
|
|
t.Fatal("expected non-empty etag")
|
|
}
|
|
|
|
// Verify returned SSE response metadata.
|
|
if sseMeta.SSEType != s3_constants.SSETypeS3 {
|
|
t.Fatalf("expected SSE response metadata type %s, got %s", s3_constants.SSETypeS3, sseMeta.SSEType)
|
|
}
|
|
|
|
// Verify entry saved on the filer.
|
|
entry, ok := filerImpl.entries[filePath]
|
|
if !ok {
|
|
t.Fatalf("entry not found on filer at %s", filePath)
|
|
}
|
|
|
|
sseHeaderVal, hasSSE := entry.Extended[s3_constants.AmzServerSideEncryption]
|
|
if !hasSSE || !bytes.Equal(sseHeaderVal, []byte("AES256")) {
|
|
t.Fatalf("expected entry to have %s=AES256, got hasSSE=%v val=%s", s3_constants.AmzServerSideEncryption, hasSSE, string(sseHeaderVal))
|
|
}
|
|
|
|
if len(entry.Extended[s3_constants.SeaweedFSSSES3Key]) == 0 {
|
|
t.Fatal("expected entry to have stored SSE-S3 key metadata")
|
|
}
|
|
|
|
if len(entry.Chunks) == 0 {
|
|
t.Fatal("expected entry to have at least one chunk")
|
|
}
|
|
|
|
for i, chunk := range entry.Chunks {
|
|
if chunk.SseType != filer_pb.SSEType_SSE_S3 {
|
|
t.Errorf("chunk %d: expected SseType SSE_S3, got %v", i, chunk.SseType)
|
|
}
|
|
if len(chunk.SseMetadata) == 0 {
|
|
t.Errorf("chunk %d: expected non-empty SseMetadata", i)
|
|
}
|
|
if tc.enableCipher && len(chunk.CipherKey) == 0 {
|
|
t.Errorf("chunk %d: expected volume CipherKey when s3a.cipher is true", i)
|
|
}
|
|
if !tc.enableCipher && len(chunk.CipherKey) != 0 {
|
|
t.Errorf("chunk %d: expected empty volume CipherKey when s3a.cipher is false", i)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|