tls: return auto_encrypt cert for listeners (#6489)

* fix cert check

* fix lock

* add tests

* test: add comments describing expected behavior for auto-encrypt and manual certificates

* test: expect nil *tls.Certificate for empty string certFile or keyFile arg
This commit is contained in:
Hans Hasselberg 2019-11-12 19:40:32 +01:00 committed by Mike Morris
parent a7e37417ed
commit 707efc5b39
2 changed files with 55 additions and 25 deletions

View File

@ -326,7 +326,7 @@ func (c *Configurator) check(config Config, pool *x509.CertPool, cert *tls.Certi
if pool == nil { if pool == nil {
return fmt.Errorf("VerifyIncoming set, and no CA certificate provided!") return fmt.Errorf("VerifyIncoming set, and no CA certificate provided!")
} }
if cert == nil || cert.Certificate == nil { if cert == nil {
return fmt.Errorf("VerifyIncoming set, and no Cert/Key pair provided!") return fmt.Errorf("VerifyIncoming set, and no Cert/Key pair provided!")
} }
} }
@ -351,7 +351,7 @@ func (c *Config) baseVerifyIncoming() bool {
func loadKeyPair(certFile, keyFile string) (*tls.Certificate, error) { func loadKeyPair(certFile, keyFile string) (*tls.Certificate, error) {
if certFile == "" || keyFile == "" { if certFile == "" || keyFile == "" {
return &tls.Certificate{}, nil return nil, nil
} }
cert, err := tls.LoadX509KeyPair(certFile, keyFile) cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil { if err != nil {
@ -428,11 +428,16 @@ func (c *Configurator) commonTLSConfig(verifyIncoming bool) *tls.Config {
tlsConfig.PreferServerCipherSuites = c.base.PreferServerCipherSuites tlsConfig.PreferServerCipherSuites = c.base.PreferServerCipherSuites
// GetCertificate is used when acting as a server and responding to // GetCertificate is used when acting as a server and responding to
// client requests. Always return the manually configured cert, because // client requests. Default to the manually configured cert, but allow
// on the server this is all we have. And on the client, this is the // autoEncrypt cert too so that a client can encrypt incoming
// only sensitive option. // connections without having a manual cert configured.
tlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) { tlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return c.manual.cert, nil cert := c.manual.cert
if cert == nil {
cert = c.autoEncrypt.cert
}
return cert, nil
} }
// GetClientCertificate is used when acting as a client and responding // GetClientCertificate is used when acting as a client and responding
@ -630,9 +635,9 @@ func (c *Configurator) OutgoingRPCWrapper() DCWrapper {
// there is no cert, it will return a time in the past. // there is no cert, it will return a time in the past.
func (c *Configurator) AutoEncryptCertNotAfter() time.Time { func (c *Configurator) AutoEncryptCertNotAfter() time.Time {
c.RLock() c.RLock()
defer c.RUnlock()
tlsCert := c.autoEncrypt.cert tlsCert := c.autoEncrypt.cert
c.RUnlock() if tlsCert == nil || tlsCert.Certificate == nil {
if tlsCert == nil {
return time.Now().AddDate(0, 0, -1) return time.Now().AddDate(0, 0, -1)
} }
cert, err := x509.ParseCertificate(tlsCert.Certificate[0]) cert, err := x509.ParseCertificate(tlsCert.Certificate[0])

View File

@ -293,17 +293,16 @@ func TestConfigurator_loadKeyPair(t *testing.T) {
cert, key string cert, key string
shoulderr bool shoulderr bool
isnil bool isnil bool
isempty bool
} }
variants := []variant{ variants := []variant{
{"", "", false, false, true}, {"", "", false, true},
{"bogus", "", false, false, true}, {"bogus", "", false, true},
{"", "bogus", false, false, true}, {"", "bogus", false, true},
{"../test/key/ourdomain.cer", "", false, false, true}, {"../test/key/ourdomain.cer", "", false, true},
{"", "../test/key/ourdomain.key", false, false, true}, {"", "../test/key/ourdomain.key", false, true},
{"bogus", "bogus", true, true, false}, {"bogus", "bogus", true, true},
{"../test/key/ourdomain.cer", "../test/key/ourdomain.key", {"../test/key/ourdomain.cer", "../test/key/ourdomain.key",
false, false, false}, false, false},
} }
for i, v := range variants { for i, v := range variants {
info := fmt.Sprintf("case %d", i) info := fmt.Sprintf("case %d", i)
@ -317,10 +316,6 @@ func TestConfigurator_loadKeyPair(t *testing.T) {
require.NoError(t, err1, info) require.NoError(t, err1, info)
require.NoError(t, err2, info) require.NoError(t, err2, info)
} }
if v.isempty {
require.Empty(t, cert1.Certificate, info)
require.Empty(t, cert2.Certificate, info)
}
if v.isnil { if v.isnil {
require.Nil(t, cert1, info) require.Nil(t, cert1, info)
require.Nil(t, cert2, info) require.Nil(t, cert2, info)
@ -538,17 +533,47 @@ func TestConfigurator_CommonTLSConfigGetClientCertificate(t *testing.T) {
c, err := NewConfigurator(Config{}, nil) c, err := NewConfigurator(Config{}, nil)
require.NoError(t, err) require.NoError(t, err)
cert, err := c.commonTLSConfig(false).GetCertificate(nil) cert, err := c.commonTLSConfig(false).GetClientCertificate(nil)
require.NoError(t, err) require.NoError(t, err)
require.Nil(t, cert.Certificate) require.Nil(t, cert)
c.manual.cert = &tls.Certificate{} c1, err := loadKeyPair("../test/key/something_expired.cer", "../test/key/something_expired.key")
cert, err = c.commonTLSConfig(false).GetCertificate(nil) require.NoError(t, err)
c.manual.cert = c1
cert, err = c.commonTLSConfig(false).GetClientCertificate(nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, c.manual.cert, cert) require.Equal(t, c.manual.cert, cert)
c2, err := loadKeyPair("../test/key/ourdomain.cer", "../test/key/ourdomain.key")
require.NoError(t, err)
c.autoEncrypt.cert = c2
cert, err = c.commonTLSConfig(false).GetClientCertificate(nil) cert, err = c.commonTLSConfig(false).GetClientCertificate(nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, c.autoEncrypt.cert, cert)
}
func TestConfigurator_CommonTLSConfigGetCertificate(t *testing.T) {
c, err := NewConfigurator(Config{}, nil)
require.NoError(t, err)
cert, err := c.commonTLSConfig(false).GetCertificate(nil)
require.NoError(t, err)
require.Nil(t, cert)
// Setting a certificate as the auto-encrypt cert will return it as the regular server certificate
c1, err := loadKeyPair("../test/key/something_expired.cer", "../test/key/something_expired.key")
require.NoError(t, err)
c.autoEncrypt.cert = c1
cert, err = c.commonTLSConfig(false).GetCertificate(nil)
require.NoError(t, err)
require.Equal(t, c.autoEncrypt.cert, cert)
// Setting a different certificate as a manual cert will override the auto-encrypt cert and instead return the manual cert
c2, err := loadKeyPair("../test/key/ourdomain.cer", "../test/key/ourdomain.key")
require.NoError(t, err)
c.manual.cert = c2
cert, err = c.commonTLSConfig(false).GetCertificate(nil)
require.NoError(t, err)
require.Equal(t, c.manual.cert, cert) require.Equal(t, c.manual.cert, cert)
} }
@ -715,7 +740,7 @@ func TestConfigurator_UpdateSetsStuff(t *testing.T) {
c, err := NewConfigurator(Config{}, nil) c, err := NewConfigurator(Config{}, nil)
require.NoError(t, err) require.NoError(t, err)
require.Nil(t, c.caPool) require.Nil(t, c.caPool)
require.Nil(t, c.manual.cert.Certificate) require.Nil(t, c.manual.cert)
require.Equal(t, c.base, &Config{}) require.Equal(t, c.base, &Config{})
require.Equal(t, 1, c.version) require.Equal(t, 1, c.version)