Require cipher suites to be specified in the configuration.

* Configuration requires a cipher suite
* Removed default cipher suites
* Added help to assist with TLS configuration and list available cipher suites.
This commit is contained in:
Colin Sullivan
2015-11-20 16:00:25 -07:00
parent 0675a8fd1f
commit 0ff1b55fa9
10 changed files with 181 additions and 11 deletions

View File

@@ -19,6 +19,7 @@ func main() {
var showVersion bool
var debugAndTrace bool
var configFile string
var showTlsHelp bool
// Parse flags
flag.IntVar(&opts.Port, "port", 0, "Port to listen on.")
@@ -52,6 +53,7 @@ func main() {
flag.BoolVar(&showVersion, "v", false, "Print version information.")
flag.IntVar(&opts.ProfPort, "profile", 0, "Profiling HTTP port")
flag.StringVar(&opts.RoutesStr, "routes", "", "Routes to actively solicit a connection.")
flag.BoolVar(&showTlsHelp, "help_tls", false, "TLS help.")
// Not public per se, will be replaced with dynamic system, but can be used to lower memory footprint when
// lots of connections present.
@@ -66,6 +68,10 @@ func main() {
server.PrintServerAndExit()
}
if showTlsHelp {
server.PrintTlsHelpAndDie()
}
// One flag can set multiple options.
if debugAndTrace {
opts.Trace, opts.Debug = true, true

View File

@@ -7,6 +7,10 @@ net: apcera.me # net interface
tls {
cert_file: "./configs/certs/server.pem"
key_file: "./configs/certs/key.pem"
cipher_suites: [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}
authorization {

View File

@@ -0,0 +1,33 @@
# Simple TLS config file
port: 4443
net: apcera.me # net interface
tls {
cert_file: "./configs/certs/server.pem"
key_file: "./configs/certs/key.pem"
cipher_suites: [
"TLS_RSA_WITH_RC4_128_SHA",
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
# "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
# "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
]
}
authorization {
user: derek
password: buckley
timeout: 1
}

View File

@@ -14,6 +14,7 @@ import (
"time"
"github.com/nats-io/gnatsd/conf"
"os"
)
// Options block for gnatsd server.
@@ -66,6 +67,7 @@ type tlsConfig struct {
keyFile string
caFile string
verify bool
ciphers []uint16
}
// ProcessConfigFile processes a configuration file.
@@ -199,6 +201,67 @@ func parseAuthorization(am map[string]interface{}) authorization {
return auth
}
// keep one place where we hold all of the available ciphers
var 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_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,
// go 1.5 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
// go 1.5 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
}
// For Usage...
func PrintTlsHelpAndDie() {
var tlsUsage = `
TLS configuration is specified in the tls section of a configuration file:
e.g.
tls {
cert_file: "./certs/server-cert.pem"
key_file: "./certs/server-key.pem"
ca_file: "./certs/ca.pem"
verify: true
cipher_suites: [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}
Available cipher suites include:
`
fmt.Printf("%s\n", tlsUsage)
for k, _ := range cipherMap {
fmt.Printf(" %s\n", k);
}
fmt.Printf("\n")
os.Exit(0)
}
func parseCipher(cipherName string) (uint16, error) {
cipher, exists := cipherMap[cipherName];
if !exists {
return 0, fmt.Errorf("Unrecognized cipher %s", cipherName);
}
return cipher, nil;
}
// Helper function to parse TLS configs.
func parseTLS(tlsm map[string]interface{}) (*tls.Config, error) {
tc := tlsConfig{}
@@ -228,11 +291,29 @@ func parseTLS(tlsm map[string]interface{}) (*tls.Config, error) {
return nil, fmt.Errorf("error parsing tls config, expected 'verify' to be a boolean")
}
tc.verify = verify
case "cipher_suites":
ra := mv.([]interface{})
if len(ra) == 0 {
return nil, fmt.Errorf("error parsing tls config, 'cipher_suites' cannot be empty.")
}
tc.ciphers = make([]uint16, 0, len(ra))
for _, r := range ra {
cipher, err := parseCipher(r.(string))
if err != nil {
return nil, err
}
tc.ciphers = append(tc.ciphers, cipher)
}
default:
return nil, fmt.Errorf("error parsing tls config, unknown field [%q]", mk)
}
}
// specifing a cipher suite is required.
if (tc.ciphers == nil) {
return nil, fmt.Errorf("error parsing tls config, 'cipher_suites' not present.")
}
// Now load in cert and private key
cert, err := tls.LoadX509KeyPair(tc.certFile, tc.keyFile)
if err != nil {
@@ -242,21 +323,16 @@ func parseTLS(tlsm map[string]interface{}) (*tls.Config, error) {
if err != nil {
return nil, fmt.Errorf("error parsing certificate: %v", err)
}
// Create TLSConfig
// We will determine the cipher suites that we prefer.
config := tls.Config{
Certificates: []tls.Certificate{cert},
PreferServerCipherSuites: true,
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
// The SHA384 versions are only in Go1.5
// tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
// tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_RC4_128_SHA,
},
CipherSuites: tc.ciphers,
}
// Require client certificates as needed
if tc.verify == true {
config.ClientAuth = tls.RequireAnyClientCert

View File

@@ -100,13 +100,12 @@ func TestTLSConfigFile(t *testing.T) {
golden, opts)
}
// Now check TLSConfig a bit more closely
// CipherSuites
// The default CipherSuites we recommend
ciphers := []uint16{
// tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
// tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_RC4_128_SHA,
}
if !reflect.DeepEqual(tlsConfig.CipherSuites, ciphers) {
t.Fatalf("Got incorrect cipher suite list: [%+v]", tlsConfig.CipherSuites)
@@ -125,6 +124,40 @@ func TestTLSConfigFile(t *testing.T) {
if err := cert.VerifyHostname("localhost"); err != nil {
t.Fatalf("Could not verify hostname in certificate: %v\n", err)
}
// Now test adding cipher suites.
opts, err = ProcessConfigFile("./configs/tls_ciphers.conf")
if err != nil {
t.Fatalf("Received an error reading config file: %v\n", err)
}
tlsConfig = opts.TLSConfig
if tlsConfig == nil {
t.Fatal("Expected opts.TLSConfig to be non-nil")
}
// CipherSuites listed in the config - test all of them.
ciphers = []uint16{
tls.TLS_RSA_WITH_RC4_128_SHA,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
//tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
//tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
}
if !reflect.DeepEqual(tlsConfig.CipherSuites, ciphers) {
t.Fatalf("Got incorrect cipher suite list: [%+v]", tlsConfig.CipherSuites)
}
}
func TestMergeOverrides(t *testing.T) {

View File

@@ -34,6 +34,7 @@ Cluster Options:
Common Options:
-h, --help Show this message
-v, --version Show version
--help_tls TLS help.
`
// Usage will print out the flag options for the server.

View File

@@ -17,6 +17,10 @@ cluster {
# Optional certificate authority verifying connected routes
# Required when we have self-signed CA, etc.
ca_file: "./configs/certs/ca.pem"
cipher_suites: [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}
# Routes are actively solicited and connected to from this server.

View File

@@ -17,6 +17,10 @@ cluster {
# Optional certificate authority verifying connected routes
# Required when we have self-signed CA, etc.
ca_file: "./configs/certs/ca.pem"
cipher_suites: [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}
# Routes are actively solicited and connected to from this server.

View File

@@ -9,6 +9,10 @@ tls {
cert_file: "./configs/certs/server-cert.pem"
# Server private key
key_file: "./configs/certs/server-key.pem"
cipher_suites: [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}
authorization {

View File

@@ -13,4 +13,9 @@ tls {
ca_file: "./configs/certs/ca.pem"
# Require a client certificate
verify: true
cipher_suites: [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}