github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/storage/bucket/sse_bucket_client.go (about)

     1  package bucket
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  
     7  	"github.com/minio/minio-go/v7/pkg/encrypt"
     8  	"github.com/pkg/errors"
     9  	"github.com/thanos-io/thanos/pkg/objstore"
    10  	"github.com/thanos-io/thanos/pkg/objstore/s3"
    11  
    12  	cortex_s3 "github.com/cortexproject/cortex/pkg/storage/bucket/s3"
    13  )
    14  
    15  // TenantConfigProvider defines a per-tenant config provider.
    16  type TenantConfigProvider interface {
    17  	// S3SSEType returns the per-tenant S3 SSE type.
    18  	S3SSEType(userID string) string
    19  
    20  	// S3SSEKMSKeyID returns the per-tenant S3 KMS-SSE key id or an empty string if not set.
    21  	S3SSEKMSKeyID(userID string) string
    22  
    23  	// S3SSEKMSEncryptionContext returns the per-tenant S3 KMS-SSE key id or an empty string if not set.
    24  	S3SSEKMSEncryptionContext(userID string) string
    25  }
    26  
    27  // SSEBucketClient is a wrapper around a objstore.BucketReader that configures the object
    28  // storage server-side encryption (SSE) for a given user.
    29  type SSEBucketClient struct {
    30  	userID      string
    31  	bucket      objstore.Bucket
    32  	cfgProvider TenantConfigProvider
    33  }
    34  
    35  // NewSSEBucketClient makes a new SSEBucketClient. The cfgProvider can be nil.
    36  func NewSSEBucketClient(userID string, bucket objstore.Bucket, cfgProvider TenantConfigProvider) *SSEBucketClient {
    37  	return &SSEBucketClient{
    38  		userID:      userID,
    39  		bucket:      bucket,
    40  		cfgProvider: cfgProvider,
    41  	}
    42  }
    43  
    44  // Close implements objstore.Bucket.
    45  func (b *SSEBucketClient) Close() error {
    46  	return b.bucket.Close()
    47  }
    48  
    49  // Upload the contents of the reader as an object into the bucket.
    50  func (b *SSEBucketClient) Upload(ctx context.Context, name string, r io.Reader) error {
    51  	if sse, err := b.getCustomS3SSEConfig(); err != nil {
    52  		return err
    53  	} else if sse != nil {
    54  		// If the underlying bucket client is not S3 and a custom S3 SSE config has been
    55  		// provided, the config option will be ignored.
    56  		ctx = s3.ContextWithSSEConfig(ctx, sse)
    57  	}
    58  
    59  	return b.bucket.Upload(ctx, name, r)
    60  }
    61  
    62  // Delete implements objstore.Bucket.
    63  func (b *SSEBucketClient) Delete(ctx context.Context, name string) error {
    64  	return b.bucket.Delete(ctx, name)
    65  }
    66  
    67  // Name implements objstore.Bucket.
    68  func (b *SSEBucketClient) Name() string {
    69  	return b.bucket.Name()
    70  }
    71  
    72  func (b *SSEBucketClient) getCustomS3SSEConfig() (encrypt.ServerSide, error) {
    73  	if b.cfgProvider == nil {
    74  		return nil, nil
    75  	}
    76  
    77  	// No S3 SSE override if the type override hasn't been provided.
    78  	sseType := b.cfgProvider.S3SSEType(b.userID)
    79  	if sseType == "" {
    80  		return nil, nil
    81  	}
    82  
    83  	cfg := cortex_s3.SSEConfig{
    84  		Type:                 sseType,
    85  		KMSKeyID:             b.cfgProvider.S3SSEKMSKeyID(b.userID),
    86  		KMSEncryptionContext: b.cfgProvider.S3SSEKMSEncryptionContext(b.userID),
    87  	}
    88  
    89  	sse, err := cfg.BuildMinioConfig()
    90  	if err != nil {
    91  		return nil, errors.Wrapf(err, "unable to customise S3 SSE config for tenant %s", b.userID)
    92  	}
    93  
    94  	return sse, nil
    95  }
    96  
    97  // Iter implements objstore.Bucket.
    98  func (b *SSEBucketClient) Iter(ctx context.Context, dir string, f func(string) error, options ...objstore.IterOption) error {
    99  	return b.bucket.Iter(ctx, dir, f, options...)
   100  }
   101  
   102  // Get implements objstore.Bucket.
   103  func (b *SSEBucketClient) Get(ctx context.Context, name string) (io.ReadCloser, error) {
   104  	return b.bucket.Get(ctx, name)
   105  }
   106  
   107  // GetRange implements objstore.Bucket.
   108  func (b *SSEBucketClient) GetRange(ctx context.Context, name string, off, length int64) (io.ReadCloser, error) {
   109  	return b.bucket.GetRange(ctx, name, off, length)
   110  }
   111  
   112  // Exists implements objstore.Bucket.
   113  func (b *SSEBucketClient) Exists(ctx context.Context, name string) (bool, error) {
   114  	return b.bucket.Exists(ctx, name)
   115  }
   116  
   117  // IsObjNotFoundErr implements objstore.Bucket.
   118  func (b *SSEBucketClient) IsObjNotFoundErr(err error) bool {
   119  	return b.bucket.IsObjNotFoundErr(err)
   120  }
   121  
   122  // Attributes implements objstore.Bucket.
   123  func (b *SSEBucketClient) Attributes(ctx context.Context, name string) (objstore.ObjectAttributes, error) {
   124  	return b.bucket.Attributes(ctx, name)
   125  }
   126  
   127  // ReaderWithExpectedErrs implements objstore.Bucket.
   128  func (b *SSEBucketClient) ReaderWithExpectedErrs(fn objstore.IsOpFailureExpectedFunc) objstore.BucketReader {
   129  	return b.WithExpectedErrs(fn)
   130  }
   131  
   132  // WithExpectedErrs implements objstore.Bucket.
   133  func (b *SSEBucketClient) WithExpectedErrs(fn objstore.IsOpFailureExpectedFunc) objstore.Bucket {
   134  	if ib, ok := b.bucket.(objstore.InstrumentedBucket); ok {
   135  		return &SSEBucketClient{
   136  			userID:      b.userID,
   137  			bucket:      ib.WithExpectedErrs(fn),
   138  			cfgProvider: b.cfgProvider,
   139  		}
   140  	}
   141  
   142  	return b
   143  }