Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to set cluster TLS cipher suites. #3228

Merged
merged 3 commits into from
Aug 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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