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

query frontend, store: Add TLS support to redis_client #5674

Merged
merged 3 commits into from Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -21,6 +21,7 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re
- [#5658](https://github.com/thanos-io/thanos/pull/5658) Query Frontend: Introduce new optional parameters (`query-range.min-split-interval`, `query-range.max-split-interval`, `query-range.horizontal-shards`) to implement more dynamic horizontal query splitting.
- [#5721](https://github.com/thanos-io/thanos/pull/5721) Store: Add metric `thanos_bucket_store_empty_postings_total` for number of empty postings when fetching series.
- [#5723](https://github.com/thanos-io/thanos/pull/5723) Compactor: Support disable block viewer UI.
- [#5674](https://github.com/thanos-io/thanos/pull/5674) Query Frontend/Store: Add support connecting to redis using TLS.

### Changed

Expand Down
7 changes: 7 additions & 0 deletions docs/components/query-frontend.md
Expand Up @@ -125,6 +125,13 @@ config:
get_multi_batch_size: 100
max_set_multi_concurrency: 100
set_multi_batch_size: 100
tls_enabled: false
tls_config:
ca_file: ""
cert_file: ""
key_file: ""
server_name: ""
insecure_skip_verify: false
expiration: 24h0m0s
```

Expand Down
18 changes: 16 additions & 2 deletions docs/components/store.md
Expand Up @@ -328,6 +328,13 @@ config:
get_multi_batch_size: 100
max_set_multi_concurrency: 100
set_multi_batch_size: 100
tls_enabled: false
tls_config:
ca_file: ""
cert_file: ""
key_file: ""
server_name: ""
insecure_skip_verify: false
```

The **required** settings are:
Expand All @@ -336,8 +343,8 @@ The **required** settings are:

While the remaining settings are **optional**:

- `username`: the username to connect redis, only redis 6.0 and grater need this field.
- `password`: the password to connect redis.
- `username`: the username to connect to redis, only redis 6.0 and grater need this field.
- `password`: the password to connect to redis.
- `db`: the database to be selected after connecting to the server.
- `dial_timeout`: the redis dial timeout.
- `read_timeout`: the redis read timeout.
Expand All @@ -350,6 +357,13 @@ While the remaining settings are **optional**:
- `get_multi_batch_size`: specifies the maximum size per batch for mget.
- `max_set_multi_concurrency`: specifies the maximum number of concurrent SetMulti() operations.
- `set_multi_batch_size`: specifies the maximum size per batch for pipeline set.
- `tls_enabled`: enables the use of TLS to connect to redis
- `tls_config`: TLS connection configuration:
- `ca_file`: path to Root CA certificate file to use
- `cert_file`: path to Client Certificate file to use
- `key_file`: path to the Key file for cert_file (NOTE: Both this and `cert_file` must be set if used)
- `servername`: Override the server name used to validate the server certificate
- `insecure_skip_verify`: Disable certificate verification

## Caching Bucket

Expand Down
43 changes: 41 additions & 2 deletions pkg/cacheutil/redis_client.go
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/thanos-io/thanos/pkg/extprom"
"github.com/thanos-io/thanos/pkg/gate"
thanos_tls "github.com/thanos-io/thanos/pkg/tls"
)

var (
Expand All @@ -35,9 +36,25 @@ var (
GetMultiBatchSize: 100,
MaxSetMultiConcurrency: 100,
SetMultiBatchSize: 100,
TLSEnabled: false,
TLSConfig: TLSConfig{},
}
)

// TLSConfig configures TLS connections.
type TLSConfig struct {
// The CA cert to use for the targets.
CAFile string `yaml:"ca_file"`
// The client cert file for the targets.
CertFile string `yaml:"cert_file"`
// The client key file for the targets.
KeyFile string `yaml:"key_file"`
// Used to verify the hostname for the targets. See https://tools.ietf.org/html/rfc4366#section-3.1
ServerName string `yaml:"server_name"`
// Disable target certificate validation.
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
}

// RedisClientConfig is the config accepted by RedisClient.
type RedisClientConfig struct {
// Addr specifies the addresses of redis server.
Expand Down Expand Up @@ -94,12 +111,19 @@ type RedisClientConfig struct {

// SetMultiBatchSize specifies the maximum size per batch for pipeline set.
SetMultiBatchSize int `yaml:"set_multi_batch_size"`

// TLSEnabled enable tls for redis connection.
TLSEnabled bool `yaml:"tls_enabled"`

// TLSConfig to use to connect to the redis server.
TLSConfig TLSConfig `yaml:"tls_config"`
}

func (c *RedisClientConfig) validate() error {
if c.Addr == "" {
return errors.New("no redis addr provided")
}

return nil
}

Expand Down Expand Up @@ -136,7 +160,8 @@ func NewRedisClientWithConfig(logger log.Logger, name string, config RedisClient
if err := config.validate(); err != nil {
return nil, err
}
redisClient := redis.NewClient(&redis.Options{

opts := &redis.Options{
Addr: config.Addr,
Username: config.Username,
Password: config.Password,
Expand All @@ -147,8 +172,22 @@ func NewRedisClientWithConfig(logger log.Logger, name string, config RedisClient
MinIdleConns: config.MinIdleConns,
MaxConnAge: config.MaxConnAge,
IdleTimeout: config.IdleTimeout,
})
}

if config.TLSEnabled {
tlsConfig := config.TLSConfig

tlsClientConfig, err := thanos_tls.NewClientConfig(logger, tlsConfig.CertFile, tlsConfig.KeyFile,
tlsConfig.CAFile, tlsConfig.ServerName, tlsConfig.InsecureSkipVerify)

if err != nil {
return nil, err
}

opts.TLSConfig = tlsClientConfig
}

redisClient := redis.NewClient(opts)
if reg != nil {
reg = prometheus.WrapRegistererWith(prometheus.Labels{"name": name}, reg)
}
Expand Down
83 changes: 83 additions & 0 deletions pkg/cacheutil/redis_client_test.go
Expand Up @@ -136,3 +136,86 @@ func TestRedisClient(t *testing.T) {
})
}
}

func TestValidateRedisConfig(t *testing.T) {
tests := []struct {
name string
config func() RedisClientConfig
expect_err bool // func(*testing.T, interface{}, error)
}{
{
name: "simpleConfig",
config: func() RedisClientConfig {
cfg := DefaultRedisClientConfig
cfg.Addr = "127.0.0.1:6789"
cfg.Username = "user"
cfg.Password = "1234"
return cfg
},
expect_err: false,
},
{
name: "tlsConfigDefaults",
config: func() RedisClientConfig {
cfg := DefaultRedisClientConfig
cfg.Addr = "127.0.0.1:6789"
cfg.Username = "user"
cfg.Password = "1234"
cfg.TLSEnabled = true
return cfg
},
expect_err: false,
},
{
name: "tlsClientCertConfig",
config: func() RedisClientConfig {
cfg := DefaultRedisClientConfig
cfg.Addr = "127.0.0.1:6789"
cfg.Username = "user"
cfg.Password = "1234"
cfg.TLSEnabled = true
cfg.TLSConfig = TLSConfig{
CertFile: "cert/client.pem",
KeyFile: "cert/client.key",
}
return cfg
},
expect_err: false,
},
{
name: "tlsInvalidClientCertConfig",
config: func() RedisClientConfig {
cfg := DefaultRedisClientConfig
cfg.Addr = "127.0.0.1:6789"
cfg.Username = "user"
cfg.Password = "1234"
cfg.TLSEnabled = true
cfg.TLSConfig = TLSConfig{
CertFile: "cert/client.pem",
}
return cfg
},
expect_err: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := tt.config()

logger := log.NewLogfmtLogger(os.Stderr)
reg := prometheus.NewRegistry()
val, err := NewRedisClientWithConfig(logger, tt.name, cfg, reg)
if val != nil {
defer val.Stop()
}

if tt.expect_err {
testutil.NotOk(t, err, val)
} else {
testutil.Ok(t, err, val)
}
})
}

}