github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/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 "time" 10 11 "github.com/grafana/dskit/flagext" 12 "github.com/minio/minio-go/v7/pkg/encrypt" 13 "github.com/pkg/errors" 14 "github.com/thanos-io/thanos/pkg/objstore/s3" 15 16 "github.com/cortexproject/cortex/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 IdleConnTimeout time.Duration `yaml:"idle_conn_timeout"` 43 ResponseHeaderTimeout time.Duration `yaml:"response_header_timeout"` 44 InsecureSkipVerify bool `yaml:"insecure_skip_verify"` 45 TLSHandshakeTimeout time.Duration `yaml:"tls_handshake_timeout"` 46 ExpectContinueTimeout time.Duration `yaml:"expect_continue_timeout"` 47 MaxIdleConns int `yaml:"max_idle_connections"` 48 MaxIdleConnsPerHost int `yaml:"max_idle_connections_per_host"` 49 MaxConnsPerHost int `yaml:"max_connections_per_host"` 50 51 // Allow upstream callers to inject a round tripper 52 Transport http.RoundTripper `yaml:"-"` 53 } 54 55 // RegisterFlagsWithPrefix registers the flags for s3 storage with the provided prefix 56 func (cfg *HTTPConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 57 f.DurationVar(&cfg.IdleConnTimeout, prefix+"s3.http.idle-conn-timeout", 90*time.Second, "The time an idle connection will remain idle before closing.") 58 f.DurationVar(&cfg.ResponseHeaderTimeout, prefix+"s3.http.response-header-timeout", 2*time.Minute, "The amount of time the client will wait for a servers response headers.") 59 f.BoolVar(&cfg.InsecureSkipVerify, prefix+"s3.http.insecure-skip-verify", false, "If the client connects to S3 via HTTPS and this option is enabled, the client will accept any certificate and hostname.") 60 f.DurationVar(&cfg.TLSHandshakeTimeout, prefix+"s3.tls-handshake-timeout", 10*time.Second, "Maximum time to wait for a TLS handshake. 0 means no limit.") 61 f.DurationVar(&cfg.ExpectContinueTimeout, prefix+"s3.expect-continue-timeout", 1*time.Second, "The time to wait for a server's first response headers after fully writing the request headers if the request has an Expect header. 0 to send the request body immediately.") 62 f.IntVar(&cfg.MaxIdleConns, prefix+"s3.max-idle-connections", 100, "Maximum number of idle (keep-alive) connections across all hosts. 0 means no limit.") 63 f.IntVar(&cfg.MaxIdleConnsPerHost, prefix+"s3.max-idle-connections-per-host", 100, "Maximum number of idle (keep-alive) connections to keep per-host. If 0, a built-in default value is used.") 64 f.IntVar(&cfg.MaxConnsPerHost, prefix+"s3.max-connections-per-host", 0, "Maximum number of connections per host. 0 means no limit.") 65 } 66 67 // Config holds the config options for an S3 backend 68 type Config struct { 69 Endpoint string `yaml:"endpoint"` 70 Region string `yaml:"region"` 71 BucketName string `yaml:"bucket_name"` 72 SecretAccessKey flagext.Secret `yaml:"secret_access_key"` 73 AccessKeyID string `yaml:"access_key_id"` 74 Insecure bool `yaml:"insecure"` 75 SignatureVersion string `yaml:"signature_version"` 76 77 SSE SSEConfig `yaml:"sse"` 78 HTTP HTTPConfig `yaml:"http"` 79 } 80 81 // RegisterFlags registers the flags for s3 storage with the provided prefix 82 func (cfg *Config) RegisterFlags(f *flag.FlagSet) { 83 cfg.RegisterFlagsWithPrefix("", f) 84 } 85 86 // RegisterFlagsWithPrefix registers the flags for s3 storage with the provided prefix 87 func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 88 f.StringVar(&cfg.AccessKeyID, prefix+"s3.access-key-id", "", "S3 access key ID") 89 f.Var(&cfg.SecretAccessKey, prefix+"s3.secret-access-key", "S3 secret access key") 90 f.StringVar(&cfg.BucketName, prefix+"s3.bucket-name", "", "S3 bucket name") 91 f.StringVar(&cfg.Region, prefix+"s3.region", "", "S3 region. If unset, the client will issue a S3 GetBucketLocation API call to autodetect it.") 92 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.") 93 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.") 94 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, ", "))) 95 cfg.SSE.RegisterFlagsWithPrefix(prefix+"s3.sse.", f) 96 cfg.HTTP.RegisterFlagsWithPrefix(prefix, f) 97 } 98 99 // Validate config and returns error on failure 100 func (cfg *Config) Validate() error { 101 if !util.StringsContain(supportedSignatureVersions, cfg.SignatureVersion) { 102 return errUnsupportedSignatureVersion 103 } 104 105 if err := cfg.SSE.Validate(); err != nil { 106 return err 107 } 108 109 return nil 110 } 111 112 // SSEConfig configures S3 server side encryption 113 // struct that is going to receive user input (through config file or CLI) 114 type SSEConfig struct { 115 Type string `yaml:"type"` 116 KMSKeyID string `yaml:"kms_key_id"` 117 KMSEncryptionContext string `yaml:"kms_encryption_context"` 118 } 119 120 func (cfg *SSEConfig) RegisterFlags(f *flag.FlagSet) { 121 cfg.RegisterFlagsWithPrefix("", f) 122 } 123 124 // RegisterFlagsWithPrefix adds the flags required to config this to the given FlagSet 125 func (cfg *SSEConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 126 f.StringVar(&cfg.Type, prefix+"type", "", fmt.Sprintf("Enable AWS Server Side Encryption. Supported values: %s.", strings.Join(supportedSSETypes, ", "))) 127 f.StringVar(&cfg.KMSKeyID, prefix+"kms-key-id", "", "KMS Key ID used to encrypt objects in S3") 128 f.StringVar(&cfg.KMSEncryptionContext, prefix+"kms-encryption-context", "", "KMS Encryption Context used for object encryption. It expects JSON formatted string.") 129 } 130 131 func (cfg *SSEConfig) Validate() error { 132 if cfg.Type != "" && !util.StringsContain(supportedSSETypes, cfg.Type) { 133 return errUnsupportedSSEType 134 } 135 136 if _, err := parseKMSEncryptionContext(cfg.KMSEncryptionContext); err != nil { 137 return errInvalidSSEContext 138 } 139 140 return nil 141 } 142 143 // BuildThanosConfig builds the SSE config expected by the Thanos client. 144 func (cfg *SSEConfig) BuildThanosConfig() (s3.SSEConfig, error) { 145 switch cfg.Type { 146 case "": 147 return s3.SSEConfig{}, nil 148 case SSEKMS: 149 encryptionCtx, err := parseKMSEncryptionContext(cfg.KMSEncryptionContext) 150 if err != nil { 151 return s3.SSEConfig{}, err 152 } 153 154 return s3.SSEConfig{ 155 Type: s3.SSEKMS, 156 KMSKeyID: cfg.KMSKeyID, 157 KMSEncryptionContext: encryptionCtx, 158 }, nil 159 case SSES3: 160 return s3.SSEConfig{ 161 Type: s3.SSES3, 162 }, nil 163 default: 164 return s3.SSEConfig{}, errUnsupportedSSEType 165 } 166 } 167 168 // BuildMinioConfig builds the SSE config expected by the Minio client. 169 func (cfg *SSEConfig) BuildMinioConfig() (encrypt.ServerSide, error) { 170 switch cfg.Type { 171 case "": 172 return nil, nil 173 case SSEKMS: 174 encryptionCtx, err := parseKMSEncryptionContext(cfg.KMSEncryptionContext) 175 if err != nil { 176 return nil, err 177 } 178 179 if encryptionCtx == nil { 180 // To overcome a limitation in Minio which checks interface{} == nil. 181 return encrypt.NewSSEKMS(cfg.KMSKeyID, nil) 182 } 183 return encrypt.NewSSEKMS(cfg.KMSKeyID, encryptionCtx) 184 case SSES3: 185 return encrypt.NewSSE(), nil 186 default: 187 return nil, errUnsupportedSSEType 188 } 189 } 190 191 func parseKMSEncryptionContext(data string) (map[string]string, error) { 192 if data == "" { 193 return nil, nil 194 } 195 196 decoded := map[string]string{} 197 err := errors.Wrap(json.Unmarshal([]byte(data), &decoded), "unable to parse KMS encryption context") 198 return decoded, err 199 }