Skip to content

Commit 9db957d

Browse files
committed
Add consul tls
Signed-off-by: SungJin1212 <[email protected]>
1 parent a78470b commit 9db957d

File tree

4 files changed

+165
-18
lines changed

4 files changed

+165
-18
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* [ENHANCEMENT] Distributor: Add new `cortex_reduced_resolution_histogram_samples_total` metric to track the number of histogram samples which resolution was reduced. #6182
3434
* [ENHANCEMENT] StoreGateway: Implement metadata API limit in queryable. #6195
3535
* [ENHANCEMENT] Ingester: Add matchers to ingester LabelNames() and LabelNamesStream() RPC. #6209
36+
* [ENHANCEMENT] KV: Add TLS configs to consul. #6374
3637
* [ENHANCEMENT] Ingester/Store Gateway Clients: Introduce an experimental HealthCheck handler to quickly fail requests directed to unhealthy targets. #6225 #6257
3738
* [ENHANCEMENT] Upgrade build image and Go version to 1.23.2. #6261 #6262
3839
* [ENHANCEMENT] Ingester: Introduce a new experimental feature for caching expanded postings on the ingester. #6296

docs/configuration/config-file-reference.md

+28-1
Original file line numberDiff line numberDiff line change
@@ -2445,7 +2445,7 @@ The `consul_config` configures the consul client. The supported CLI flags `<pref
24452445
# CLI flag: -<prefix>.consul.acl-token
24462446
[acl_token: <string> | default = ""]
24472447
2448-
# HTTP timeout when talking to Consul
2448+
# HTTP timeout when talking to Consul.
24492449
# CLI flag: -<prefix>.consul.client-timeout
24502450
[http_client_timeout: <duration> | default = 20s]
24512451
@@ -2461,6 +2461,33 @@ The `consul_config` configures the consul client. The supported CLI flags `<pref
24612461
# Burst size used in rate limit. Values less than 1 are treated as 1.
24622462
# CLI flag: -<prefix>.consul.watch-burst-size
24632463
[watch_burst_size: <int> | default = 1]
2464+
2465+
# Enable TLS.
2466+
# CLI flag: -<prefix>.consul.tls-enabled
2467+
[tls_enabled: <boolean> | default = false]
2468+
2469+
# Path to the client certificate file, which will be used for authenticating
2470+
# with the server. Also requires the key path to be configured.
2471+
# CLI flag: -<prefix>.consul.tls-cert-path
2472+
[tls_cert_path: <string> | default = ""]
2473+
2474+
# Path to the key file for the client certificate. Also requires the client
2475+
# certificate to be configured.
2476+
# CLI flag: -<prefix>.consul.tls-key-path
2477+
[tls_key_path: <string> | default = ""]
2478+
2479+
# Path to the CA certificates file to validate server certificate against. If
2480+
# not set, the host's root CA certificates are used.
2481+
# CLI flag: -<prefix>.consul.tls-ca-path
2482+
[tls_ca_path: <string> | default = ""]
2483+
2484+
# Override the expected name on the server certificate.
2485+
# CLI flag: -<prefix>.consul.tls-server-name
2486+
[tls_server_name: <string> | default = ""]
2487+
2488+
# Skip validating server certificate.
2489+
# CLI flag: -<prefix>.consul.tls-insecure-skip-verify
2490+
[tls_insecure_skip_verify: <boolean> | default = false]
24642491
```
24652492

24662493
### `distributor_config`

pkg/ring/kv/consul/client.go

+58-17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/cortexproject/cortex/pkg/ring/kv/codec"
2121
"github.com/cortexproject/cortex/pkg/util/backoff"
2222
"github.com/cortexproject/cortex/pkg/util/flagext"
23+
cortextls "github.com/cortexproject/cortex/pkg/util/tls"
2324
)
2425

2526
const (
@@ -40,12 +41,14 @@ var (
4041

4142
// Config to create a ConsulClient
4243
type Config struct {
43-
Host string `yaml:"host"`
44-
ACLToken flagext.Secret `yaml:"acl_token"`
45-
HTTPClientTimeout time.Duration `yaml:"http_client_timeout"`
46-
ConsistentReads bool `yaml:"consistent_reads"`
47-
WatchKeyRateLimit float64 `yaml:"watch_rate_limit"` // Zero disables rate limit
48-
WatchKeyBurstSize int `yaml:"watch_burst_size"` // Burst when doing rate-limit, defaults to 1
44+
Host string `yaml:"host"`
45+
ACLToken flagext.Secret `yaml:"acl_token"`
46+
HTTPClientTimeout time.Duration `yaml:"http_client_timeout"`
47+
ConsistentReads bool `yaml:"consistent_reads"`
48+
WatchKeyRateLimit float64 `yaml:"watch_rate_limit"` // Zero disables rate limit
49+
WatchKeyBurstSize int `yaml:"watch_burst_size"` // Burst when doing rate-limit, defaults to 1
50+
EnableTLS bool `yaml:"tls_enabled"`
51+
TLS cortextls.ClientConfig `yaml:",inline"`
4952

5053
// Used in tests only.
5154
MaxCasRetries int `yaml:"-"`
@@ -74,24 +77,62 @@ type Client struct {
7477
func (cfg *Config) RegisterFlags(f *flag.FlagSet, prefix string) {
7578
f.StringVar(&cfg.Host, prefix+"consul.hostname", "localhost:8500", "Hostname and port of Consul.")
7679
f.Var(&cfg.ACLToken, prefix+"consul.acl-token", "ACL Token used to interact with Consul.")
77-
f.DurationVar(&cfg.HTTPClientTimeout, prefix+"consul.client-timeout", 2*longPollDuration, "HTTP timeout when talking to Consul")
80+
f.DurationVar(&cfg.HTTPClientTimeout, prefix+"consul.client-timeout", 2*longPollDuration, "HTTP timeout when talking to Consul.")
7881
f.BoolVar(&cfg.ConsistentReads, prefix+"consul.consistent-reads", false, "Enable consistent reads to Consul.")
7982
f.Float64Var(&cfg.WatchKeyRateLimit, prefix+"consul.watch-rate-limit", 1, "Rate limit when watching key or prefix in Consul, in requests per second. 0 disables the rate limit.")
8083
f.IntVar(&cfg.WatchKeyBurstSize, prefix+"consul.watch-burst-size", 1, "Burst size used in rate limit. Values less than 1 are treated as 1.")
84+
f.BoolVar(&cfg.EnableTLS, prefix+"consul.tls-enabled", false, "Enable TLS.")
85+
cfg.TLS.RegisterFlagsWithPrefix(prefix+"consul", f)
8186
}
8287

83-
// NewClient returns a new Client.
84-
func NewClient(cfg Config, codec codec.Codec, logger log.Logger, registerer prometheus.Registerer) (*Client, error) {
85-
client, err := consul.NewClient(&consul.Config{
88+
func (cfg *Config) GetTLS() *consul.TLSConfig {
89+
return &consul.TLSConfig{
90+
Address: cfg.TLS.ServerName,
91+
CertFile: cfg.TLS.CertPath,
92+
KeyFile: cfg.TLS.KeyPath,
93+
CAFile: cfg.TLS.CAPath,
94+
InsecureSkipVerify: cfg.TLS.InsecureSkipVerify,
95+
}
96+
}
97+
98+
func getConsulConfig(cfg Config) (*consul.Config, error) {
99+
scheme := "http"
100+
transport := cleanhttp.DefaultPooledTransport()
101+
102+
config := &consul.Config{
86103
Address: cfg.Host,
87104
Token: cfg.ACLToken.Value,
88-
Scheme: "http",
89-
HttpClient: &http.Client{
90-
Transport: cleanhttp.DefaultPooledTransport(),
91-
// See https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
92-
Timeout: cfg.HTTPClientTimeout,
93-
},
94-
})
105+
}
106+
107+
if cfg.EnableTLS {
108+
tlsConfig := cfg.GetTLS()
109+
tlsClientConfig, err := consul.SetupTLSConfig(tlsConfig)
110+
if err != nil {
111+
return nil, err
112+
}
113+
transport.TLSClientConfig = tlsClientConfig
114+
scheme = "https"
115+
config.TLSConfig = *tlsConfig
116+
}
117+
118+
config.Scheme = scheme
119+
config.HttpClient = &http.Client{
120+
Transport: transport,
121+
// See https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
122+
Timeout: cfg.HTTPClientTimeout,
123+
}
124+
125+
return config, nil
126+
}
127+
128+
// NewClient returns a new Client.
129+
func NewClient(cfg Config, codec codec.Codec, logger log.Logger, registerer prometheus.Registerer) (*Client, error) {
130+
config, err := getConsulConfig(cfg)
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
client, err := consul.NewClient(config)
95136
if err != nil {
96137
return nil, err
97138
}

pkg/ring/kv/consul/client_test.go

+78
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package consul
22

33
import (
44
"context"
5+
"crypto/x509"
6+
"crypto/x509/pkix"
57
"fmt"
8+
"path/filepath"
69
"strconv"
710
"testing"
811
"time"
@@ -12,7 +15,9 @@ import (
1215
"github.com/stretchr/testify/assert"
1316
"github.com/stretchr/testify/require"
1417

18+
"github.com/cortexproject/cortex/integration/ca"
1519
"github.com/cortexproject/cortex/pkg/ring/kv/codec"
20+
"github.com/cortexproject/cortex/pkg/util/tls"
1621
)
1722

1823
func writeValuesToKV(t *testing.T, client *Client, key string, start, end int, sleep time.Duration) <-chan struct{} {
@@ -30,6 +35,79 @@ func writeValuesToKV(t *testing.T, client *Client, key string, start, end int, s
3035
return ch
3136
}
3237

38+
func TestGetConsulConfig(t *testing.T) {
39+
testCADir := t.TempDir()
40+
41+
serverCA := ca.New("Consul Server CA")
42+
caCertFile := filepath.Join(testCADir, "ca.crt")
43+
require.NoError(t, serverCA.WriteCACertificate(caCertFile))
44+
45+
serverCertFile := filepath.Join(testCADir, "server.crt")
46+
serverKeyFile := filepath.Join(testCADir, "server.key")
47+
require.NoError(t, serverCA.WriteCertificate(
48+
&x509.Certificate{
49+
Subject: pkix.Name{CommonName: "server"},
50+
DNSNames: []string{"localhost"},
51+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
52+
},
53+
serverCertFile,
54+
serverKeyFile,
55+
))
56+
57+
clientCA := ca.New("Consul Client CA")
58+
clientCACertFile := filepath.Join(testCADir, "client.crt")
59+
require.NoError(t, clientCA.WriteCACertificate(clientCACertFile))
60+
61+
tests := []struct {
62+
name string
63+
cfg Config
64+
}{
65+
{
66+
name: "tls config validation should return no error (skip verify: true)",
67+
cfg: Config{
68+
Host: "localhost:8501",
69+
EnableTLS: true,
70+
TLS: tls.ClientConfig{
71+
CertPath: serverCertFile,
72+
KeyPath: serverKeyFile,
73+
CAPath: clientCACertFile,
74+
ServerName: "testServer",
75+
InsecureSkipVerify: true,
76+
},
77+
},
78+
},
79+
{
80+
name: "tls config validation should return no error (skip verify: false)",
81+
cfg: Config{
82+
Host: "localhost:8501",
83+
EnableTLS: true,
84+
TLS: tls.ClientConfig{
85+
CertPath: serverCertFile,
86+
KeyPath: serverKeyFile,
87+
CAPath: clientCACertFile,
88+
ServerName: "testServer",
89+
InsecureSkipVerify: false,
90+
},
91+
},
92+
},
93+
{
94+
name: "no tls config should return no error",
95+
cfg: Config{
96+
Host: "localhost:8500",
97+
EnableTLS: false,
98+
},
99+
},
100+
}
101+
102+
for _, test := range tests {
103+
t.Run(test.name, func(t *testing.T) {
104+
_, err := getConsulConfig(test.cfg)
105+
require.NoError(t, err)
106+
})
107+
}
108+
109+
}
110+
33111
func TestWatchKeyWithRateLimit(t *testing.T) {
34112
c, closer := NewInMemoryClientWithConfig(codec.String{}, Config{
35113
WatchKeyRateLimit: 5.0,

0 commit comments

Comments
 (0)