diff --git a/internal/service/sync/tls.go b/internal/service/sync/tls.go index 146e8c4..ee388d3 100644 --- a/internal/service/sync/tls.go +++ b/internal/service/sync/tls.go @@ -9,23 +9,30 @@ import ( networkv1 "k8s.io/api/networking/v1" ) +const ( + TLS_ANNOTATION_PREFIX = "servers.com/certificate-" +) + // SyncTLS syncs ingress tls certs stored in secrets to portal. +// If secret name starts with certManagerPrefix- we looking for cert from API +// Due to secret name don't support upperCase for such cases we additionally checks annotations +// with TLS_ANNOTATION_PREFIX which overrides ingress tls certs for matching hosts. // Returns map of hosts to portal cert id func (s *SyncManager) SyncTLS(ingress *networkv1.Ingress, certManagerPrefix string) (map[string]string, error) { var sslCerts = make(map[string]string) - for _, tls := range ingress.Spec.TLS { - if strings.HasPrefix(tls.SecretName, certManagerPrefix) { - id := strings.TrimPrefix(tls.SecretName, certManagerPrefix) + + hostsSecrets := mergeTLSWithAnnotations(ingress) + for host, secretName := range hostsSecrets { + if strings.HasPrefix(secretName, certManagerPrefix) { + id := strings.TrimPrefix(secretName, certManagerPrefix) certificate, err := s.tlsMgr.GetByID(id) if err != nil { return nil, fmt.Errorf("fetching cert with id %q from API failed: %v", id, err) } - for _, host := range tls.Hosts { - sslCerts[host] = certificate.ID - } + sslCerts[host] = certificate.ID continue } - sKey := ingress.Namespace + "/" + tls.SecretName + sKey := ingress.Namespace + "/" + secretName secret, err := s.store.GetSecret(sKey) if err != nil { return nil, fmt.Errorf("fetching secret with key %q from store failed: %v", sKey, err) @@ -47,28 +54,22 @@ func (s *SyncManager) SyncTLS(ingress *networkv1.Ingress, certManagerPrefix stri primary, chain := tlsmanager.SplitCerts(cert) fingerprint := tlsmanager.GetPemFingerprint(primary) - if fingerprint == "" { return nil, fmt.Errorf("can't calculate 'tls.crt' fingerprint for %s", string(cert)) } if s.tlsMgr.HasRegistration(fingerprint) { certificate, err := s.tlsMgr.Get(fingerprint) - if err != nil { return nil, err } - - for _, host := range tls.Hosts { - sslCerts[host] = certificate.ID - } - + sslCerts[host] = certificate.ID continue } certificate, err := s.tlsMgr.SyncCertificate( fingerprint, - tls.SecretName, + secretName, primary, tlsmanager.StripSpaces(key), chain, @@ -78,10 +79,31 @@ func (s *SyncManager) SyncTLS(ingress *networkv1.Ingress, certManagerPrefix stri return nil, err } + sslCerts[host] = certificate.ID + } + return sslCerts, nil +} + +// mergeTLSWithAnnotations merge info about host and associated secret from ingress.Spec.TLS and ingress.Annotations +// returns map[host]secret +func mergeTLSWithAnnotations(ingress *networkv1.Ingress) map[string]string { + res := make(map[string]string) + + for _, tls := range ingress.Spec.TLS { + sName := tls.SecretName for _, host := range tls.Hosts { - sslCerts[host] = certificate.ID + res[host] = sName } + } + // annotations overrides settings from tls + for k, v := range ingress.Annotations { + if strings.HasPrefix(k, TLS_ANNOTATION_PREFIX) { + if host, ok := strings.CutPrefix(k, TLS_ANNOTATION_PREFIX); ok { + res[host] = v + } + } } - return sslCerts, nil + + return res } diff --git a/internal/service/sync/tls_test.go b/internal/service/sync/tls_test.go index c2f49a4..732a847 100644 --- a/internal/service/sync/tls_test.go +++ b/internal/service/sync/tls_test.go @@ -14,6 +14,7 @@ import ( "go.uber.org/mock/gomock" v1 "k8s.io/api/core/v1" networkv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var ( @@ -38,6 +39,11 @@ func TestSyncTLS(t *testing.T) { }, }, }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + TLS_ANNOTATION_PREFIX + "example1.com": "test-secret", + }, + }, } ingress.Namespace = "default" @@ -51,9 +57,9 @@ func TestSyncTLS(t *testing.T) { t.Run("Successfully syncing TLS", func(t *testing.T) { g := NewWithT(t) - storeHandler.EXPECT().GetSecret("default/test-secret").Return(secret, nil) + storeHandler.EXPECT().GetSecret("default/test-secret").Return(secret, nil).Times(2) - tlsManagerHandler.EXPECT().HasRegistration(testdata.ValidPEMFingerprint).Return(false) + tlsManagerHandler.EXPECT().HasRegistration(testdata.ValidPEMFingerprint).Return(false).Times(2) expectedCert := &client.SSLCertificate{ID: "cert-id"} tlsManagerHandler.EXPECT().SyncCertificate( @@ -62,11 +68,12 @@ func TestSyncTLS(t *testing.T) { gomock.Any(), gomock.Any(), gomock.Any()). - Return(expectedCert, nil) + Return(expectedCert, nil).Times(2) result, err := syncManager.SyncTLS(ingress, scCertManagerPrefix) g.Expect(err).To(BeNil()) g.Expect(result).To(HaveKeyWithValue("example.com", "cert-id")) + g.Expect(result).To(HaveKeyWithValue("example1.com", "cert-id")) }) t.Run("Error fetching secret", func(t *testing.T) { @@ -131,12 +138,14 @@ func TestSyncTLS(t *testing.T) { g := NewWithT(t) ingress.Spec.TLS[0].SecretName = scCertManagerPrefix + "someid" + ingress.Annotations[TLS_ANNOTATION_PREFIX+"example1.com"] = scCertManagerPrefix + "someid" tlsManagerHandler.EXPECT(). GetByID("someid"). - Return(&serverscom.SSLCertificate{ID: "someid"}, nil) + Return(&serverscom.SSLCertificate{ID: "someid"}, nil).Times(2) result, err := syncManager.SyncTLS(ingress, scCertManagerPrefix) g.Expect(err).To(BeNil()) g.Expect(result).To(HaveKeyWithValue("example.com", "someid")) + g.Expect(result).To(HaveKeyWithValue("example1.com", "someid")) }) }