Skip to content

Commit 146aace

Browse files
committed
Add signature v2 support for S3 chunks client
Signed-off-by: Christian Simon <[email protected]>
1 parent 65c0761 commit 146aace

File tree

11 files changed

+112
-8
lines changed

11 files changed

+112
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* `cortex_compactor_tenants_processing_succeeded`
4646
* `cortex_compactor_tenants_processing_failed`
4747
* [ENHANCEMENT] Added new experimental API endpoints: `POST /purger/delete_tenant` and `GET /purger/delete_tenant_status` for deleting all tenant data. Only works with blocks storage. #3549
48+
* [ENHANCEMENT] Chunks storage: add option to use V2 signatures for S3 authentication. #3560
4849
* [BUGFIX] Blocks storage ingester: fixed some cases leading to a TSDB WAL corruption after a partial write to disk. #3423
4950
* [BUGFIX] Blocks storage: Fix the race between ingestion and `/flush` call resulting in overlapping blocks. #3422
5051
* [BUGFIX] Querier: fixed `-querier.max-query-into-future` which wasn't correctly enforced on range queries. #3452

docs/configuration/config-file-reference.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,11 @@ storage:
12591259
# CLI flag: -ruler.storage.s3.http.insecure-skip-verify
12601260
[insecure_skip_verify: <boolean> | default = false]
12611261

1262+
# The signature version to use for authenticating against S3. Supported
1263+
# values are: v4, v2.
1264+
# CLI flag: -ruler.storage.s3.signature-version
1265+
[signature_version: <string> | default = "v4"]
1266+
12621267
swift:
12631268
# Openstack authentication URL.
12641269
# CLI flag: -ruler.storage.swift.auth-url
@@ -1577,6 +1582,11 @@ storage:
15771582
# CLI flag: -alertmanager.storage.s3.http.insecure-skip-verify
15781583
[insecure_skip_verify: <boolean> | default = false]
15791584

1585+
# The signature version to use for authenticating against S3. Supported
1586+
# values are: v4, v2.
1587+
# CLI flag: -alertmanager.storage.s3.signature-version
1588+
[signature_version: <string> | default = "v4"]
1589+
15801590
# Enable the experimental alertmanager config api.
15811591
# CLI flag: -experimental.alertmanager.enable-api
15821592
[enable_api: <boolean> | default = false]
@@ -2054,6 +2064,11 @@ aws:
20542064
# CLI flag: -s3.http.insecure-skip-verify
20552065
[insecure_skip_verify: <boolean> | default = false]
20562066

2067+
# The signature version to use for authenticating against S3. Supported values
2068+
# are: v4, v2.
2069+
# CLI flag: -s3.signature-version
2070+
[signature_version: <string> | default = "v4"]
2071+
20572072
azure:
20582073
# Azure Cloud environment. Supported values are: AzureGlobal, AzureChinaCloud,
20592074
# AzureGermanCloud, AzureUSGovernment.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ require (
3737
github.com/json-iterator/go v1.1.10
3838
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
3939
github.com/lib/pq v1.3.0
40+
github.com/minio/minio-go/v7 v7.0.2
4041
github.com/mitchellh/go-wordwrap v1.0.0
4142
github.com/ncw/swift v1.0.50
4243
github.com/oklog/ulid v1.3.1

pkg/alertmanager/multitenant.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ func (cfg *MultitenantAlertmanagerConfig) RegisterFlags(f *flag.FlagSet) {
125125
cfg.Store.RegisterFlags(f)
126126
}
127127

