Add option to set cluster TLS cipher suites. (#3228)
* Add option to set cluster TLS cipher suites. Fixes #3227
This commit is contained in:
parent
d31ce49771
commit
3edb337a00
|
@ -42,7 +42,9 @@ type Config struct {
|
|||
DefaultLeaseTTL time.Duration `hcl:"-"`
|
||||
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"`
|
||||
}
|
||||
|
||||
|
@ -276,6 +278,11 @@ func (c *Config) Merge(c2 *Config) *Config {
|
|||
result.ClusterName = c2.ClusterName
|
||||
}
|
||||
|
||||
result.ClusterCipherSuites = c.ClusterCipherSuites
|
||||
if c2.ClusterCipherSuites != "" {
|
||||
result.ClusterCipherSuites = c2.ClusterCipherSuites
|
||||
}
|
||||
|
||||
result.EnableUI = c.EnableUI
|
||||
if c2.EnableUI {
|
||||
result.EnableUI = c2.EnableUI
|
||||
|
@ -376,6 +383,7 @@ func ParseConfig(d string, logger log.Logger) (*Config, error) {
|
|||
"default_lease_ttl",
|
||||
"max_lease_ttl",
|
||||
"cluster_name",
|
||||
"cluster_cipher_suites",
|
||||
"plugin_directory",
|
||||
}
|
||||
if err := checkHCLKeys(list, valid); err != nil {
|
||||
|
|
|
@ -99,6 +99,8 @@ func TestLoadConfigFile_json(t *testing.T) {
|
|||
DisableClustering: true,
|
||||
},
|
||||
|
||||
ClusterCipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
|
||||
Telemetry: &Telemetry{
|
||||
StatsiteAddr: "baz",
|
||||
StatsdAddr: "",
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"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": {
|
||||
"consul": {
|
||||
"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_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_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_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,
|
||||
|
@ -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_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_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_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_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 {
|
||||
if v, ok := cipherMap[cipher]; ok {
|
|
@ -7,12 +7,12 @@ import (
|
|||
)
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(v) != 12 {
|
||||
if len(v) != 17 {
|
||||
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")
|
||||
for _, v := range clientHello.SupportedProtos {
|
||||
switch v {
|
||||
case "h2", "req_fw_sb-act_v1":
|
||||
case "h2", requestForwardingALPN:
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown ALPN proto %s", v)
|
||||
}
|
||||
|
@ -414,6 +414,7 @@ func (c *Core) ClusterTLSConfig() (*tls.Config, error) {
|
|||
RootCAs: caPool,
|
||||
ClientCAs: caPool,
|
||||
NextProtos: clientHello.SupportedProtos,
|
||||
CipherSuites: c.clusterCipherSuites,
|
||||
}
|
||||
|
||||
switch {
|
||||
|
@ -438,6 +439,7 @@ func (c *Core) ClusterTLSConfig() (*tls.Config, error) {
|
|||
GetClientCertificate: clientLookup,
|
||||
GetConfigForClient: serverConfigLookup,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: c.clusterCipherSuites,
|
||||
}
|
||||
|
||||
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/mlock"
|
||||
"github.com/hashicorp/vault/helper/reload"
|
||||
"github.com/hashicorp/vault/helper/tlsutil"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
"github.com/hashicorp/vault/shamir"
|
||||
|
@ -285,6 +286,8 @@ type Core struct {
|
|||
//
|
||||
// Name
|
||||
clusterName string
|
||||
// Specific cipher suites to use for clustering, if any
|
||||
clusterCipherSuites []uint16
|
||||
// Used to modify cluster parameters
|
||||
clusterParamsLock sync.RWMutex
|
||||
// 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"`
|
||||
|
||||
ClusterCipherSuites string `json:"cluster_cipher_suites" structs:"cluster_cipher_suites" mapstructure:"cluster_cipher_suites"`
|
||||
|
||||
EnableUI bool `json:"ui" structs:"ui" mapstructure:"ui"`
|
||||
|
||||
PluginDirectory string `json:"plugin_directory" structs:"plugin_directory" mapstructure:"plugin_directory"`
|
||||
|
@ -459,6 +464,14 @@ func NewCore(conf *CoreConfig) (*Core, error) {
|
|||
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}
|
||||
// Load CORS config and provide a value for the core field.
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
const (
|
||||
clusterListenerAcceptDeadline = 500 * time.Millisecond
|
||||
heartbeatInterval = 30 * time.Second
|
||||
requestForwardingALPN = "req_fw_sb-act_v1"
|
||||
)
|
||||
|
||||
// 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
|
||||
tlsConfig.NextProtos = []string{"h2", "req_fw_sb-act_v1"}
|
||||
tlsConfig.NextProtos = []string{"h2", requestForwardingALPN}
|
||||
|
||||
// Create our RPC server and register the request handler server
|
||||
c.clusterParamsLock.Lock()
|
||||
|
@ -144,13 +145,13 @@ func (c *Core) startForwarding() error {
|
|||
}
|
||||
|
||||
switch tlsConn.ConnectionState().NegotiatedProtocol {
|
||||
case "req_fw_sb-act_v1":
|
||||
case requestForwardingALPN:
|
||||
if !ha {
|
||||
conn.Close()
|
||||
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{
|
||||
Handler: c.rpcServer,
|
||||
})
|
||||
|
@ -227,7 +228,7 @@ func (c *Core) refreshRequestForwardingConnection(clusterAddr string) error {
|
|||
// the TLS state.
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
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.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: 2 * heartbeatInterval,
|
||||
|
|
|
@ -1105,6 +1105,8 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te
|
|||
coreConfig.Logger = base.Logger
|
||||
}
|
||||
|
||||
coreConfig.ClusterCipherSuites = base.ClusterCipherSuites
|
||||
|
||||
coreConfig.DisableCache = base.DisableCache
|
||||
|
||||
coreConfig.DevToken = base.DevToken
|
||||
|
|
Loading…
Reference in New Issue