Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds SSE-KMS and SSE-C config to S3 Objstore #3064

Merged
merged 13 commits into from
Aug 25, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ We use *breaking* word for marking changes that are not backward compatible (rel
- [#2976](https://github.com/thanos-io/thanos/pull/2976) Query: Better rounding for incoming query timestamps.
- [#2929](https://github.com/thanos-io/thanos/pull/2929) Mixin: Fix expression for 'unhealthy sidecar' alert and also increase the timeout for 10 minutes.
- [#3024](https://github.com/thanos-io/thanos/pull/3024) Query: consider group name and file for deduplication
- [#3064](https://github.com/thanos-io/thanos/pull/3064) s3: Add SSE/SSE-KMS/SSE-C configuration

### Added

Expand Down
18 changes: 17 additions & 1 deletion docs/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ config:
access_key: ""
insecure: false
signature_version2: false
encrypt_sse: false
secret_key: ""
put_user_metadata: {}
http_config:
Expand All @@ -93,6 +92,11 @@ config:
trace:
enable: false
part_size: 134217728
sse_config:
enabled: false
jalev marked this conversation as resolved.
Show resolved Hide resolved
kms_key_id: ""
kms_encryption_context: {}
encryption_key: ""
```

At a minimum, you will need to provide a value for the `bucket`, `endpoint`, `access_key`, and `secret_key` keys. The rest of the keys are optional.
Expand All @@ -115,6 +119,18 @@ For debug and testing purposes you can set

* `trace.enable: true` to enable the minio client's verbose logging. Each request and response will be logged into the debug logger, so debug level logging must be enabled for this functionality.

#### S3 Server-Side Encryption

SSE can be configued using the `sse_config`. [SSE-S3](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html), [SSE-KMS](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html), and [SSE-C](https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html) are supported.

The following combinations are allowed:
* If `enabled` is set to `true` but nothing else is set, we default to using [SSE-S3](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html).
* If `kms_key_id` is set, [SSE-KMS](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html) is used, and the objects that Thanos uploads will be encrypted using that key.
* If `kms_encryption_context` is set with `kms_key_id`, you will add an [encryption context](https://docs.aws.amazon.com/kms/latest/developerguide/services-s3.html#s3-encryption-context) that provides a layer of integrity checks. Note that you do not have to set this as AWS will provide a default one for you.
* If `encryption_key` is set, [SSE-C](https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html) is set up using the key provided.

Thanos will throw an error if `encryption_key` AND `kms_key_id` is set.

#### Credentials

By default Thanos will try to retrieve credentials from the following sources:
Expand Down
40 changes: 36 additions & 4 deletions pkg/objstore/s3/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"os"
Expand Down Expand Up @@ -53,13 +54,22 @@ type Config struct {
AccessKey string `yaml:"access_key"`
Insecure bool `yaml:"insecure"`
SignatureV2 bool `yaml:"signature_version2"`
SSEEncryption bool `yaml:"encrypt_sse"`
SecretKey string `yaml:"secret_key"`
PutUserMetadata map[string]string `yaml:"put_user_metadata"`
HTTPConfig HTTPConfig `yaml:"http_config"`
TraceConfig TraceConfig `yaml:"trace"`
// PartSize used for multipart upload. Only used if uploaded object size is known and larger than configured PartSize.
PartSize uint64 `yaml:"part_size"`
PartSize uint64 `yaml:"part_size"`
SSEConfig SSEConfig `yaml:"sse_config"`
}

// SSEConfig deals with the configuration of SSE for Minio. The following options are valid:
// kmsencryptioncontext == https://docs.aws.amazon.com/kms/latest/developerguide/services-s3.html#s3-encryption-context
type SSEConfig struct {
Enable bool `yaml:"enabled"`
KMSKeyID string `yaml:"kms_key_id"`
KMSEncryptionContext map[string]string `yaml:"kms_encryption_context"`
EncryptionKey string `yaml:"encryption_key"`
}

type TraceConfig struct {
Expand Down Expand Up @@ -173,8 +183,26 @@ func NewBucketWithConfig(logger log.Logger, config Config, component string) (*B
client.SetAppInfo(fmt.Sprintf("thanos-%s", component), fmt.Sprintf("%s (%s)", version.Version, runtime.Version()))

var sse encrypt.ServerSide
if config.SSEEncryption {
sse = encrypt.NewSSE()
if config.SSEConfig.Enable {
switch {
case config.SSEConfig.KMSKeyID != "":
sse, err = encrypt.NewSSEKMS(config.SSEConfig.KMSKeyID, config.SSEConfig.KMSEncryptionContext)
if err != nil {
return nil, errors.Wrap(err, "initialize s3 client SSE-KMS")
}
case config.SSEConfig.EncryptionKey != "":
key, err := ioutil.ReadFile(config.SSEConfig.EncryptionKey)
if err != nil {
return nil, err
}

sse, err = encrypt.NewSSEC(key)
if err != nil {
return nil, errors.Wrap(err, "initialize s3 client SSE-C")
}
default:
sse = encrypt.NewSSE()
}
}

if config.TraceConfig.Enable {
Expand Down Expand Up @@ -211,6 +239,10 @@ func validate(conf Config) error {
if conf.AccessKey != "" && conf.SecretKey == "" {
return errors.New("no s3 secret_key specified while access_key is present in config file; either both should be present in config or envvars/IAM should be used.")
}

if conf.SSEConfig.EncryptionKey != "" && conf.SSEConfig.KMSKeyID != "" {
return errors.New("sse_encryption_key AND sse_kms_key_id set in sse_config. You can set one or the other, but NOT both.")
}
return nil
}

Expand Down
12 changes: 12 additions & 0 deletions pkg/objstore/s3/s3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ insecure: false`)
}
}

func TestParseConfig_SSEConfig(t *testing.T) {
input := []byte(`sse_config:
enabled: true`)

cfg, err := parseConfig(input)
testutil.Ok(t, err)

if !cfg.SSEConfig.Enable {
t.Errorf("parsing of sse_config failed: got %v, expected %v", cfg.SSEConfig.Enable, true)
}
}

func TestParseConfig_DefaultHTTPConfig(t *testing.T) {
input := []byte(`bucket: abcd
insecure: false`)
Expand Down