tls: support multiple key/cert pairs, for SNI

This does not add tests or docs, this is to sound out how people feel about the
API.

Turn the TLS options cert_file and key_file into list-separated paths (so
colon-delimited if on Unix, like `$PATH`); the count of entries must match, and
the keys and certs should zip together.

With this change, TLS SNI works to pick the correct cert to return for a given
connection, allowing a NATS server to have multiple identities.
This commit is contained in:
Phil Pennock
2021-03-22 19:08:30 -04:00
parent b828e01761
commit 8139801de6

View File

@@ -3715,16 +3715,28 @@ func GenTLSConfig(tc *TLSConfigOpts) (*tls.Config, error) {
case tc.CertFile == "" && tc.KeyFile != "":
return nil, fmt.Errorf("missing 'cert_file' in TLS configuration")
case tc.CertFile != "" && tc.KeyFile != "":
// Now load in cert and private key
cert, err := tls.LoadX509KeyPair(tc.CertFile, tc.KeyFile)
if err != nil {
return nil, fmt.Errorf("error parsing X509 certificate/key pair: %v", err)
// To support multiple certs (for SANs) without rearchitecting our
// configuration, we accept a list, separated by the platform list
// separator; both specs must contain the same count of entries.
// (So, colon-separated on Unix, semi-colon on Windows).
certFiles := strings.Split(tc.CertFile, string(os.PathListSeparator))
keyFiles := strings.Split(tc.KeyFile, string(os.PathListSeparator))
if len(certFiles) != len(keyFiles) {
return nil, fmt.Errorf("TLS configuration has %d entries in cert_file but %d in key_file", len(certFiles), len(keyFiles))
}
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, fmt.Errorf("error parsing certificate: %v", err)
config.Certificates = make([]tls.Certificate, len(certFiles))
// Now load in cert and private keys
for i := range certFiles {
cert, err := tls.LoadX509KeyPair(certFiles[i], keyFiles[i])
if err != nil {
return nil, fmt.Errorf("error parsing X509 certificate/key pair: %v", err)
}
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, fmt.Errorf("error parsing certificate: %v", err)
}
config.Certificates[i] = cert
}
config.Certificates = []tls.Certificate{cert}
}
// Require client certificates as needed