github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/bucket/s3/config.go (about) 1 package s3 2 3 import ( 4 "encoding/json" 5 "flag" 6 "fmt" 7 "net/http" 8 "strings" 9 10 "github.com/grafana/dskit/flagext" 11 "github.com/minio/minio-go/v7/pkg/encrypt" 12 "github.com/pkg/errors" 13 "github.com/thanos-io/thanos/pkg/objstore/s3" 14 15 bucket_http "github.com/grafana/loki/pkg/storage/bucket/http" 16 "github.com/grafana/loki/pkg/util" 17 ) 18 19 const ( 20 SignatureVersionV4 = "v4" 21 SignatureVersionV2 = "v2" 22 23 // SSEKMS config type constant to configure S3 server side encryption using KMS 24 // https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html 25 SSEKMS = "SSE-KMS" 26 27 // SSES3 config type constant to configure S3 server side encryption with AES-256 28 // https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html 29 SSES3 = "SSE-S3" 30 ) 31 32 var ( 33 supportedSignatureVersions = []string{SignatureVersionV4, SignatureVersionV2} 34 supportedSSETypes = []string{SSEKMS, SSES3} 35 errUnsupportedSignatureVersion = errors.New("unsupported signature version") 36 errUnsupportedSSEType = errors.New("unsupported S3 SSE type") 37 errInvalidSSEContext = errors.New("invalid S3 SSE encryption context") 38 ) 39 40 // HTTPConfig stores the http.Transport configuration for the s3 minio client. 41 type HTTPConfig struct { 42 bucket_http.Config `yaml:",inline"` 43 44 // Allow upstream callers to inject a round tripper 45 Transport http.RoundTripper `yaml:"-"` 46 } 47 48 // RegisterFlagsWithPrefix registers the flags for s3 storage with the provided prefix 49 func (cfg *HTTPConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 50 cfg.Config.RegisterFlagsWithPrefix(prefix+"s3.", f) 51 } 52 53 // Config holds the config options for an S3 backend 54 type Config struct { 55 Endpoint string `yaml:"endpoint"` 56 Region string `yaml:"region"` 57 BucketName string `yaml:"bucket_name"` 58 SecretAccessKey flagext.Secret `yaml:"secret_access_key"` 59 AccessKeyID string `yaml:"access_key_id"` 60 Insecure bool `yaml:"insecure"` 61 SignatureVersion string `yaml:"signature_version"` 62 63 SSE SSEConfig `yaml:"sse"` 64 HTTP HTTPConfig `yaml:"http"` 65 } 66 67 // RegisterFlags registers the flags for s3 storage with the provided prefix 68 func (cfg *Config) RegisterFlags(f *flag.FlagSet) { 69 cfg.RegisterFlagsWithPrefix("", f) 70 } 71 72 // RegisterFlagsWithPrefix registers the flags for s3 storage with the provided prefix 73 func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 74 f.StringVar(&cfg.AccessKeyID, prefix+"s3.access-key-id", "", "S3 access key ID") 75 f.Var(&cfg.SecretAccessKey, prefix+"s3.secret-access-key", "S3 secret access key") 76 f.StringVar(&cfg.BucketName, prefix+"s3.bucket-name", "", "S3 bucket name") 77 f.StringVar(&cfg.Region, prefix+"s3.region", "", "S3 region. If unset, the client will issue a S3 GetBucketLocation API call to autodetect it.") 78 f.StringVar(&cfg.Endpoint, prefix+"s3.endpoint", "", "The S3 bucket endpoint. It could be an AWS S3 endpoint listed at https://docs.aws.amazon.com/general/latest/gr/s3.html or the address of an S3-compatible service in hostname:port format.") 79 f.BoolVar(&cfg.Insecure, prefix+"s3.insecure", false, "If enabled, use http:// for the S3 endpoint instead of https://. This could be useful in local dev/test environments while using an S3-compatible backend storage, like Minio.") 80 f.StringVar(&cfg.SignatureVersion, prefix+"s3.signature-version", SignatureVersionV4, fmt.Sprintf("The signature version to use for authenticating against S3. Supported values are: %s.", strings.Join(supportedSignatureVersions, ", "))) 81 cfg.SSE.RegisterFlagsWithPrefix(prefix+"s3.sse.", f) 82 cfg.HTTP.RegisterFlagsWithPrefix(prefix, f) 83 } 84 85 // Validate config and returns error on failure 86 func (cfg *Config) Validate() error { 87 if !util.StringsContain(supportedSignatureVersions, cfg.SignatureVersion) { 88 return errUnsupportedSignatureVersion 89 } 90 91 return cfg.SSE.Validate() 92 } 93 94 // SSEConfig configures S3 server side encryption 95 // struct that is going to receive user input (through config file or CLI) 96 type SSEConfig struct { 97 Type string `yaml:"type"` 98 KMSKeyID string `yaml:"kms_key_id"` 99 KMSEncryptionContext string `yaml:"kms_encryption_context"` 100 } 101 102 func (cfg *SSEConfig) RegisterFlags(f *flag.FlagSet) { 103 cfg.RegisterFlagsWithPrefix("", f) 104 } 105 106 // RegisterFlagsWithPrefix adds the flags required to config this to the given FlagSet 107 func (cfg *SSEConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 108 f.StringVar(&cfg.Type, prefix+"type", "", fmt.Sprintf("Enable AWS Server Side Encryption. Supported values: %s.", strings.Join(supportedSSETypes, ", "))) 109 f.StringVar(&cfg.KMSKeyID, prefix+"kms-key-id", "", "KMS Key ID used to encrypt objects in S3") 110 f.StringVar(&cfg.KMSEncryptionContext, prefix+"kms-encryption-context", "", "KMS Encryption Context used for object encryption. It expects JSON formatted string.") 111 } 112 113 func (cfg *SSEConfig) Validate() error { 114 if cfg.Type != "" && !util.StringsContain(supportedSSETypes, cfg.Type) { 115 return errUnsupportedSSEType 116 } 117 118 if _, err := parseKMSEncryptionContext(cfg.KMSEncryptionContext); err != nil { 119 return errInvalidSSEContext 120 } 121 122 return nil 123 } 124 125 // BuildThanosConfig builds the SSE config expected by the Thanos client. 126 func (cfg *SSEConfig) BuildThanosConfig() (s3.SSEConfig, error) { 127 switch cfg.Type { 128 case "": 129 return s3.SSEConfig{}, nil 130 case SSEKMS: 131 encryptionCtx, err := parseKMSEncryptionContext(cfg.KMSEncryptionContext) 132 if err != nil { 133 return s3.SSEConfig{}, err 134 } 135 136 return s3.SSEConfig{ 137 Type: s3.SSEKMS, 138 KMSKeyID: cfg.KMSKeyID, 139 KMSEncryptionContext: encryptionCtx, 140 }, nil 141 case SSES3: 142 return s3.SSEConfig{ 143 Type: s3.SSES3, 144 }, nil 145 default: 146 return s3.SSEConfig{}, errUnsupportedSSEType 147 } 148 } 149 150 // BuildMinioConfig builds the SSE config expected by the Minio client. 151 func (cfg *SSEConfig) BuildMinioConfig() (encrypt.ServerSide, error) { 152 switch cfg.Type { 153 case "": 154 return nil, nil 155 case SSEKMS: 156 encryptionCtx, err := parseKMSEncryptionContext(cfg.KMSEncryptionContext) 157 if err != nil { 158 return nil, err 159 } 160 161 if encryptionCtx == nil { 162 // To overcome a limitation in Minio which checks interface{} == nil. 163 return encrypt.NewSSEKMS(cfg.KMSKeyID, nil) 164 } 165 return encrypt.NewSSEKMS(cfg.KMSKeyID, encryptionCtx) 166 case SSES3: 167 return encrypt.NewSSE(), nil 168 default: 169 return nil, errUnsupportedSSEType 170 } 171 } 172 173 func parseKMSEncryptionContext(data string) (map[string]string, error) { 174 if data == "" { 175 return nil, nil 176 } 177 178 decoded := map[string]string{} 179 err := errors.Wrap(json.Unmarshal([]byte(data), &decoded), "unable to parse KMS encryption context") 180 return decoded, err 181 }