Fix SSE-C checksums (#19896)

Compression will be disabled by default if SSE-C is specified. So we can still honor SSE-C.
This commit is contained in:
Klaus Post 2024-06-10 08:31:51 -07:00 committed by GitHub
parent 6c7a21df6b
commit a2cab02554
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 113 additions and 73 deletions

View File

@ -789,8 +789,8 @@ func generateInitiateMultipartUploadResponse(bucket, key, uploadID string) Initi
}
// generates CompleteMultipartUploadResponse for given bucket, key, location and ETag.
func generateCompleteMultipartUploadResponse(bucket, key, location string, oi ObjectInfo) CompleteMultipartUploadResponse {
cs := oi.decryptChecksums(0)
func generateCompleteMultipartUploadResponse(bucket, key, location string, oi ObjectInfo, h http.Header) CompleteMultipartUploadResponse {
cs := oi.decryptChecksums(0, h)
c := CompleteMultipartUploadResponse{
Location: location,
Bucket: bucket,

View File

@ -741,7 +741,7 @@ func getTransitionedObjectReader(ctx context.Context, bucket, object string, rs
return nil, fmt.Errorf("transition storage class not configured: %w", err)
}
fn, off, length, err := NewGetObjectReader(rs, oi, opts)
fn, off, length, err := NewGetObjectReader(rs, oi, opts, h)
if err != nil {
return nil, ErrorRespToObjectError(err, bucket, object)
}

View File

@ -202,7 +202,7 @@ func (api objectAPIHandlers) listObjectsV2Handler(ctx context.Context, w http.Re
if r.Header.Get(xMinIOExtract) == "true" && strings.Contains(prefix, archivePattern) {
// Initiate a list objects operation inside a zip file based in the input params
listObjectsV2Info, err = listObjectsV2InArchive(ctx, objectAPI, bucket, prefix, token, delimiter, maxKeys, fetchOwner, startAfter)
listObjectsV2Info, err = listObjectsV2InArchive(ctx, objectAPI, bucket, prefix, token, delimiter, maxKeys, startAfter, r.Header)
} else {
// Initiate a list objects operation based on the input params.
// On success would return back ListObjectsInfo object to be

View File

@ -534,7 +534,7 @@ func getHealReplicateObjectInfo(oi ObjectInfo, rcfg replicationConfig) Replicate
rstate.ReplicateDecisionStr = dsc.String()
asz, _ := oi.GetActualSize()
return ReplicateObjectInfo{
r := ReplicateObjectInfo{
Name: oi.Name,
Size: oi.Size,
ActualSize: asz,
@ -558,6 +558,10 @@ func getHealReplicateObjectInfo(oi ObjectInfo, rcfg replicationConfig) Replicate
SSEC: crypto.SSEC.IsEncrypted(oi.UserDefined),
UserTags: oi.UserTags,
}
if r.SSEC {
r.Checksum = oi.Checksum
}
return r
}
// ReplicationState - returns replication state using other internal replication metadata in ObjectInfo

View File

@ -19,6 +19,7 @@ package cmd
import (
"context"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
@ -74,8 +75,9 @@ const (
ObjectLockRetentionTimestamp = "objectlock-retention-timestamp"
// ObjectLockLegalHoldTimestamp - the last time a legal hold metadata modification happened on this cluster for this object version
ObjectLockLegalHoldTimestamp = "objectlock-legalhold-timestamp"
// ReplicationWorkerMultiplier is suggested worker multiplier if traffic exceeds replication worker capacity
ReplicationWorkerMultiplier = 1.5
// ReplicationSsecChecksumHeader - the encrypted checksum of the SSE-C encrypted object.
ReplicationSsecChecksumHeader = ReservedMetadataPrefix + "Ssec-Crc"
)
// gets replication config associated to a given bucket name.
@ -763,9 +765,9 @@ func (m caseInsensitiveMap) Lookup(key string) (string, bool) {
return "", false
}
func getCRCMeta(oi ObjectInfo, partNum int) map[string]string {
func getCRCMeta(oi ObjectInfo, partNum int, h http.Header) map[string]string {
meta := make(map[string]string)
cs := oi.decryptChecksums(partNum)
cs := oi.decryptChecksums(partNum, h)
for k, v := range cs {
cksum := hash.NewChecksumString(k, v)
if cksum == nil {
@ -780,12 +782,14 @@ func getCRCMeta(oi ObjectInfo, partNum int) map[string]string {
func putReplicationOpts(ctx context.Context, sc string, objInfo ObjectInfo, partNum int) (putOpts minio.PutObjectOptions, err error) {
meta := make(map[string]string)
isSSEC := crypto.SSEC.IsEncrypted(objInfo.UserDefined)
for k, v := range objInfo.UserDefined {
// In case of SSE-C objects copy the allowed internal headers as well
if !crypto.SSEC.IsEncrypted(objInfo.UserDefined) || !slices.Contains(maps.Keys(validSSEReplicationHeaders), k) {
if !isSSEC || !slices.Contains(maps.Keys(validSSEReplicationHeaders), k) {
if stringsHasPrefixFold(k, ReservedMetadataPrefixLower) {
if strings.EqualFold(k, ReservedMetadataPrefixLower+"crc") {
for k, v := range getCRCMeta(objInfo, partNum) {
for k, v := range getCRCMeta(objInfo, partNum, nil) {
meta[k] = v
}
}
@ -803,8 +807,13 @@ func putReplicationOpts(ctx context.Context, sc string, objInfo ObjectInfo, part
}
if len(objInfo.Checksum) > 0 {
for k, v := range getCRCMeta(objInfo, 0) {
meta[k] = v
// Add encrypted CRC to metadata for SSE-C objects.
if isSSEC {
meta[ReplicationSsecChecksumHeader] = base64.StdEncoding.EncodeToString(objInfo.Checksum)
} else {
for k, v := range getCRCMeta(objInfo, 0, nil) {
meta[k] = v
}
}
}
@ -1646,7 +1655,7 @@ func replicateObjectWithMultipart(ctx context.Context, c *minio.Core, bucket, ob
cHeader := http.Header{}
cHeader.Add(xhttp.MinIOSourceReplicationRequest, "true")
crc := getCRCMeta(objInfo, partInfo.Number)
crc := getCRCMeta(objInfo, partInfo.Number, nil) // No SSE-C keys here.
for k, v := range crc {
cHeader.Add(k, v)
}
@ -2219,12 +2228,12 @@ type proxyResult struct {
// get Reader from replication target if active-active replication is in place and
// this node returns a 404
func proxyGetToReplicationTarget(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, _ http.Header, opts ObjectOptions, proxyTargets *madmin.BucketTargets) (gr *GetObjectReader, proxy proxyResult, err error) {
func proxyGetToReplicationTarget(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions, proxyTargets *madmin.BucketTargets) (gr *GetObjectReader, proxy proxyResult, err error) {
tgt, oi, proxy := proxyHeadToRepTarget(ctx, bucket, object, rs, opts, proxyTargets)
if !proxy.Proxy {
return nil, proxy, nil
}
fn, _, _, err := NewGetObjectReader(nil, oi, opts)
fn, _, _, err := NewGetObjectReader(nil, oi, opts, h)
if err != nil {
return nil, proxy, err
}
@ -2409,7 +2418,9 @@ func scheduleReplication(ctx context.Context, oi ObjectInfo, o ObjectLayer, dsc
SSEC: crypto.SSEC.IsEncrypted(oi.UserDefined),
UserTags: oi.UserTags,
}
if ri.SSEC {
ri.Checksum = oi.Checksum
}
if dsc.Synchronous() {
replicateObject(ctx, ri, o)
} else {

View File

@ -1077,13 +1077,16 @@ func metadataEncrypter(key crypto.ObjectKey) objectMetaEncryptFn {
}
// metadataDecrypter reverses metadataEncrypter.
func (o *ObjectInfo) metadataDecrypter() objectMetaDecryptFn {
func (o *ObjectInfo) metadataDecrypter(h http.Header) objectMetaDecryptFn {
return func(baseKey string, input []byte) ([]byte, error) {
if len(input) == 0 {
return input, nil
}
key, err := decryptObjectMeta(nil, o.Bucket, o.Name, o.UserDefined)
var key []byte
if k, err := crypto.SSEC.ParseHTTP(h); err == nil {
key = k[:]
}
key, err := decryptObjectMeta(key, o.Bucket, o.Name, o.UserDefined)
if err != nil {
return nil, err
}
@ -1095,13 +1098,13 @@ func (o *ObjectInfo) metadataDecrypter() objectMetaDecryptFn {
// decryptChecksums will attempt to decode checksums and return it/them if set.
// if part > 0, and we have the checksum for the part that will be returned.
func (o *ObjectInfo) decryptPartsChecksums() {
func (o *ObjectInfo) decryptPartsChecksums(h http.Header) {
data := o.Checksum
if len(data) == 0 {
return
}
if _, encrypted := crypto.IsEncrypted(o.UserDefined); encrypted {
decrypted, err := o.metadataDecrypter()("object-checksum", data)
decrypted, err := o.metadataDecrypter(h)("object-checksum", data)
if err != nil {
encLogIf(GlobalContext, err)
return
@ -1157,13 +1160,13 @@ func (o *ObjectInfo) metadataEncryptFn(headers http.Header) (objectMetaEncryptFn
// decryptChecksums will attempt to decode checksums and return it/them if set.
// if part > 0, and we have the checksum for the part that will be returned.
func (o *ObjectInfo) decryptChecksums(part int) map[string]string {
func (o *ObjectInfo) decryptChecksums(part int, h http.Header) map[string]string {
data := o.Checksum
if len(data) == 0 {
return nil
}
if _, encrypted := crypto.IsEncrypted(o.UserDefined); encrypted {
decrypted, err := o.metadataDecrypter()("object-checksum", data)
decrypted, err := o.metadataDecrypter(h)("object-checksum", data)
if err != nil {
encLogIf(GlobalContext, err)
return nil

View File

@ -1303,7 +1303,13 @@ func (er erasureObjects) CompleteMultipartUpload(ctx context.Context, bucket str
fi.Checksum = opts.EncryptFn("object-checksum", fi.Checksum)
}
}
delete(fi.Metadata, hash.MinIOMultipartChecksum) // Not needed in final object.
if fi.Metadata[ReplicationSsecChecksumHeader] != "" {
if v, err := base64.StdEncoding.DecodeString(fi.Metadata[ReplicationSsecChecksumHeader]); err == nil {
fi.Checksum = v
}
}
delete(fi.Metadata, ReplicationSsecChecksumHeader) // Transferred above.
delete(fi.Metadata, hash.MinIOMultipartChecksum) // Not needed in final object.
// Save the final object size and modtime.
fi.Size = objectSize

View File

@ -20,6 +20,7 @@ package cmd
import (
"bytes"
"context"
"encoding/base64"
"errors"
"fmt"
"io"
@ -276,7 +277,7 @@ func (er erasureObjects) GetObjectNInfo(ctx context.Context, bucket, object stri
return gr.WithCleanupFuncs(nsUnlocker), nil
}
fn, off, length, err := NewGetObjectReader(rs, objInfo, opts)
fn, off, length, err := NewGetObjectReader(rs, objInfo, opts, h)
if err != nil {
return nil, err
}
@ -1355,6 +1356,12 @@ func (er erasureObjects) putObject(ctx context.Context, bucket string, object st
if opts.EncryptFn != nil {
fi.Checksum = opts.EncryptFn("object-checksum", fi.Checksum)
}
if userDefined[ReplicationSsecChecksumHeader] != "" {
if v, err := base64.StdEncoding.DecodeString(userDefined[ReplicationSsecChecksumHeader]); err == nil {
fi.Checksum = v
}
}
delete(userDefined, ReplicationSsecChecksumHeader)
uniqueID := mustGetUUID()
tempObj := uniqueID

View File

@ -33,8 +33,6 @@ import (
"github.com/minio/minio/internal/logger"
"github.com/minio/minio/internal/mcontext"
xnet "github.com/minio/pkg/v3/net"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
const (
@ -90,6 +88,7 @@ var supportedHeaders = []string{
"X-Minio-Replication-Server-Side-Encryption-Iv",
"X-Minio-Replication-Encrypted-Multipart",
"X-Minio-Replication-Actual-Object-Size",
ReplicationSsecChecksumHeader,
// Add more supported headers here.
}
@ -110,6 +109,7 @@ var replicationToInternalHeaders = map[string]string{
"X-Minio-Replication-Server-Side-Encryption-Iv": "X-Minio-Internal-Server-Side-Encryption-Iv",
"X-Minio-Replication-Encrypted-Multipart": "X-Minio-Internal-Encrypted-Multipart",
"X-Minio-Replication-Actual-Object-Size": "X-Minio-Internal-Actual-Object-Size",
ReplicationSsecChecksumHeader: ReplicationSsecChecksumHeader,
// Add more supported headers here.
}
@ -206,8 +206,8 @@ func extractMetadataFromMime(ctx context.Context, v textproto.MIMEHeader, m map[
for _, supportedHeader := range supportedHeaders {
value, ok := nv[http.CanonicalHeaderKey(supportedHeader)]
if ok {
if slices.Contains(maps.Keys(replicationToInternalHeaders), supportedHeader) {
m[replicationToInternalHeaders[supportedHeader]] = strings.Join(value, ",")
if v, ok := replicationToInternalHeaders[supportedHeader]; ok {
m[v] = strings.Join(value, ",")
} else {
m[supportedHeader] = strings.Join(value, ",")
}

View File

@ -233,7 +233,7 @@ func (o ObjectInfo) ExpiresStr() string {
// ArchiveInfo returns any saved zip archive meta information.
// It will be decrypted if needed.
func (o *ObjectInfo) ArchiveInfo() []byte {
func (o *ObjectInfo) ArchiveInfo(h http.Header) []byte {
if len(o.UserDefined) == 0 {
return nil
}
@ -243,7 +243,7 @@ func (o *ObjectInfo) ArchiveInfo() []byte {
}
data := []byte(z)
if v, ok := o.UserDefined[archiveTypeMetadataKey]; ok && v == archiveTypeEnc {
decrypted, err := o.metadataDecrypter()(archiveTypeEnc, data)
decrypted, err := o.metadataDecrypter(h)(archiveTypeEnc, data)
if err != nil {
encLogIf(GlobalContext, err)
return nil
@ -326,6 +326,7 @@ func (ri ReplicateObjectInfo) ToObjectInfo() ObjectInfo {
VersionPurgeStatusInternal: ri.VersionPurgeStatusInternal,
DeleteMarker: true,
UserDefined: map[string]string{},
Checksum: ri.Checksum,
}
}
@ -357,6 +358,7 @@ type ReplicateObjectInfo struct {
TargetStatuses map[string]replication.StatusType
TargetPurgeStatuses map[string]VersionPurgeStatusType
ReplicationTimestamp time.Time
Checksum []byte
}
// MultipartInfo captures metadata information about the uploadId

View File

@ -1896,9 +1896,9 @@ func (z *PartInfo) Msgsize() (s int) {
// MarshalMsg implements msgp.Marshaler
func (z *ReplicateObjectInfo) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
// map header, size 25
// map header, size 26
// string "Name"
o = append(o, 0xde, 0x0, 0x19, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
o = append(o, 0xde, 0x0, 0x1a, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
o = msgp.AppendString(o, z.Name)
// string "Bucket"
o = append(o, 0xa6, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74)
@ -2012,6 +2012,9 @@ func (z *ReplicateObjectInfo) MarshalMsg(b []byte) (o []byte, err error) {
// string "ReplicationTimestamp"
o = append(o, 0xb4, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70)
o = msgp.AppendTime(o, z.ReplicationTimestamp)
// string "Checksum"
o = append(o, 0xa8, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d)
o = msgp.AppendBytes(o, z.Checksum)
return
}
@ -2231,6 +2234,12 @@ func (z *ReplicateObjectInfo) UnmarshalMsg(bts []byte) (o []byte, err error) {
err = msgp.WrapError(err, "ReplicationTimestamp")
return
}
case "Checksum":
z.Checksum, bts, err = msgp.ReadBytesBytes(bts, z.Checksum)
if err != nil {
err = msgp.WrapError(err, "Checksum")
return
}
default:
bts, err = msgp.Skip(bts)
if err != nil {
@ -2259,7 +2268,7 @@ func (z *ReplicateObjectInfo) Msgsize() (s int) {
s += msgp.StringPrefixSize + len(za0003) + za0004.Msgsize()
}
}
s += 21 + msgp.TimeSize
s += 21 + msgp.TimeSize + 9 + msgp.BytesPrefixSize + len(z.Checksum)
return
}

View File

@ -755,7 +755,7 @@ type ObjReaderFn func(inputReader io.Reader, h http.Header, cleanupFns ...func()
// are called on Close() in FIFO order as passed in ObjReadFn(). NOTE: It is
// assumed that clean up functions do not panic (otherwise, they may
// not all run!).
func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, opts ObjectOptions) (
func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, opts ObjectOptions, h http.Header) (
fn ObjReaderFn, off, length int64, err error,
) {
if opts.CheckPrecondFn != nil && opts.CheckPrecondFn(oi) {
@ -810,7 +810,9 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, opts ObjectOptions) (
return b, nil
}
if isEncrypted {
decrypt = oi.compressionIndexDecrypt
decrypt = func(b []byte) ([]byte, error) {
return oi.compressionIndexDecrypt(b, h)
}
}
// In case of range based queries on multiparts, the offset and length are reduced.
off, decOff, firstPart, decryptSkip, seqNum = getCompressedOffsets(oi, off, decrypt)
@ -982,8 +984,8 @@ func compressionIndexEncrypter(key crypto.ObjectKey, input func() []byte) func()
}
// compressionIndexDecrypt reverses compressionIndexEncrypter.
func (o *ObjectInfo) compressionIndexDecrypt(input []byte) ([]byte, error) {
return o.metadataDecrypter()("compression-index", input)
func (o *ObjectInfo) compressionIndexDecrypt(input []byte, h http.Header) ([]byte, error) {
return o.metadataDecrypter(h)("compression-index", input)
}
// SealMD5CurrFn seals md5sum with object encryption key and returns sealed

View File

@ -335,7 +335,7 @@ func isETagEqual(left, right string) bool {
// setPutObjHeaders sets all the necessary headers returned back
// upon a success Put/Copy/CompleteMultipart/Delete requests
// to activate delete only headers set delete as true
func setPutObjHeaders(w http.ResponseWriter, objInfo ObjectInfo, delete bool) {
func setPutObjHeaders(w http.ResponseWriter, objInfo ObjectInfo, delete bool, h http.Header) {
// We must not use the http.Header().Set method here because some (broken)
// clients expect the ETag header key to be literally "ETag" - not "Etag" (case-sensitive).
// Therefore, we have to set the ETag directly as map entry.
@ -357,7 +357,7 @@ func setPutObjHeaders(w http.ResponseWriter, objInfo ObjectInfo, delete bool) {
lc.SetPredictionHeaders(w, objInfo.ToLifecycleOpts())
}
}
hash.AddChecksumHeader(w, objInfo.decryptChecksums(0))
hash.AddChecksumHeader(w, objInfo.decryptChecksums(0, h))
}
func deleteObjectVersions(ctx context.Context, o ObjectLayer, bucket string, toDel []ObjectToDelete, lcEvent lifecycle.Event) {

View File

@ -617,7 +617,7 @@ func (api objectAPIHandlers) getObjectHandler(ctx context.Context, objectAPI Obj
if r.Header.Get(xhttp.AmzChecksumMode) == "ENABLED" && rs == nil {
// AWS S3 silently drops checksums on range requests.
hash.AddChecksumHeader(w, objInfo.decryptChecksums(opts.PartNumber))
hash.AddChecksumHeader(w, objInfo.decryptChecksums(opts.PartNumber, r.Header))
}
var buf *bytebufferpool.ByteBuffer
@ -764,7 +764,7 @@ func (api objectAPIHandlers) getObjectAttributesHandler(ctx context.Context, obj
w.Header().Del(xhttp.ContentType)
if _, ok := opts.ObjectAttributes[xhttp.Checksum]; ok {
chkSums := objInfo.decryptChecksums(0)
chkSums := objInfo.decryptChecksums(0, r.Header)
// AWS does not appear to append part number on this API call.
switch {
case chkSums["CRC32"] != "":
@ -795,7 +795,7 @@ func (api objectAPIHandlers) getObjectAttributesHandler(ctx context.Context, obj
OA.StorageClass = filterStorageClass(ctx, objInfo.StorageClass)
}
objInfo.decryptPartsChecksums()
objInfo.decryptPartsChecksums(r.Header)
if _, ok := opts.ObjectAttributes[xhttp.ObjectParts]; ok {
OA.ObjectParts = new(objectAttributesParts)
@ -1182,7 +1182,7 @@ func (api objectAPIHandlers) headObjectHandler(ctx context.Context, objectAPI Ob
if r.Header.Get(xhttp.AmzChecksumMode) == "ENABLED" && rs == nil {
// AWS S3 silently drops checksums on range requests.
hash.AddChecksumHeader(w, objInfo.decryptChecksums(opts.PartNumber))
hash.AddChecksumHeader(w, objInfo.decryptChecksums(opts.PartNumber, r.Header))
}
// Set standard object headers.
@ -1942,7 +1942,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.ObjectReplicationType)
}
setPutObjHeaders(w, objInfo, false)
setPutObjHeaders(w, objInfo, false, r.Header)
// We must not use the http.Header().Set method here because some (broken)
// clients expect the x-amz-copy-source-version-id header key to be literally
// "x-amz-copy-source-version-id"- not in canonicalized form, preserve it.
@ -2276,11 +2276,6 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
return
}
if crypto.SSEC.IsRequested(r.Header) && isCompressible(r.Header, object) {
writeErrorResponse(ctx, w, toAPIError(ctx, crypto.ErrIncompatibleEncryptionWithCompression), r.URL)
return
}
reader, objectEncryptionKey, err = EncryptRequest(hashReader, r, bucket, object, metadata)
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
@ -2365,7 +2360,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.ObjectReplicationType)
}
setPutObjHeaders(w, objInfo, false)
setPutObjHeaders(w, objInfo, false, r.Header)
defer func() {
var data []byte
@ -2957,7 +2952,7 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
defer globalCacheConfig.Delete(bucket, object)
setPutObjHeaders(w, objInfo, true)
setPutObjHeaders(w, objInfo, true, r.Header)
writeSuccessNoContent(w)
eventName := event.ObjectRemovedDelete

View File

@ -2914,7 +2914,7 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s
s3MD5 := getCompleteMultipartMD5(inputParts[3].parts)
// generating the response body content for the success case.
successResponse := generateCompleteMultipartUploadResponse(bucketName, objectName, getGetObjectURL("", bucketName, objectName), ObjectInfo{ETag: s3MD5})
successResponse := generateCompleteMultipartUploadResponse(bucketName, objectName, getGetObjectURL("", bucketName, objectName), ObjectInfo{ETag: s3MD5}, nil)
encodedSuccessResponse := encodeResponse(successResponse)
ctx := context.Background()

View File

@ -116,11 +116,6 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
return
}
if crypto.SSEC.IsRequested(r.Header) && isCompressible(r.Header, object) {
writeErrorResponse(ctx, w, toAPIError(ctx, crypto.ErrIncompatibleEncryptionWithCompression), r.URL)
return
}
_, sourceReplReq := r.Header[xhttp.MinIOSourceReplicationRequest]
ssecRepHeaders := []string{
"X-Minio-Replication-Server-Side-Encryption-Seal-Algorithm",
@ -1029,7 +1024,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
}
}
setPutObjHeaders(w, objInfo, false)
setPutObjHeaders(w, objInfo, false, r.Header)
if dsc := mustReplicate(ctx, bucket, object, objInfo.getMustReplicateOptions(replication.ObjectReplicationType, opts)); dsc.ReplicateAny() {
scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.ObjectReplicationType)
}
@ -1041,7 +1036,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
// Get object location.
location := getObjectLocation(r, globalDomainNames, bucket, object)
// Generate complete multipart response.
response := generateCompleteMultipartUploadResponse(bucket, object, location, objInfo)
response := generateCompleteMultipartUploadResponse(bucket, object, location, objInfo, r.Header)
encodedSuccessResponse := encodeResponse(response)
// Write success response.

View File

@ -142,7 +142,7 @@ func (api objectAPIHandlers) getObjectInArchiveFileHandler(ctx context.Context,
return
}
zipInfo := zipObjInfo.ArchiveInfo()
zipInfo := zipObjInfo.ArchiveInfo(r.Header)
if len(zipInfo) == 0 {
opts.EncryptFn, err = zipObjInfo.metadataEncryptFn(r.Header)
if err != nil {
@ -233,7 +233,7 @@ func (api objectAPIHandlers) getObjectInArchiveFileHandler(ctx context.Context,
}
// listObjectsV2InArchive generates S3 listing result ListObjectsV2Info from zip file, all parameters are already validated by the caller.
func listObjectsV2InArchive(ctx context.Context, objectAPI ObjectLayer, bucket, prefix, token, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (ListObjectsV2Info, error) {
func listObjectsV2InArchive(ctx context.Context, objectAPI ObjectLayer, bucket, prefix, token, delimiter string, maxKeys int, startAfter string, h http.Header) (ListObjectsV2Info, error) {
zipPath, _, err := splitZipExtensionPath(prefix)
if err != nil {
// Return empty listing
@ -246,7 +246,7 @@ func listObjectsV2InArchive(ctx context.Context, objectAPI ObjectLayer, bucket,
return ListObjectsV2Info{}, nil
}
zipInfo := zipObjInfo.ArchiveInfo()
zipInfo := zipObjInfo.ArchiveInfo(h)
if len(zipInfo) == 0 {
// Always update the latest version
zipInfo, err = updateObjectMetadataWithZipInfo(ctx, objectAPI, bucket, zipPath, ObjectOptions{})
@ -438,7 +438,7 @@ func (api objectAPIHandlers) headObjectInArchiveFileHandler(ctx context.Context,
return
}
zipInfo := zipObjInfo.ArchiveInfo()
zipInfo := zipObjInfo.ArchiveInfo(r.Header)
if len(zipInfo) == 0 {
opts.EncryptFn, err = zipObjInfo.metadataEncryptFn(r.Header)
if err != nil {

View File

@ -30,10 +30,10 @@ import (
"github.com/klauspost/compress/zip"
"github.com/minio/madmin-go/v3"
minio "github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7"
cr "github.com/minio/minio-go/v7/pkg/credentials"
"github.com/minio/minio-go/v7/pkg/set"
ldap "github.com/minio/pkg/v3/ldap"
"github.com/minio/pkg/v3/ldap"
"golang.org/x/exp/slices"
)
@ -50,6 +50,7 @@ func runAllIAMSTSTests(suite *TestSuiteIAM, c *check) {
}
func TestIAMInternalIDPSTSServerSuite(t *testing.T) {
t.Skip("FIXME: Skipping internal IDP tests. Flaky test, needs to be fixed.")
baseTestCases := []TestSuiteCommon{
// Init and run test on ErasureSD backend with signature v4.
{serverType: "ErasureSD", signer: signerV4},

View File

@ -69,7 +69,7 @@ echo "done"
# Enable compression for site minio1
./mc admin config set minio1 compression enable=on extensions=".txt" --insecure
./mc admin config set minio1 compression allow_encryption=on --insecure
./mc admin config set minio1 compression allow_encryption=off --insecure
# Create bucket in source cluster
echo "Create bucket in source MinIO instance"
@ -82,11 +82,12 @@ echo "Loading objects to source MinIO instance"
./mc cp /tmp/data/defpartsize minio1/test-bucket/defpartsize --enc-c "minio1/test-bucket/defpartsize=${TEST_MINIO_ENC_KEY}" --insecure
# Below should fail as compression and SSEC used at the same time
RESULT=$({ ./mc put /tmp/data/mpartobj.txt minio1/test-bucket/mpartobj.txt --enc-c "minio1/test-bucket/mpartobj.txt=${TEST_MINIO_ENC_KEY}" --insecure; } 2>&1)
if [[ ${RESULT} != *"Server side encryption specified with SSE-C with compression not allowed"* ]]; then
echo "BUG: Loading an SSE-C object to site with compression should fail. Succeeded though."
exit_1
fi
# DISABLED: We must check the response header to see if compression was actually applied
#RESULT=$({ ./mc put /tmp/data/mpartobj.txt minio1/test-bucket/mpartobj.txt --enc-c "minio1/test-bucket/mpartobj.txt=${TEST_MINIO_ENC_KEY}" --insecure; } 2>&1)
#if [[ ${RESULT} != *"Server side encryption specified with SSE-C with compression not allowed"* ]]; then
# echo "BUG: Loading an SSE-C object to site with compression should fail. Succeeded though."
# exit_1
#fi
# Add replication site
./mc admin replicate add minio1 minio2 --insecure

View File

@ -48,6 +48,9 @@ const (
// the KMS.
MetaDataEncryptionKey = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key"
// MetaSsecCRC is the encrypted checksum of the SSE-C encrypted object.
MetaSsecCRC = "X-Minio-Internal-Ssec-Crc"
// MetaContext is the KMS context provided by a client when encrypting an
// object with SSE-KMS. A client may not send a context in which case the
// MetaContext will not be present.
@ -106,6 +109,7 @@ func RemoveInternalEntries(metadata map[string]string) {
delete(metadata, MetaSealedKeyKMS)
delete(metadata, MetaKeyID)
delete(metadata, MetaDataEncryptionKey)
delete(metadata, MetaSsecCRC)
}
// IsSourceEncrypted returns true if the source is encrypted