Skip to content

Commit 6f3fe68

Browse files
authored
Merge pull request #216 from nicksardo/cherrypick-tls-fix
Cherrypick: Fix multiple secrets with same certificate
2 parents fb7844b + 58ceb9b commit 6f3fe68

File tree

2 files changed

+56
-30
lines changed

2 files changed

+56
-30
lines changed

pkg/loadbalancers/l7.go

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,9 @@ func (l *L7) checkProxy() (err error) {
190190
return nil
191191
}
192192

193-
func (l *L7) deleteOldSSLCerts() (err error) {
193+
func (l *L7) deleteOldSSLCerts() {
194194
if len(l.oldSSLCerts) == 0 {
195-
return nil
195+
return
196196
}
197197
certsMap := getMapfromCertList(l.sslCerts)
198198
for _, cert := range l.oldSSLCerts {
@@ -204,13 +204,11 @@ func (l *L7) deleteOldSSLCerts() (err error) {
204204
// cert found in current map
205205
continue
206206
}
207-
glog.Infof("Cleaning up old SSL Certificate %s", cert.Name)
207+
glog.V(3).Infof("Cleaning up old SSL Certificate %s", cert.Name)
208208
if certErr := utils.IgnoreHTTPNotFound(l.cloud.DeleteSslCertificate(cert.Name)); certErr != nil {
209209
glog.Errorf("Old cert delete failed - %v", certErr)
210-
err = certErr
211210
}
212211
}
213-
return err
214212
}
215213

216214
// Returns the name portion of a link - which is the last section
@@ -317,47 +315,55 @@ func (l *L7) checkSSLCert() error {
317315
return err
318316
}
319317

320-
var newCerts []*compute.SslCertificate
318+
existingCertsMap := getMapfromCertList(l.sslCerts)
319+
l.oldSSLCerts = l.sslCerts
320+
l.sslCerts = make([]*compute.SslCertificate, 0)
321+
321322
// mapping of currently configured certs
322-
certsMap := getMapfromCertList(l.sslCerts)
323+
visitedCertMap := make(map[string]string)
323324
var failedCerts []string
324325

325326
for _, tlsCert := range l.runtimeInfo.TLS {
326327
ingCert := tlsCert.Cert
327328
ingKey := tlsCert.Key
328-
newCertName := l.namer.SSLCertName(l.Name, tlsCert.CertHash)
329+
gcpCertName := l.namer.SSLCertName(l.Name, tlsCert.CertHash)
330+
331+
if addedBy, exists := visitedCertMap[gcpCertName]; exists {
332+
glog.V(3).Infof("Secret %q has a certificate already used by %v", tlsCert.Name, addedBy)
333+
continue
334+
}
329335

330336
// PrivateKey is write only, so compare certs alone. We're assuming that
331337
// no one will change just the key. We can remember the key and compare,
332338
// but a bug could end up leaking it, which feels worse.
333339
// If the cert contents have changed, its hash would be different, so would be the cert name. So it is enough
334340
// to check if this cert name exists in the map.
335-
if certsMap != nil {
336-
if cert, ok := certsMap[newCertName]; ok {
337-
glog.Infof("Retaining cert - %s", tlsCert.Name)
338-
newCerts = append(newCerts, cert)
341+
if existingCertsMap != nil {
342+
if cert, ok := existingCertsMap[gcpCertName]; ok {
343+
glog.V(3).Infof("Secret %q already exists as certificate %q", tlsCert.Name, gcpCertName)
344+
visitedCertMap[gcpCertName] = fmt.Sprintf("certificate:%q", gcpCertName)
345+
l.sslCerts = append(l.sslCerts, cert)
339346
continue
340347
}
341348
}
342349
// Controller needs to create the certificate, no need to check if it exists and delete. If it did exist, it
343350
// would have been listed in the populateSSLCert function and matched in the check above.
344-
glog.V(2).Infof("Creating new sslCertificate %v for %v", newCertName, l.Name)
351+
glog.V(2).Infof("Creating new sslCertificate %q for LB %q", gcpCertName, l.Name)
345352
cert, err := l.cloud.CreateSslCertificate(&compute.SslCertificate{
346-
Name: newCertName,
353+
Name: gcpCertName,
347354
Certificate: ingCert,
348355
PrivateKey: ingKey,
349356
})
350357
if err != nil {
351-
glog.Errorf("Failed to create new sslCertificate %v for %v - %s", newCertName, l.Name, err)
352-
failedCerts = append(failedCerts, newCertName+" Error:"+err.Error())
358+
glog.Errorf("Failed to create new sslCertificate %q for %q - %v", gcpCertName, l.Name, err)
359+
failedCerts = append(failedCerts, gcpCertName+" Error:"+err.Error())
353360
continue
354361
}
355-
newCerts = append(newCerts, cert)
362+
visitedCertMap[gcpCertName] = fmt.Sprintf("secret:%q", tlsCert.Name)
363+
l.sslCerts = append(l.sslCerts, cert)
356364
}
357365

358366
// Save the old certs for cleanup after we update the target proxy.
359-
l.oldSSLCerts = l.sslCerts
360-
l.sslCerts = newCerts
361367
if len(failedCerts) > 0 {
362368
return fmt.Errorf("Cert creation failures - %s", strings.Join(failedCerts, ","))
363369
}
@@ -627,19 +633,15 @@ func (l *L7) edgeHopHttp() error {
627633
}
628634

629635
func (l *L7) edgeHopHttps() error {
636+
defer l.deleteOldSSLCerts()
630637
if err := l.checkSSLCert(); err != nil {
631638
return err
632639
}
640+
633641
if err := l.checkHttpsProxy(); err != nil {
634642
return err
635643
}
636-
if err := l.checkHttpsForwardingRule(); err != nil {
637-
return err
638-
}
639-
if err := l.deleteOldSSLCerts(); err != nil {
640-
return err
641-
}
642-
return nil
644+
return l.checkHttpsForwardingRule()
643645
}
644646

645647
// GetIP returns the ip associated with the forwarding rule for this l7.

pkg/loadbalancers/loadbalancers_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,31 @@ func TestCertUpdate(t *testing.T) {
160160
verifyCertAndProxyLink(expectCerts, expectCerts, f, t)
161161
}
162162

163+
// Test that multiple secrets with the same certificate value don't cause a sync error.
164+
func TestMultipleSecretsWithSameCert(t *testing.T) {
165+
namer := utils.NewNamer("uid1", "fw1")
166+
lbName := namer.LoadBalancer("test")
167+
168+
lbInfo := &L7RuntimeInfo{
169+
Name: lbName,
170+
AllowHTTP: false,
171+
TLS: []*TLSCerts{
172+
createCert("key", "cert", "secret-a"),
173+
createCert("key", "cert", "secret-b"),
174+
},
175+
}
176+
f := NewFakeLoadBalancers(lbInfo.Name, namer)
177+
pool := newFakeLoadBalancerPool(f, t, namer)
178+
179+
// Sync first cert
180+
if err := pool.Sync([]*L7RuntimeInfo{lbInfo}); err != nil {
181+
t.Fatalf("pool.Sync() = err %v", err)
182+
}
183+
certName := namer.SSLCertName(lbName, GetCertHash("cert"))
184+
expectCerts := map[string]string{certName: lbInfo.TLS[0].Cert}
185+
verifyCertAndProxyLink(expectCerts, expectCerts, f, t)
186+
}
187+
163188
// Tests that controller can overwrite existing, unused certificates
164189
func TestCertCreationWithCollision(t *testing.T) {
165190
namer := utils.NewNamer("uid1", "fw1")
@@ -458,7 +483,7 @@ func TestPreSharedToSecretBasedCertUpdate(t *testing.T) {
458483

459484
func verifyProxyCertsInOrder(hostname string, f *FakeLoadBalancers, t *testing.T) {
460485
t.Helper()
461-
t.Logf("f =\n%s", f)
486+
t.Logf("f =\n%s", f.String())
462487

463488
tps, err := f.GetTargetHttpsProxy(f.TPName(true))
464489
if err != nil {
@@ -488,11 +513,10 @@ func verifyProxyCertsInOrder(hostname string, f *FakeLoadBalancers, t *testing.T
488513
// expectCerts is the mapping of certs expected in the FakeLoadBalancer. expectCertsProxy is the mapping of certs expected
489514
// to be in use by the target proxy. Both values will be different for the PreSharedToSecretBasedCertUpdate test.
490515
// f will contain the preshared as well as secret-based certs, but target proxy will contain only one or the other.
491-
func verifyCertAndProxyLink(expectCerts map[string]string, expectCertsProxy map[string]string, f *FakeLoadBalancers,
492-
t *testing.T) {
516+
func verifyCertAndProxyLink(expectCerts map[string]string, expectCertsProxy map[string]string, f *FakeLoadBalancers, t *testing.T) {
493517
t.Helper()
494518

495-
t.Logf("f =\n%s", f)
519+
t.Logf("f =\n%s", f.String())
496520

497521
// f needs to contain only the certs in expectCerts, nothing more, nothing less
498522
allCerts, err := f.ListSslCertificates()

0 commit comments

Comments
 (0)