Skip to content

Commit

Permalink
Add option to set cluster TLS cipher suites. (#3228)
Browse files Browse the repository at this point in the history
* Add option to set cluster TLS cipher suites.

Fixes #3227
  • Loading branch information
jefferai authored Aug 30, 2017
1 parent 2ca896c commit 3f31ed7
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 8 deletions.
10 changes: 9 additions & 1 deletion command/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions command/server/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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: "",
Expand Down
1 change: 1 addition & 0 deletions command/server/test-fixtures/config.hcl.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions helper/tlsutil/tls.go → helper/tlsutil/tlsutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions helper/tlsutil/tlsutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

Expand Down
4 changes: 3 additions & 1 deletion vault/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -414,6 +414,7 @@ func (c *Core) ClusterTLSConfig() (*tls.Config, error) {
RootCAs: caPool,
ClientCAs: caPool,
NextProtos: clientHello.SupportedProtos,
CipherSuites: c.clusterCipherSuites,
}

switch {
Expand All @@ -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
Expand Down
34 changes: 34 additions & 0 deletions vault/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
13 changes: 13 additions & 0 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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.

Expand Down
9 changes: 5 additions & 4 deletions vault/request_forwarding.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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,
})
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions vault/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 3f31ed7

Please sign in to comment.