128+
// Validate config and returns error on failure
129+
func (cfg *MultitenantAlertmanagerConfig) Validate() error {
130+
if err := cfg.Store.Validate(); err != nil {
131+
return errors.Wrap(err, "invalid storage config")
132+
}
133+
return nil
134+
}
135+
128136
type multitenantAlertmanagerMetrics struct {
129137
lastReloadSuccessful *prometheus.GaugeVec
130138
lastReloadSuccessfulTimestamp *prometheus.GaugeVec

pkg/alertmanager/storage.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"flag"
66
"fmt"
77

8+
"github.com/pkg/errors"
9+
810
"github.com/cortexproject/cortex/pkg/alertmanager/alerts"
911
"github.com/cortexproject/cortex/pkg/alertmanager/alerts/configdb"
1012
"github.com/cortexproject/cortex/pkg/alertmanager/alerts/local"
@@ -43,6 +45,14 @@ func (cfg *AlertStoreConfig) RegisterFlags(f *flag.FlagSet) {
4345
cfg.S3.RegisterFlagsWithPrefix("alertmanager.storage.", f)
4446
}
4547

48+
// Validate config and returns error on failure
49+
func (cfg *AlertStoreConfig) Validate() error {
50+
if err := cfg.S3.Validate(); err != nil {
51+
return errors.Wrap(err, "invalid S3 Storage config")
52+
}
53+
return nil
54+
}
55+
4656
// NewAlertStore returns a new rule storage backend poller and store
4757
func NewAlertStore(cfg AlertStoreConfig) (AlertStore, error) {
4858
switch cfg.Type {

pkg/chunk/aws/dynamodb_storage_client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ func (cfg *StorageConfig) RegisterFlags(f *flag.FlagSet) {
8787
cfg.S3Config.RegisterFlags(f)
8888
}
8989

90+
// Validate config and returns error on failure
91+
func (cfg *StorageConfig) Validate() error {
92+
if err := cfg.S3Config.Validate(); err != nil {
93+
return errors.Wrap(err, "invalid S3 Storage config")
94+
}
95+
return nil
96+
}
97+
9098
type dynamoDBStorageClient struct {
9199
cfg DynamoDBConfig
92100
schemaCfg chunk.SchemaConfig

pkg/chunk/aws/s3_storage_client.go

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"crypto/tls"
66
"flag"
7+
"fmt"
78
"hash/fnv"
89
"io"
910
"net"
@@ -14,18 +15,32 @@ import (
1415
"github.com/aws/aws-sdk-go/aws"
1516
"github.com/aws/aws-sdk-go/aws/awserr"
1617
"github.com/aws/aws-sdk-go/aws/credentials"
18+
"github.com/aws/aws-sdk-go/aws/request"
1719
"github.com/aws/aws-sdk-go/aws/session"
20+
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
1821
"github.com/aws/aws-sdk-go/service/s3"
1922
"github.com/aws/aws-sdk-go/service/s3/s3iface"
23+
"github.com/minio/minio-go/v7/pkg/signer"
2024
"github.com/pkg/errors"
2125
"github.com/prometheus/client_golang/prometheus"
2226
awscommon "github.com/weaveworks/common/aws"
2327
"github.com/weaveworks/common/instrument"
2428

2529
"github.com/cortexproject/cortex/pkg/chunk"
30+
"github.com/cortexproject/cortex/pkg/util"
2631
"github.com/cortexproject/cortex/pkg/util/flagext"
2732
)
2833

34+
const (
35+
SignatureVersionV4 = "v4"
36+
SignatureVersionV2 = "v2"
37+
)
38+
39+
var (
40+
supportedSignatureVersions = []string{SignatureVersionV4, SignatureVersionV2}
41+
errUnsupportedSignatureVersion = errors.New("unsupported signature version")
42+
)
43+
2944
var (
3045
s3RequestDuration = instrument.NewHistogramCollector(prometheus.NewHistogramVec(prometheus.HistogramOpts{
3146
Namespace: "cortex",
@@ -48,14 +63,15 @@ type S3Config struct {
4863
S3 flagext.URLValue
4964
S3ForcePathStyle bool
5065

51-
BucketNames string
52-
Endpoint string `yaml:"endpoint"`
53-
Region string `yaml:"region"`
54-
AccessKeyID string `yaml:"access_key_id"`
55-
SecretAccessKey string `yaml:"secret_access_key"`
56-
Insecure bool `yaml:"insecure"`
57-
SSEEncryption bool `yaml:"sse_encryption"`
58-
HTTPConfig HTTPConfig `yaml:"http_config"`
66+
BucketNames string
67+
Endpoint string `yaml:"endpoint"`
68+
Region string `yaml:"region"`
69+
AccessKeyID string `yaml:"access_key_id"`
70+
SecretAccessKey string `yaml:"secret_access_key"`
71+
Insecure bool `yaml:"insecure"`
72+
SSEEncryption bool `yaml:"sse_encryption"`
73+
HTTPConfig HTTPConfig `yaml:"http_config"`
74+
SignatureVersion string `yaml:"signature_version"`
5975

6076
Inject InjectRequestMiddleware `yaml:"-"`
6177
}
@@ -89,6 +105,15 @@ func (cfg *S3Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
89105
f.DurationVar(&cfg.HTTPConfig.IdleConnTimeout, prefix+"s3.http.idle-conn-timeout", 90*time.Second, "The maximum amount of time an idle connection will be held open.")
90106
f.DurationVar(&cfg.HTTPConfig.ResponseHeaderTimeout, prefix+"s3.http.response-header-timeout", 0, "If non-zero, specifies the amount of time to wait for a server's response headers after fully writing the request.")
91107
f.BoolVar(&cfg.HTTPConfig.InsecureSkipVerify, prefix+"s3.http.insecure-skip-verify", false, "Set to false to skip verifying the certificate chain and hostname.")
108+
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, ", ")))
109+
}
110+
111+
// Validate config and returns error on failure
112+
func (cfg *S3Config) Validate() error {
113+
if !util.StringsContain(supportedSignatureVersions, cfg.SignatureVersion) {
114+
return errUnsupportedSignatureVersion
115+
}
116+
return nil
92117
}
93118

94119
type S3ObjectClient struct {
@@ -111,6 +136,10 @@ func NewS3ObjectClient(cfg S3Config) (*S3ObjectClient, error) {
111136

112137
s3Client := s3.New(sess)
113138

139+
if cfg.SignatureVersion == SignatureVersionV2 {
140+
s3Client.Handlers.Sign.Swap(v4.SignRequestHandler.Name, v2SignRequestHandler(cfg))
141+
}
142+
114143
var sseEncryption *string
115144
if cfg.SSEEncryption {
116145
sseEncryption = aws.String("AES256")
@@ -124,6 +153,28 @@ func NewS3ObjectClient(cfg S3Config) (*S3ObjectClient, error) {
124153
return &client, nil
125154
}
126155

156+
func v2SignRequestHandler(cfg S3Config) request.NamedHandler {
157+
return request.NamedHandler{
158+
Name: "v2.SignRequestHandler",
159+
Fn: func(req *request.Request) {
160+
credentials, err := req.Config.Credentials.GetWithContext(req.Context())
161+
if err != nil {
162+
if err != nil {
163+
req.Error = err
164+
return
165+
}
166+
}
167+
168+
req.HTTPRequest = signer.SignV2(
169+
*req.HTTPRequest,
170+
credentials.AccessKeyID,
171+
credentials.SecretAccessKey,
172+
!cfg.S3ForcePathStyle,
173+
)
174+
},
175+
}
176+
}
177+
127178
func buildS3Config(cfg S3Config) (*aws.Config, []string, error) {
128179
var s3Config *aws.Config
129180
var err error

pkg/chunk/storage/factory.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ func (cfg *Config) Validate() error {
117117
if err := cfg.AzureStorageConfig.Validate(); err != nil {
118118
return errors.Wrap(err, "invalid Azure Storage config")
119119
}
120+
if err := cfg.AWSStorageConfig.Validate(); err != nil {
121+
return errors.Wrap(err, "invalid AWS Storage config")
122+
}
120123
return nil
121124
}
122125

pkg/cortex/cortex.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ func (c *Config) Validate(log log.Logger) error {
213213
if err := c.Compactor.Validate(); err != nil {
214214
return errors.Wrap(err, "invalid compactor config")
215215
}
216+
if err := c.Alertmanager.Validate(); err != nil {
217+
return errors.Wrap(err, "invalid alertmanager config")
218+
}
216219

217220
if c.Storage.Engine == storage.StorageEngineBlocks && c.Querier.SecondStoreEngine != storage.StorageEngineChunks && len(c.Schema.Configs) > 0 {
218221
level.Warn(log).Log("schema configuration is not used by the blocks storage engine, and will have no effect")

pkg/ruler/storage.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ func (cfg *RuleStoreConfig) Validate() error {
5454
if err := cfg.Azure.Validate(); err != nil {
5555
return errors.Wrap(err, "invalid Azure Storage config")
5656
}
57+
if err := cfg.S3.Validate(); err != nil {
58+
return errors.Wrap(err, "invalid S3 Storage config")
59+
}
5760
return nil
5861
}
5962

0 commit comments

Comments
 (0)