diff --git a/docs/sources/configure/_index.md b/docs/sources/configure/_index.md index ba96be3b3d60..36603f1be1d3 100644 --- a/docs/sources/configure/_index.md +++ b/docs/sources/configure/_index.md @@ -4552,6 +4552,72 @@ memcached_client: # CLI flag: -.memcached.circuit-breaker-interval [circuit_breaker_interval: | default = 10s] + # Enable connecting to Memcached with TLS. + # CLI flag: -.memcached.tls-enabled + [tls_enabled: | default = false] + + # Path to the client certificate, which will be used for authenticating with + # the server. Also requires the key path to be configured. + # CLI flag: -.memcached.tls-cert-path + [tls_cert_path: | default = ""] + + # Path to the key for the client certificate. Also requires the client + # certificate to be configured. + # CLI flag: -.memcached.tls-key-path + [tls_key_path: | default = ""] + + # Path to the CA certificates to validate server certificate against. If not + # set, the host's root CA certificates are used. + # CLI flag: -.memcached.tls-ca-path + [tls_ca_path: | default = ""] + + # Override the expected name on the server certificate. + # CLI flag: -.memcached.tls-server-name + [tls_server_name: | default = ""] + + # Skip validating server certificate. + # CLI flag: -.memcached.tls-insecure-skip-verify + [tls_insecure_skip_verify: | default = false] + + # Override the default cipher suite list (separated by commas). Allowed + # values: + # + # Secure Ciphers: + # - TLS_RSA_WITH_AES_128_CBC_SHA + # - TLS_RSA_WITH_AES_256_CBC_SHA + # - TLS_RSA_WITH_AES_128_GCM_SHA256 + # - TLS_RSA_WITH_AES_256_GCM_SHA384 + # - TLS_AES_128_GCM_SHA256 + # - TLS_AES_256_GCM_SHA384 + # - TLS_CHACHA20_POLY1305_SHA256 + # - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + # - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + # - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + # - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + # - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + # - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + # - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + # - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + # - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + # - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + # + # Insecure Ciphers: + # - TLS_RSA_WITH_RC4_128_SHA + # - TLS_RSA_WITH_3DES_EDE_CBC_SHA + # - TLS_RSA_WITH_AES_128_CBC_SHA256 + # - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + # - TLS_ECDHE_RSA_WITH_RC4_128_SHA + # - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + # - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + # - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + # CLI flag: -.memcached.tls-cipher-suites + [tls_cipher_suites: | default = ""] + + # Override the default minimum TLS version. Allowed values: VersionTLS10, + # VersionTLS11, VersionTLS12, VersionTLS13 + # CLI flag: -.memcached.tls-min-version + [tls_min_version: | default = ""] + redis: # Redis Server or Cluster configuration endpoint to use for caching. A # comma-separated list of endpoints for Redis Cluster or Redis Sentinel. If diff --git a/pkg/storage/chunk/cache/memcached_client.go b/pkg/storage/chunk/cache/memcached_client.go index f05763ba59d1..ca355e8f4e24 100644 --- a/pkg/storage/chunk/cache/memcached_client.go +++ b/pkg/storage/chunk/cache/memcached_client.go @@ -2,6 +2,7 @@ package cache import ( "context" + "crypto/tls" "flag" "net" "sort" @@ -12,6 +13,7 @@ import ( "github.com/go-kit/log" "github.com/go-kit/log/level" + dstls "github.com/grafana/dskit/crypto/tls" "github.com/grafana/dskit/dns" "github.com/grafana/gomemcache/memcache" "github.com/pkg/errors" @@ -62,6 +64,11 @@ type memcachedClient struct { skipped prometheus.Counter logger log.Logger + + cfg MemcachedClientConfig + + // DialTimeout specifies a custom dialer used to dial new connections to a server. + DialTimeout func(network, address string, timeout time.Duration) (net.Conn, error) } // MemcachedClientConfig defines how a MemcachedClient should be constructed. @@ -77,6 +84,12 @@ type MemcachedClientConfig struct { CBFailures uint `yaml:"circuit_breaker_consecutive_failures"` CBTimeout time.Duration `yaml:"circuit_breaker_timeout"` // reset error count after this long CBInterval time.Duration `yaml:"circuit_breaker_interval"` // remain closed for this long after CBFailures errors + + // TLSEnabled enables connecting to Memcached with TLS. + TLSEnabled bool `yaml:"tls_enabled"` + + // TLS to use to connect to the Memcached server. + TLS dstls.ClientConfig `yaml:",inline"` } // RegisterFlagsWithPrefix adds the flags required to config this to the given FlagSet @@ -92,6 +105,8 @@ func (cfg *MemcachedClientConfig) RegisterFlagsWithPrefix(prefix, description st f.DurationVar(&cfg.CBTimeout, prefix+"memcached.circuit-breaker-timeout", 10*time.Second, description+"Duration circuit-breaker remains open after tripping (if zero then 60 seconds is used).") f.DurationVar(&cfg.CBInterval, prefix+"memcached.circuit-breaker-interval", 10*time.Second, description+"Reset circuit-breaker counts after this long (if zero then never reset).") f.IntVar(&cfg.MaxItemSize, prefix+"memcached.max-item-size", 0, description+"The maximum size of an item stored in memcached. Bigger items are not stored. If set to 0, no maximum size is enforced.") + f.BoolVar(&cfg.TLSEnabled, prefix+"memcached.tls-enabled", false, "Enable connecting to Memcached with TLS.") + cfg.TLS.RegisterFlagsWithPrefix(prefix+"memcached.", f) } // NewMemcachedClient creates a new MemcacheClient that gets its server list @@ -112,9 +127,26 @@ func NewMemcachedClient(cfg MemcachedClientConfig, name string, r prometheus.Reg "name": name, }, r)) + dialTimeout := net.DialTimeout + if cfg.TLSEnabled { + cfg, err := cfg.TLS.GetTLSConfig() + if err != nil { + level.Error(logger).Log("msg", "couldn't create TLS configuration", "err", err) + } else { + dialTimeout = func(network, address string, timeout time.Duration) (net.Conn, error) { + base := new(net.Dialer) + base.Timeout = timeout + + return tls.DialWithDialer(base, network, address, cfg) + } + } + } + newClient := &memcachedClient{ + DialTimeout: dialTimeout, name: name, Client: client, + cfg: cfg, serverList: selector, hostname: cfg.Host, service: cfg.Service, @@ -143,6 +175,8 @@ func NewMemcachedClient(cfg MemcachedClientConfig, name string, r prometheus.Reg } if cfg.CBFailures > 0 { newClient.Client.DialTimeout = newClient.dialViaCircuitBreaker + } else { + newClient.Client.DialTimeout = dialTimeout } if len(cfg.Addresses) > 0 { @@ -182,7 +216,7 @@ func (c *memcachedClient) dialViaCircuitBreaker(network, address string, timeout c.Unlock() conn, err := cb.Execute(func() (interface{}, error) { - return net.DialTimeout(network, address, timeout) + return c.DialTimeout(network, address, timeout) }) if err != nil { return nil, err