From 8139801de63e1ab769cc8fda6370e9b178e57468 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Mon, 22 Mar 2021 19:08:30 -0400 Subject: [PATCH] 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. --- server/opts.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/server/opts.go b/server/opts.go index 639d0c74..e4cbca82 100644 --- a/server/opts.go +++ b/server/opts.go @@ -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