-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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 TLS cipher suite options and CA path support #2963
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ import ( | |
"time" | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove blank line and goimports |
||
"github.com/hashicorp/consul/lib" | ||
"crypto/tls" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. import sorting |
||
) | ||
|
||
func TestConfigEncryptBytes(t *testing.T) { | ||
|
@@ -354,7 +355,8 @@ func TestDecodeConfig(t *testing.T) { | |
} | ||
|
||
// TLS | ||
input = `{"verify_incoming": true, "verify_outgoing": true, "verify_server_hostname": true, "tls_min_version": "tls12"}` | ||
input = `{"verify_incoming": true, "verify_outgoing": true, "verify_server_hostname": true, "tls_min_version": "tls12", | ||
"tls_cipher_suites": "TLS_RSA_WITH_AES_256_CBC_SHA", "tls_prefer_server_cipher_suites": true}` | ||
config, err = DecodeConfig(bytes.NewReader([]byte(input))) | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
|
@@ -376,8 +378,16 @@ func TestDecodeConfig(t *testing.T) { | |
t.Fatalf("bad: %#v", config) | ||
} | ||
|
||
if len(config.TLSCipherSuites) != 1 || config.TLSCipherSuites[0] != tls.TLS_RSA_WITH_AES_256_CBC_SHA { | ||
t.Fatalf("bad: %#v", config) | ||
} | ||
|
||
if !config.TLSPreferServerCipherSuites { | ||
t.Fatalf("bad: %#v", config) | ||
} | ||
|
||
// TLS keys | ||
input = `{"ca_file": "my/ca/file", "cert_file": "my.cert", "key_file": "key.pem", "server_name": "example.com"}` | ||
input = `{"ca_file": "my/ca/file", "ca_path":"my/ca/path", "cert_file": "my.cert", "key_file": "key.pem", "server_name": "example.com"}` | ||
config, err = DecodeConfig(bytes.NewReader([]byte(input))) | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
|
@@ -386,6 +396,9 @@ func TestDecodeConfig(t *testing.T) { | |
if config.CAFile != "my/ca/file" { | ||
t.Fatalf("bad: %#v", config) | ||
} | ||
if config.CAPath != "my/ca/path" { | ||
t.Fatalf("bad: %#v", config) | ||
} | ||
if config.CertFile != "my.cert" { | ||
t.Fatalf("bad: %#v", config) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,8 @@ import ( | |
"net" | ||
"strings" | ||
"time" | ||
|
||
"github.com/hashicorp/go-rootcerts" | ||
) | ||
|
||
// DCWrapper is a function that is used to wrap a non-TLS connection | ||
|
@@ -51,6 +53,10 @@ type Config struct { | |
// or VerifyOutgoing to verify the TLS connection. | ||
CAFile string | ||
|
||
// CAPath is a path to a directory containing certificate authority files. This is used | ||
// with VerifyIncoming or VerifyOutgoing to verify the TLS connection. | ||
CAPath string | ||
|
||
// CertFile is used to provide a TLS certificate that is used for serving TLS connections. | ||
// Must be provided to serve TLS connections. | ||
CertFile string | ||
|
@@ -71,6 +77,13 @@ type Config struct { | |
|
||
// TLSMinVersion is the minimum accepted TLS version that can be used. | ||
TLSMinVersion string | ||
|
||
// CipherSuites is the list of TLS cipher suites to use. | ||
CipherSuites []uint16 | ||
|
||
// PreferServerCipherSuites specifies whether to prefer the server's ciphersuite | ||
// over the client ciphersuites. | ||
PreferServerCipherSuites bool | ||
} | ||
|
||
// AppendCA opens and parses the CA file and adds the certificates to | ||
|
@@ -130,15 +143,24 @@ func (c *Config) OutgoingTLSConfig() (*tls.Config, error) { | |
tlsConfig.ServerName = "VerifyServerHostname" | ||
tlsConfig.InsecureSkipVerify = false | ||
} | ||
if len(c.CipherSuites) != 0 { | ||
tlsConfig.CipherSuites = c.CipherSuites | ||
} | ||
if c.PreferServerCipherSuites { | ||
tlsConfig.PreferServerCipherSuites = true | ||
} | ||
|
||
// Ensure we have a CA if VerifyOutgoing is set | ||
if c.VerifyOutgoing && c.CAFile == "" { | ||
if c.VerifyOutgoing && c.CAFile == "" && c.CAPath == "" { | ||
return nil, fmt.Errorf("VerifyOutgoing set, and no CA certificate provided!") | ||
} | ||
|
||
// Parse the CA cert if any | ||
err := c.AppendCA(tlsConfig.RootCAs) | ||
if err != nil { | ||
// Parse the CA certs if any | ||
rootConfig := &rootcerts.Config{ | ||
CAFile: c.CAFile, | ||
CAPath: c.CAPath, | ||
} | ||
if err := rootcerts.ConfigureTLS(tlsConfig, rootConfig); err != nil { | ||
return nil, err | ||
} | ||
|
||
|
@@ -305,10 +327,27 @@ func (c *Config) IncomingTLSConfig() (*tls.Config, error) { | |
tlsConfig.ServerName = c.NodeName | ||
} | ||
|
||
// Parse the CA cert if any | ||
err := c.AppendCA(tlsConfig.ClientCAs) | ||
if err != nil { | ||
return nil, err | ||
// Set the cipher suites | ||
if len(c.CipherSuites) != 0 { | ||
tlsConfig.CipherSuites = c.CipherSuites | ||
} | ||
if c.PreferServerCipherSuites { | ||
tlsConfig.PreferServerCipherSuites = true | ||
} | ||
|
||
// Parse the CA certs if any | ||
if c.CAFile != "" { | ||
pool, err := rootcerts.LoadCAFile(c.CAFile) | ||
if err != nil { | ||
return nil, err | ||
} | ||
tlsConfig.ClientCAs = pool | ||
} else if c.CAPath != "" { | ||
pool, err := rootcerts.LoadCAPath(c.CAPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
tlsConfig.ClientCAs = pool | ||
} | ||
|
||
// Add cert/key | ||
|
@@ -322,7 +361,7 @@ func (c *Config) IncomingTLSConfig() (*tls.Config, error) { | |
// Check if we require verification | ||
if c.VerifyIncoming { | ||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert | ||
if c.CAFile == "" { | ||
if c.CAFile == "" && c.CAPath == "" { | ||
return nil, fmt.Errorf("VerifyIncoming set, and no CA certificate provided!") | ||
} | ||
if cert == nil { | ||
|
@@ -340,3 +379,43 @@ func (c *Config) IncomingTLSConfig() (*tls.Config, error) { | |
} | ||
return tlsConfig, nil | ||
} | ||
|
||
// ParseCiphers parse ciphersuites from the comma-separated string into recognized slice | ||
func ParseCiphers(cipherStr string) ([]uint16, error) { | ||
suites := []uint16{} | ||
|
||
cipherStr = strings.TrimSpace(cipherStr) | ||
if cipherStr == "" { | ||
return []uint16{}, nil | ||
} | ||
ciphers := strings.Split(cipherStr, ",") | ||
|
||
cipherMap := map[string]uint16{ | ||
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_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_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, | ||
"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, | ||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | ||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, | ||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_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_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, | ||
"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, | ||
} | ||
for _, cipher := range ciphers { | ||
if v, ok := cipherMap[cipher]; ok { | ||
suites = append(suites, v) | ||
} else { | ||
return suites, fmt.Errorf("unsupported cipher %q", cipher) | ||
} | ||
} | ||
|
||
return suites, nil | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gofmt this file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
goimports
for sorting imports correctly