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 }