mirror of
https://github.com/minio/minio.git
synced 2025-01-11 15:03:22 -05:00
5a80cbec2a
This PR adds pass-through, single encryption at gateway and double encryption support (gateway encryption with pass through of SSE headers to backend). If KMS is set up (either with Vault as KMS or using MINIO_SSE_MASTER_KEY),gateway will automatically perform single encryption. If MINIO_GATEWAY_SSE is set up in addition to Vault KMS, double encryption is performed.When neither KMS nor MINIO_GATEWAY_SSE is set, do a pass through to backend. When double encryption is specified, MINIO_GATEWAY_SSE can be set to "C" for SSE-C encryption at gateway and backend, "S3" for SSE-S3 encryption at gateway/backend or both to support more than one option. Fixes #6323, #6696
373 lines
10 KiB
Go
373 lines
10 KiB
Go
/*
|
|
* Minio Cloud Storage, (C) 2017 Minio, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/minio/minio/cmd/logger"
|
|
"github.com/minio/minio/pkg/hash"
|
|
|
|
minio "github.com/minio/minio-go"
|
|
)
|
|
|
|
var (
|
|
// CanonicalizeETag provides canonicalizeETag function alias.
|
|
CanonicalizeETag = canonicalizeETag
|
|
|
|
// MustGetUUID function alias.
|
|
MustGetUUID = mustGetUUID
|
|
|
|
// IsMinAllowedPartSize function alias.
|
|
IsMinAllowedPartSize = isMinAllowedPartSize
|
|
|
|
// GetCompleteMultipartMD5 functon alias.
|
|
GetCompleteMultipartMD5 = getCompleteMultipartMD5
|
|
|
|
// Contains function alias.
|
|
Contains = contains
|
|
|
|
// ExtractETag provides extractETag function alias.
|
|
ExtractETag = extractETag
|
|
// CleanMetadataKeys provides cleanMetadataKeys function alias.
|
|
CleanMetadataKeys = cleanMetadataKeys
|
|
)
|
|
|
|
// StatInfo - alias for statInfo
|
|
type StatInfo struct {
|
|
statInfo
|
|
}
|
|
|
|
// AnonErrToObjectErr - converts standard http codes into meaningful object layer errors.
|
|
func AnonErrToObjectErr(statusCode int, params ...string) error {
|
|
bucket := ""
|
|
object := ""
|
|
if len(params) >= 1 {
|
|
bucket = params[0]
|
|
}
|
|
if len(params) == 2 {
|
|
object = params[1]
|
|
}
|
|
|
|
switch statusCode {
|
|
case http.StatusNotFound:
|
|
if object != "" {
|
|
return ObjectNotFound{bucket, object}
|
|
}
|
|
return BucketNotFound{Bucket: bucket}
|
|
case http.StatusBadRequest:
|
|
if object != "" {
|
|
return ObjectNameInvalid{bucket, object}
|
|
}
|
|
return BucketNameInvalid{Bucket: bucket}
|
|
case http.StatusForbidden:
|
|
fallthrough
|
|
case http.StatusUnauthorized:
|
|
return AllAccessDisabled{bucket, object}
|
|
}
|
|
|
|
return errUnexpected
|
|
}
|
|
|
|
// FromMinioClientMetadata converts minio metadata to map[string]string
|
|
func FromMinioClientMetadata(metadata map[string][]string) map[string]string {
|
|
mm := map[string]string{}
|
|
for k, v := range metadata {
|
|
mm[http.CanonicalHeaderKey(k)] = v[0]
|
|
}
|
|
return mm
|
|
}
|
|
|
|
// FromMinioClientObjectPart converts minio ObjectPart to PartInfo
|
|
func FromMinioClientObjectPart(op minio.ObjectPart) PartInfo {
|
|
return PartInfo{
|
|
Size: op.Size,
|
|
ETag: canonicalizeETag(op.ETag),
|
|
LastModified: op.LastModified,
|
|
PartNumber: op.PartNumber,
|
|
}
|
|
}
|
|
|
|
// FromMinioClientListPartsInfo converts minio ListObjectPartsResult to ListPartsInfo
|
|
func FromMinioClientListPartsInfo(lopr minio.ListObjectPartsResult) ListPartsInfo {
|
|
// Convert minio ObjectPart to PartInfo
|
|
fromMinioClientObjectParts := func(parts []minio.ObjectPart) []PartInfo {
|
|
toParts := make([]PartInfo, len(parts))
|
|
for i, part := range parts {
|
|
toParts[i] = FromMinioClientObjectPart(part)
|
|
}
|
|
return toParts
|
|
}
|
|
|
|
return ListPartsInfo{
|
|
UploadID: lopr.UploadID,
|
|
Bucket: lopr.Bucket,
|
|
Object: lopr.Key,
|
|
StorageClass: "",
|
|
PartNumberMarker: lopr.PartNumberMarker,
|
|
NextPartNumberMarker: lopr.NextPartNumberMarker,
|
|
MaxParts: lopr.MaxParts,
|
|
IsTruncated: lopr.IsTruncated,
|
|
EncodingType: lopr.EncodingType,
|
|
Parts: fromMinioClientObjectParts(lopr.ObjectParts),
|
|
}
|
|
}
|
|
|
|
// FromMinioClientListMultipartsInfo converts minio ListMultipartUploadsResult to ListMultipartsInfo
|
|
func FromMinioClientListMultipartsInfo(lmur minio.ListMultipartUploadsResult) ListMultipartsInfo {
|
|
uploads := make([]MultipartInfo, len(lmur.Uploads))
|
|
|
|
for i, um := range lmur.Uploads {
|
|
uploads[i] = MultipartInfo{
|
|
Object: um.Key,
|
|
UploadID: um.UploadID,
|
|
Initiated: um.Initiated,
|
|
}
|
|
}
|
|
|
|
commonPrefixes := make([]string, len(lmur.CommonPrefixes))
|
|
for i, cp := range lmur.CommonPrefixes {
|
|
commonPrefixes[i] = cp.Prefix
|
|
}
|
|
|
|
return ListMultipartsInfo{
|
|
KeyMarker: lmur.KeyMarker,
|
|
UploadIDMarker: lmur.UploadIDMarker,
|
|
NextKeyMarker: lmur.NextKeyMarker,
|
|
NextUploadIDMarker: lmur.NextUploadIDMarker,
|
|
MaxUploads: int(lmur.MaxUploads),
|
|
IsTruncated: lmur.IsTruncated,
|
|
Uploads: uploads,
|
|
Prefix: lmur.Prefix,
|
|
Delimiter: lmur.Delimiter,
|
|
CommonPrefixes: commonPrefixes,
|
|
EncodingType: lmur.EncodingType,
|
|
}
|
|
|
|
}
|
|
|
|
// FromMinioClientObjectInfo converts minio ObjectInfo to gateway ObjectInfo
|
|
func FromMinioClientObjectInfo(bucket string, oi minio.ObjectInfo) ObjectInfo {
|
|
userDefined := FromMinioClientMetadata(oi.Metadata)
|
|
userDefined["Content-Type"] = oi.ContentType
|
|
|
|
return ObjectInfo{
|
|
Bucket: bucket,
|
|
Name: oi.Key,
|
|
ModTime: oi.LastModified,
|
|
Size: oi.Size,
|
|
ETag: canonicalizeETag(oi.ETag),
|
|
UserDefined: userDefined,
|
|
ContentType: oi.ContentType,
|
|
ContentEncoding: oi.Metadata.Get("Content-Encoding"),
|
|
StorageClass: oi.StorageClass,
|
|
}
|
|
}
|
|
|
|
// FromMinioClientListBucketV2Result converts minio ListBucketResult to ListObjectsInfo
|
|
func FromMinioClientListBucketV2Result(bucket string, result minio.ListBucketV2Result) ListObjectsV2Info {
|
|
objects := make([]ObjectInfo, len(result.Contents))
|
|
|
|
for i, oi := range result.Contents {
|
|
objects[i] = FromMinioClientObjectInfo(bucket, oi)
|
|
}
|
|
|
|
prefixes := make([]string, len(result.CommonPrefixes))
|
|
for i, p := range result.CommonPrefixes {
|
|
prefixes[i] = p.Prefix
|
|
}
|
|
|
|
return ListObjectsV2Info{
|
|
IsTruncated: result.IsTruncated,
|
|
Prefixes: prefixes,
|
|
Objects: objects,
|
|
|
|
ContinuationToken: result.ContinuationToken,
|
|
NextContinuationToken: result.NextContinuationToken,
|
|
}
|
|
}
|
|
|
|
// FromMinioClientListBucketResult converts minio ListBucketResult to ListObjectsInfo
|
|
func FromMinioClientListBucketResult(bucket string, result minio.ListBucketResult) ListObjectsInfo {
|
|
objects := make([]ObjectInfo, len(result.Contents))
|
|
|
|
for i, oi := range result.Contents {
|
|
objects[i] = FromMinioClientObjectInfo(bucket, oi)
|
|
}
|
|
|
|
prefixes := make([]string, len(result.CommonPrefixes))
|
|
for i, p := range result.CommonPrefixes {
|
|
prefixes[i] = p.Prefix
|
|
}
|
|
|
|
return ListObjectsInfo{
|
|
IsTruncated: result.IsTruncated,
|
|
NextMarker: result.NextMarker,
|
|
Prefixes: prefixes,
|
|
Objects: objects,
|
|
}
|
|
}
|
|
|
|
// FromMinioClientListBucketResultToV2Info converts minio ListBucketResult to ListObjectsV2Info
|
|
func FromMinioClientListBucketResultToV2Info(bucket string, result minio.ListBucketResult) ListObjectsV2Info {
|
|
objects := make([]ObjectInfo, len(result.Contents))
|
|
|
|
for i, oi := range result.Contents {
|
|
objects[i] = FromMinioClientObjectInfo(bucket, oi)
|
|
}
|
|
|
|
prefixes := make([]string, len(result.CommonPrefixes))
|
|
for i, p := range result.CommonPrefixes {
|
|
prefixes[i] = p.Prefix
|
|
}
|
|
|
|
return ListObjectsV2Info{
|
|
IsTruncated: result.IsTruncated,
|
|
Prefixes: prefixes,
|
|
Objects: objects,
|
|
ContinuationToken: result.Marker,
|
|
NextContinuationToken: result.NextMarker,
|
|
}
|
|
}
|
|
|
|
// ToMinioClientObjectInfoMetadata convertes metadata to map[string][]string
|
|
func ToMinioClientObjectInfoMetadata(metadata map[string]string) map[string][]string {
|
|
mm := make(map[string][]string, len(metadata))
|
|
for k, v := range metadata {
|
|
mm[http.CanonicalHeaderKey(k)] = []string{v}
|
|
}
|
|
return mm
|
|
}
|
|
|
|
// ToMinioClientMetadata converts metadata to map[string]string
|
|
func ToMinioClientMetadata(metadata map[string]string) map[string]string {
|
|
mm := make(map[string]string)
|
|
for k, v := range metadata {
|
|
mm[http.CanonicalHeaderKey(k)] = v
|
|
}
|
|
return mm
|
|
}
|
|
|
|
// ToMinioClientCompletePart converts CompletePart to minio CompletePart
|
|
func ToMinioClientCompletePart(part CompletePart) minio.CompletePart {
|
|
return minio.CompletePart{
|
|
ETag: part.ETag,
|
|
PartNumber: part.PartNumber,
|
|
}
|
|
}
|
|
|
|
// ToMinioClientCompleteParts converts []CompletePart to minio []CompletePart
|
|
func ToMinioClientCompleteParts(parts []CompletePart) []minio.CompletePart {
|
|
mparts := make([]minio.CompletePart, len(parts))
|
|
for i, part := range parts {
|
|
mparts[i] = ToMinioClientCompletePart(part)
|
|
}
|
|
return mparts
|
|
}
|
|
|
|
// ErrorRespToObjectError converts Minio errors to minio object layer errors.
|
|
func ErrorRespToObjectError(err error, params ...string) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
bucket := ""
|
|
object := ""
|
|
if len(params) >= 1 {
|
|
bucket = params[0]
|
|
}
|
|
if len(params) == 2 {
|
|
object = params[1]
|
|
}
|
|
|
|
if isNetworkOrHostDown(err) {
|
|
return BackendDown{}
|
|
}
|
|
|
|
minioErr, ok := err.(minio.ErrorResponse)
|
|
if !ok {
|
|
// We don't interpret non Minio errors. As minio errors will
|
|
// have StatusCode to help to convert to object errors.
|
|
return err
|
|
}
|
|
|
|
switch minioErr.Code {
|
|
case "BucketAlreadyOwnedByYou":
|
|
err = BucketAlreadyOwnedByYou{}
|
|
case "BucketNotEmpty":
|
|
err = BucketNotEmpty{}
|
|
case "NoSuchBucketPolicy":
|
|
err = BucketPolicyNotFound{}
|
|
case "InvalidBucketName":
|
|
err = BucketNameInvalid{Bucket: bucket}
|
|
case "InvalidPart":
|
|
err = InvalidPart{}
|
|
case "NoSuchBucket":
|
|
err = BucketNotFound{Bucket: bucket}
|
|
case "NoSuchKey":
|
|
if object != "" {
|
|
err = ObjectNotFound{Bucket: bucket, Object: object}
|
|
} else {
|
|
err = BucketNotFound{Bucket: bucket}
|
|
}
|
|
case "XMinioInvalidObjectName":
|
|
err = ObjectNameInvalid{}
|
|
case "AccessDenied":
|
|
err = PrefixAccessDenied{
|
|
Bucket: bucket,
|
|
Object: object,
|
|
}
|
|
case "XAmzContentSHA256Mismatch":
|
|
err = hash.SHA256Mismatch{}
|
|
case "NoSuchUpload":
|
|
err = InvalidUploadID{}
|
|
case "EntityTooSmall":
|
|
err = PartTooSmall{}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// parse gateway sse env variable
|
|
func parseGatewaySSE(s string) (gatewaySSE, error) {
|
|
l := strings.Split(s, ";")
|
|
var gwSlice = make([]string, 0)
|
|
for _, val := range l {
|
|
v := strings.ToUpper(val)
|
|
if v == gatewaySSES3 || v == gatewaySSEC {
|
|
gwSlice = append(gwSlice, v)
|
|
continue
|
|
}
|
|
return nil, uiErrInvalidGWSSEValue(nil).Msg("gateway SSE cannot be (%s) ", v)
|
|
}
|
|
return gatewaySSE(gwSlice), nil
|
|
}
|
|
|
|
// handle gateway env vars
|
|
func handleGatewayEnvVars() {
|
|
gwsseVal, ok := os.LookupEnv("MINIO_GATEWAY_SSE")
|
|
if ok {
|
|
var err error
|
|
GlobalGatewaySSE, err = parseGatewaySSE(gwsseVal)
|
|
if err != nil {
|
|
logger.Fatal(err, "Unable to parse MINIO_GATEWAY_SSE value (`%s`)", gwsseVal)
|
|
}
|
|
}
|
|
}
|