mirror of
				https://github.com/minio/minio.git
				synced 2025-10-29 15:55:00 -04:00 
			
		
		
		
	fix replication of checksum when encryption is enabled (#20161)
- Adding functional tests - Return checksum header on GET/HEAD, previously this was returning InvalidPartNumber error
This commit is contained in:
		
							parent
							
								
									3ae104edae
								
							
						
					
					
						commit
						6651c655cb
					
				| @ -774,21 +774,6 @@ func (m caseInsensitiveMap) Lookup(key string) (string, bool) { | ||||
| 	return "", false | ||||
| } | ||||
| 
 | ||||
| func getCRCMeta(oi ObjectInfo, partNum int, h http.Header) map[string]string { | ||||
| 	meta := make(map[string]string) | ||||
| 	cs := oi.decryptChecksums(partNum, h) | ||||
| 	for k, v := range cs { | ||||
| 		cksum := hash.NewChecksumString(k, v) | ||||
| 		if cksum == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		if cksum.Valid() { | ||||
| 			meta[cksum.Type.Key()] = v | ||||
| 		} | ||||
| 	} | ||||
| 	return meta | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| @ -797,11 +782,6 @@ func putReplicationOpts(ctx context.Context, sc string, objInfo ObjectInfo, part | ||||
| 		// In case of SSE-C objects copy the allowed internal headers as well | ||||
| 		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, nil) { | ||||
| 						meta[k] = v | ||||
| 					} | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 			if isStandardHeader(k) { | ||||
| @ -820,8 +800,12 @@ func putReplicationOpts(ctx context.Context, sc string, objInfo ObjectInfo, part | ||||
| 		if isSSEC { | ||||
| 			meta[ReplicationSsecChecksumHeader] = base64.StdEncoding.EncodeToString(objInfo.Checksum) | ||||
| 		} else { | ||||
| 			for k, v := range getCRCMeta(objInfo, 0, nil) { | ||||
| 				meta[k] = v | ||||
| 			for _, pi := range objInfo.Parts { | ||||
| 				if pi.Number == partNum { | ||||
| 					for k, v := range pi.Checksums { | ||||
| 						meta[k] = v | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -1675,8 +1659,7 @@ func replicateObjectWithMultipart(ctx context.Context, c *minio.Core, bucket, ob | ||||
| 		cHeader := http.Header{} | ||||
| 		cHeader.Add(xhttp.MinIOSourceReplicationRequest, "true") | ||||
| 		if !isSSEC { | ||||
| 			crc := getCRCMeta(objInfo, partInfo.Number, nil) // No SSE-C keys here. | ||||
| 			for k, v := range crc { | ||||
| 			for k, v := range partInfo.Checksums { | ||||
| 				cHeader.Add(k, v) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -1172,6 +1172,14 @@ func (o *ObjectInfo) decryptChecksums(part int, h http.Header) map[string]string | ||||
| 	if len(data) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if part > 0 && !crypto.SSEC.IsEncrypted(o.UserDefined) { | ||||
| 		// already decrypted in ToObjectInfo for multipart objects | ||||
| 		for _, pi := range o.Parts { | ||||
| 			if pi.Number == part { | ||||
| 				return pi.Checksums | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if _, encrypted := crypto.IsEncrypted(o.UserDefined); encrypted { | ||||
| 		decrypted, err := o.metadataDecrypter(h)("object-checksum", data) | ||||
| 		if err != nil { | ||||
|  | ||||
| @ -174,6 +174,7 @@ func (fi FileInfo) ToObjectInfo(bucket, object string, versioned bool) ObjectInf | ||||
| 		} | ||||
| 	} | ||||
| 	objInfo.Checksum = fi.Checksum | ||||
| 	objInfo.decryptPartsChecksums(nil) | ||||
| 	objInfo.Inlined = fi.InlineData() | ||||
| 	// Success. | ||||
| 	return objInfo | ||||
|  | ||||
| @ -248,10 +248,19 @@ func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Requ | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if the part number is correct. | ||||
| 	if opts.PartNumber > 1 && opts.PartNumber > len(objInfo.Parts) { | ||||
| 		// According to S3 we don't need to set any object information here. | ||||
| 		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidPartNumber), r.URL) | ||||
| 		return true | ||||
| 	if opts.PartNumber > 1 { | ||||
| 		partFound := false | ||||
| 		for _, pi := range objInfo.Parts { | ||||
| 			if pi.Number == opts.PartNumber { | ||||
| 				partFound = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if !partFound { | ||||
| 			// According to S3 we don't need to set any object information here. | ||||
| 			writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidPartNumber), r.URL) | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// If-None-Match : Return the object only if its entity tag (ETag) is different from the | ||||
|  | ||||
| @ -65,8 +65,8 @@ echo "done" | ||||
| 
 | ||||
| # Start MinIO instances | ||||
| echo -n "Starting MinIO instances ..." | ||||
| CI=on MINIO_ROOT_USER=minio MINIO_ROOT_PASSWORD=minio123 minio server --certs-dir /tmp/certs --address ":9001" --console-address ":10000" /tmp/minio1/{1...4}/disk{1...4} /tmp/minio1/{5...8}/disk{1...4} >/tmp/minio1_1.log 2>&1 & | ||||
| CI=on MINIO_ROOT_USER=minio MINIO_ROOT_PASSWORD=minio123 minio server --certs-dir /tmp/certs --address ":9002" --console-address ":11000" /tmp/minio2/{1...4}/disk{1...4} /tmp/minio2/{5...8}/disk{1...4} >/tmp/minio2_1.log 2>&1 & | ||||
| CI=on MINIO_KMS_SECRET_KEY=minio-default-key:IyqsU3kMFloCNup4BsZtf/rmfHVcTgznO2F25CkEH1g= MINIO_ROOT_USER=minio MINIO_ROOT_PASSWORD=minio123 minio server --certs-dir /tmp/certs --address ":9001" --console-address ":10000" /tmp/minio1/{1...4}/disk{1...4} /tmp/minio1/{5...8}/disk{1...4} >/tmp/minio1_1.log 2>&1 & | ||||
| CI=on MINIO_KMS_SECRET_KEY=minio-default-key:IyqsU3kMFloCNup4BsZtf/rmfHVcTgznO2F25CkEH1g= MINIO_ROOT_USER=minio MINIO_ROOT_PASSWORD=minio123 minio server --certs-dir /tmp/certs --address ":9002" --console-address ":11000" /tmp/minio2/{1...4}/disk{1...4} /tmp/minio2/{5...8}/disk{1...4} >/tmp/minio2_1.log 2>&1 & | ||||
| echo "done" | ||||
| 
 | ||||
| if [ ! -f ./mc ]; then | ||||
| @ -99,7 +99,7 @@ sleep 30 | ||||
| echo "Create bucket in source MinIO instance" | ||||
| ./mc mb minio1/test-bucket --insecure | ||||
| 
 | ||||
| # Load objects to source site | ||||
| # Load objects to source site with checksum header | ||||
| echo "Loading objects to source MinIO instance" | ||||
| OBJ_CHKSUM=$(openssl dgst -sha256 -binary </tmp/data/obj | base64) | ||||
| aws s3api --endpoint-url=https://localhost:9001 put-object --checksum-algorithm SHA256 --checksum-sha256 "${OBJ_CHKSUM}" --bucket test-bucket --key obj --body /tmp/data/obj --no-verify-ssl --profile enterprise | ||||
| @ -176,4 +176,87 @@ if [ "${SRC_OBJ_2_ETAG}" != "${DEST_OBJ_2_ETAG}" ]; then | ||||
| 	exit_1 | ||||
| fi | ||||
| 
 | ||||
| echo "Set default encryption on " | ||||
| 
 | ||||
| # test if checksum header is replicated for encrypted objects | ||||
| # Enable SSE KMS for test-bucket bucket | ||||
| ./mc encrypt set sse-kms minio-default-key minio1/test-bucket --insecure | ||||
| 
 | ||||
| # Load objects to source site with checksum header | ||||
| echo "Loading objects to source MinIO instance" | ||||
| OBJ_CHKSUM=$(openssl dgst -sha256 -binary </tmp/data/obj | base64) | ||||
| aws s3api --endpoint-url=https://localhost:9001 put-object --checksum-algorithm SHA256 --checksum-sha256 "${OBJ_CHKSUM}" --bucket test-bucket --key obj2 --body /tmp/data/obj --no-verify-ssl --profile enterprise | ||||
| 
 | ||||
| split -n 10 /tmp/data/mpartobj | ||||
| CREATE_MPART_OUT=$(aws s3api --endpoint-url=https://localhost:9001 create-multipart-upload --bucket test-bucket --key mpartobj2 --checksum-algorithm SHA256 --no-verify-ssl --profile enterprise) | ||||
| UPLOAD_ID=$(echo "${CREATE_MPART_OUT}" | jq '.UploadId' | sed 's/"//g') | ||||
| 
 | ||||
| PARTS="" | ||||
| for idx in {1..10}; do | ||||
| 	F_SUFFIX=$(num_to_alpha "$idx") | ||||
| 	PART_CHKSUM=$(openssl dgst -sha256 -binary <"xa${F_SUFFIX}" | base64) | ||||
| 	UPLOAD_PART_OUT=$(aws s3api --endpoint-url=https://localhost:9001 upload-part --checksum-algorithm SHA256 --checksum-sha256 "${PART_CHKSUM}" --bucket test-bucket --key mpartobj2 --part-number "${idx}" --body "xa${F_SUFFIX}" --upload-id "${UPLOAD_ID}" --no-verify-ssl --profile enterprise) | ||||
| 	PART_ETAG=$(echo "${UPLOAD_PART_OUT}" | jq '.ETag') | ||||
| 	if [ "${idx}" == 10 ]; then | ||||
| 		PARTS="${PARTS}{\"ETag\": ${PART_ETAG}, \"ChecksumSHA256\": \"${PART_CHKSUM}\", \"PartNumber\": ${idx}}" | ||||
| 	else | ||||
| 		PARTS="${PARTS}{\"ETag\": ${PART_ETAG}, \"ChecksumSHA256\": \"${PART_CHKSUM}\", \"PartNumber\": ${idx}}," | ||||
| 	fi | ||||
| done | ||||
| 
 | ||||
| echo "{\"Parts\":[${PARTS}]}" >fileparts.json | ||||
| jq <fileparts.json | ||||
| aws s3api --endpoint-url=https://localhost:9001 complete-multipart-upload --multipart-upload file://fileparts.json --bucket test-bucket --key mpartobj2 --upload-id "${UPLOAD_ID}" --no-verify-ssl --profile enterprise | ||||
| sleep 120 | ||||
| 
 | ||||
| # List the objects from replicated site | ||||
| echo "Objects from replicated site" | ||||
| ./mc ls minio2/test-bucket --insecure | ||||
| count1=$(./mc ls minio2/test-bucket/obj2 --insecure | wc -l) | ||||
| if [ "${count1}" -ne 1 ]; then | ||||
| 	echo "BUG: object minio1/test-bucket/obj2 not replicated" | ||||
| 	exit_1 | ||||
| fi | ||||
| count2=$(./mc ls minio2/test-bucket/mpartobj2 --insecure | wc -l) | ||||
| if [ "${count2}" -ne 1 ]; then | ||||
| 	echo "BUG: object minio1/test-bucket/mpartobj2 not replicated" | ||||
| 	exit_1 | ||||
| fi | ||||
| 
 | ||||
| # Stat the objects from source site | ||||
| echo "Stat minio1/test-bucket/obj2" | ||||
| SRC_OUT_1=$(aws s3api --endpoint-url https://localhost:9001 head-object --bucket test-bucket --key obj2 --checksum-mode ENABLED --no-verify-ssl --profile enterprise) | ||||
| SRC_OUT_2=$(aws s3api --endpoint-url https://localhost:9001 head-object --bucket test-bucket --key mpartobj2 --checksum-mode ENABLED --no-verify-ssl --profile enterprise) | ||||
| SRC_OBJ_1_CHKSUM=$(echo "${SRC_OUT_1}" | jq '.ChecksumSHA256') | ||||
| SRC_OBJ_2_CHKSUM=$(echo "${SRC_OUT_2}" | jq '.ChecksumSHA256') | ||||
| SRC_OBJ_1_ETAG=$(echo "${SRC_OUT_1}" | jq '.ETag') | ||||
| SRC_OBJ_2_ETAG=$(echo "${SRC_OUT_2}" | jq '.ETag') | ||||
| 
 | ||||
| # Stat the objects from replicated site | ||||
| echo "Stat minio2/test-bucket/obj2" | ||||
| DEST_OUT_1=$(aws s3api --endpoint-url https://localhost:9002 head-object --bucket test-bucket --key obj2 --checksum-mode ENABLED --no-verify-ssl --profile enterprise) | ||||
| DEST_OUT_2=$(aws s3api --endpoint-url https://localhost:9002 head-object --bucket test-bucket --key mpartobj2 --checksum-mode ENABLED --no-verify-ssl --profile enterprise) | ||||
| DEST_OBJ_1_CHKSUM=$(echo "${DEST_OUT_1}" | jq '.ChecksumSHA256') | ||||
| DEST_OBJ_2_CHKSUM=$(echo "${DEST_OUT_2}" | jq '.ChecksumSHA256') | ||||
| DEST_OBJ_1_ETAG=$(echo "${DEST_OUT_1}" | jq '.ETag') | ||||
| DEST_OBJ_2_ETAG=$(echo "${DEST_OUT_2}" | jq '.ETag') | ||||
| 
 | ||||
| # Check the replication of checksums and etags | ||||
| if [ "${SRC_OBJ_1_CHKSUM}" != "${DEST_OBJ_1_CHKSUM}" ]; then | ||||
| 	echo "BUG: Checksums dont match for 'obj2'. Source: ${SRC_OBJ_1_CHKSUM}, Destination: ${DEST_OBJ_1_CHKSUM}" | ||||
| 	exit_1 | ||||
| fi | ||||
| if [ "${SRC_OBJ_2_CHKSUM}" != "${DEST_OBJ_2_CHKSUM}" ]; then | ||||
| 	echo "BUG: Checksums dont match for 'mpartobj2'. Source: ${SRC_OBJ_2_CHKSUM}, Destination: ${DEST_OBJ_2_CHKSUM}" | ||||
| 	exit_1 | ||||
| fi | ||||
| if [ "${SRC_OBJ_1_ETAG}" != "${DEST_OBJ_1_ETAG}" ]; then | ||||
| 	echo "BUG: Etags dont match for 'obj2'. Source: ${SRC_OBJ_1_ETAG}, Destination: ${DEST_OBJ_1_ETAG}" | ||||
| 	exit_1 | ||||
| fi | ||||
| if [ "${SRC_OBJ_2_ETAG}" != "${DEST_OBJ_2_ETAG}" ]; then | ||||
| 	echo "BUG: Etags dont match for 'mpartobj2'. Source: ${SRC_OBJ_2_ETAG}, Destination: ${DEST_OBJ_2_ETAG}" | ||||
| 	exit_1 | ||||
| fi | ||||
| 
 | ||||
| cleanup | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user