github.com/grafana/pyroscope@v1.18.0/pkg/objstore/sse_bucket_client.go (about)

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