mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-31 18:48:08 +00:00
Add option to set cluster TLS cipher suites. (#3228)
* Add option to set cluster TLS cipher suites. Fixes #3227
This commit is contained in:
@@ -43,6 +43,8 @@ type Config struct {
|
|||||||
DefaultLeaseTTLRaw interface{} `hcl:"default_lease_ttl"`
|
DefaultLeaseTTLRaw interface{} `hcl:"default_lease_ttl"`
|
||||||
|
|
||||||
ClusterName string `hcl:"cluster_name"`
|
ClusterName string `hcl:"cluster_name"`
|
||||||
|
ClusterCipherSuites string `hcl:"cluster_cipher_suites"`
|
||||||
|
|
||||||
PluginDirectory string `hcl:"plugin_directory"`
|
PluginDirectory string `hcl:"plugin_directory"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,6 +278,11 @@ func (c *Config) Merge(c2 *Config) *Config {
|
|||||||
result.ClusterName = c2.ClusterName
|
result.ClusterName = c2.ClusterName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.ClusterCipherSuites = c.ClusterCipherSuites
|
||||||
|
if c2.ClusterCipherSuites != "" {
|
||||||
|
result.ClusterCipherSuites = c2.ClusterCipherSuites
|
||||||
|
}
|
||||||
|
|
||||||
result.EnableUI = c.EnableUI
|
result.EnableUI = c.EnableUI
|
||||||
if c2.EnableUI {
|
if c2.EnableUI {
|
||||||
result.EnableUI = c2.EnableUI
|
result.EnableUI = c2.EnableUI
|
||||||
@@ -376,6 +383,7 @@ func ParseConfig(d string, logger log.Logger) (*Config, error) {
|
|||||||
"default_lease_ttl",
|
"default_lease_ttl",
|
||||||
"max_lease_ttl",
|
"max_lease_ttl",
|
||||||
"cluster_name",
|
"cluster_name",
|
||||||
|
"cluster_cipher_suites",
|
||||||
"plugin_directory",
|
"plugin_directory",
|
||||||
}
|
}
|
||||||
if err := checkHCLKeys(list, valid); err != nil {
|
if err := checkHCLKeys(list, valid); err != nil {
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ func TestLoadConfigFile_json(t *testing.T) {
|
|||||||
DisableClustering: true,
|
DisableClustering: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ClusterCipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
|
|
||||||
Telemetry: &Telemetry{
|
Telemetry: &Telemetry{
|
||||||
StatsiteAddr: "baz",
|
StatsiteAddr: "baz",
|
||||||
StatsdAddr: "",
|
StatsdAddr: "",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"address": "127.0.0.1:443"
|
"address": "127.0.0.1:443"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
"cluster_cipher_suites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
"storage": {
|
"storage": {
|
||||||
"consul": {
|
"consul": {
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ func ParseCiphers(cipherStr string) ([]uint16, error) {
|
|||||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||||
@@ -32,10 +33,14 @@ func ParseCiphers(cipherStr string) ([]uint16, error) {
|
|||||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||||
}
|
}
|
||||||
for _, cipher := range ciphers {
|
for _, cipher := range ciphers {
|
||||||
if v, ok := cipherMap[cipher]; ok {
|
if v, ok := cipherMap[cipher]; ok {
|
||||||
@@ -7,12 +7,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestParseCiphers(t *testing.T) {
|
func TestParseCiphers(t *testing.T) {
|
||||||
testOk := "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384"
|
testOk := "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305"
|
||||||
v, err := ParseCiphers(testOk)
|
v, err := ParseCiphers(testOk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(v) != 12 {
|
if len(v) != 17 {
|
||||||
t.Fatal("missed ciphers after parse")
|
t.Fatal("missed ciphers after parse")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -398,7 +398,7 @@ func (c *Core) ClusterTLSConfig() (*tls.Config, error) {
|
|||||||
//c.logger.Trace("core: performing server config lookup")
|
//c.logger.Trace("core: performing server config lookup")
|
||||||
for _, v := range clientHello.SupportedProtos {
|
for _, v := range clientHello.SupportedProtos {
|
||||||
switch v {
|
switch v {
|
||||||
case "h2", "req_fw_sb-act_v1":
|
case "h2", requestForwardingALPN:
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown ALPN proto %s", v)
|
return nil, fmt.Errorf("unknown ALPN proto %s", v)
|
||||||
}
|
}
|
||||||
@@ -414,6 +414,7 @@ func (c *Core) ClusterTLSConfig() (*tls.Config, error) {
|
|||||||
RootCAs: caPool,
|
RootCAs: caPool,
|
||||||
ClientCAs: caPool,
|
ClientCAs: caPool,
|
||||||
NextProtos: clientHello.SupportedProtos,
|
NextProtos: clientHello.SupportedProtos,
|
||||||
|
CipherSuites: c.clusterCipherSuites,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -438,6 +439,7 @@ func (c *Core) ClusterTLSConfig() (*tls.Config, error) {
|
|||||||
GetClientCertificate: clientLookup,
|
GetClientCertificate: clientLookup,
|
||||||
GetConfigForClient: serverConfigLookup,
|
GetConfigForClient: serverConfigLookup,
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
|
CipherSuites: c.clusterCipherSuites,
|
||||||
}
|
}
|
||||||
|
|
||||||
var localCert bytes.Buffer
|
var localCert bytes.Buffer
|
||||||
|
|||||||
@@ -383,3 +383,37 @@ func testCluster_ForwardRequests(t *testing.T, c *TestClusterCore, rootToken, re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCluster_CustomCipherSuites(t *testing.T) {
|
||||||
|
cluster := NewTestCluster(t, &CoreConfig{
|
||||||
|
ClusterCipherSuites: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
|
}, nil)
|
||||||
|
cluster.Start()
|
||||||
|
defer cluster.Cleanup()
|
||||||
|
core := cluster.Cores[0]
|
||||||
|
|
||||||
|
// Wait for core to become active
|
||||||
|
TestWaitActive(t, core.Core)
|
||||||
|
|
||||||
|
tlsConf, err := core.Core.ClusterTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", core.Listeners[0].Address.IP.String(), core.Listeners[0].Address.Port+105), tlsConf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
err = conn.Handshake()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if conn.ConnectionState().CipherSuite != tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 {
|
||||||
|
var availCiphers string
|
||||||
|
for _, cipher := range core.clusterCipherSuites {
|
||||||
|
availCiphers += fmt.Sprintf("%x ", cipher)
|
||||||
|
}
|
||||||
|
t.Fatalf("got bad negotiated cipher %x, core-set suites are %s", conn.ConnectionState().CipherSuite, availCiphers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/helper/logformat"
|
"github.com/hashicorp/vault/helper/logformat"
|
||||||
"github.com/hashicorp/vault/helper/mlock"
|
"github.com/hashicorp/vault/helper/mlock"
|
||||||
"github.com/hashicorp/vault/helper/reload"
|
"github.com/hashicorp/vault/helper/reload"
|
||||||
|
"github.com/hashicorp/vault/helper/tlsutil"
|
||||||
"github.com/hashicorp/vault/logical"
|
"github.com/hashicorp/vault/logical"
|
||||||
"github.com/hashicorp/vault/physical"
|
"github.com/hashicorp/vault/physical"
|
||||||
"github.com/hashicorp/vault/shamir"
|
"github.com/hashicorp/vault/shamir"
|
||||||
@@ -285,6 +286,8 @@ type Core struct {
|
|||||||
//
|
//
|
||||||
// Name
|
// Name
|
||||||
clusterName string
|
clusterName string
|
||||||
|
// Specific cipher suites to use for clustering, if any
|
||||||
|
clusterCipherSuites []uint16
|
||||||
// Used to modify cluster parameters
|
// Used to modify cluster parameters
|
||||||
clusterParamsLock sync.RWMutex
|
clusterParamsLock sync.RWMutex
|
||||||
// The private key stored in the barrier used for establishing
|
// The private key stored in the barrier used for establishing
|
||||||
@@ -395,6 +398,8 @@ type CoreConfig struct {
|
|||||||
|
|
||||||
ClusterName string `json:"cluster_name" structs:"cluster_name" mapstructure:"cluster_name"`
|
ClusterName string `json:"cluster_name" structs:"cluster_name" mapstructure:"cluster_name"`
|
||||||
|
|
||||||
|
ClusterCipherSuites string `json:"cluster_cipher_suites" structs:"cluster_cipher_suites" mapstructure:"cluster_cipher_suites"`
|
||||||
|
|
||||||
EnableUI bool `json:"ui" structs:"ui" mapstructure:"ui"`
|
EnableUI bool `json:"ui" structs:"ui" mapstructure:"ui"`
|
||||||
|
|
||||||
PluginDirectory string `json:"plugin_directory" structs:"plugin_directory" mapstructure:"plugin_directory"`
|
PluginDirectory string `json:"plugin_directory" structs:"plugin_directory" mapstructure:"plugin_directory"`
|
||||||
@@ -459,6 +464,14 @@ func NewCore(conf *CoreConfig) (*Core, error) {
|
|||||||
enableMlock: !conf.DisableMlock,
|
enableMlock: !conf.DisableMlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.ClusterCipherSuites != "" {
|
||||||
|
suites, err := tlsutil.ParseCiphers(conf.ClusterCipherSuites)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errwrap.Wrapf("error parsing cluster cipher suites: {{err}}", err)
|
||||||
|
}
|
||||||
|
c.clusterCipherSuites = suites
|
||||||
|
}
|
||||||
|
|
||||||
c.corsConfig = &CORSConfig{core: c}
|
c.corsConfig = &CORSConfig{core: c}
|
||||||
// Load CORS config and provide a value for the core field.
|
// Load CORS config and provide a value for the core field.
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
clusterListenerAcceptDeadline = 500 * time.Millisecond
|
clusterListenerAcceptDeadline = 500 * time.Millisecond
|
||||||
heartbeatInterval = 30 * time.Second
|
heartbeatInterval = 30 * time.Second
|
||||||
|
requestForwardingALPN = "req_fw_sb-act_v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Starts the listeners and servers necessary to handle forwarded requests
|
// Starts the listeners and servers necessary to handle forwarded requests
|
||||||
@@ -45,7 +46,7 @@ func (c *Core) startForwarding() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The server supports all of the possible protos
|
// The server supports all of the possible protos
|
||||||
tlsConfig.NextProtos = []string{"h2", "req_fw_sb-act_v1"}
|
tlsConfig.NextProtos = []string{"h2", requestForwardingALPN}
|
||||||
|
|
||||||
// Create our RPC server and register the request handler server
|
// Create our RPC server and register the request handler server
|
||||||
c.clusterParamsLock.Lock()
|
c.clusterParamsLock.Lock()
|
||||||
@@ -144,13 +145,13 @@ func (c *Core) startForwarding() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch tlsConn.ConnectionState().NegotiatedProtocol {
|
switch tlsConn.ConnectionState().NegotiatedProtocol {
|
||||||
case "req_fw_sb-act_v1":
|
case requestForwardingALPN:
|
||||||
if !ha {
|
if !ha {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Trace("core: got req_fw_sb-act_v1 connection")
|
c.logger.Trace("core: got request forwarding connection")
|
||||||
go fws.ServeConn(conn, &http2.ServeConnOpts{
|
go fws.ServeConn(conn, &http2.ServeConnOpts{
|
||||||
Handler: c.rpcServer,
|
Handler: c.rpcServer,
|
||||||
})
|
})
|
||||||
@@ -227,7 +228,7 @@ func (c *Core) refreshRequestForwardingConnection(clusterAddr string) error {
|
|||||||
// the TLS state.
|
// the TLS state.
|
||||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||||
c.rpcClientConn, err = grpc.DialContext(ctx, clusterURL.Host,
|
c.rpcClientConn, err = grpc.DialContext(ctx, clusterURL.Host,
|
||||||
grpc.WithDialer(c.getGRPCDialer("req_fw_sb-act_v1", "", nil)),
|
grpc.WithDialer(c.getGRPCDialer(requestForwardingALPN, "", nil)),
|
||||||
grpc.WithInsecure(), // it's not, we handle it in the dialer
|
grpc.WithInsecure(), // it's not, we handle it in the dialer
|
||||||
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||||
Time: 2 * heartbeatInterval,
|
Time: 2 * heartbeatInterval,
|
||||||
|
|||||||
@@ -1105,6 +1105,8 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te
|
|||||||
coreConfig.Logger = base.Logger
|
coreConfig.Logger = base.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
coreConfig.ClusterCipherSuites = base.ClusterCipherSuites
|
||||||
|
|
||||||
coreConfig.DisableCache = base.DisableCache
|
coreConfig.DisableCache = base.DisableCache
|
||||||
|
|
||||||
coreConfig.DevToken = base.DevToken
|
coreConfig.DevToken = base.DevToken
|
||||||
|
|||||||
Reference in New Issue
Block a user