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:
		| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jeff Mitchell
					Jeff Mitchell