diff --git a/internal/ingress/controller/store/backend_ssl.go b/internal/ingress/controller/store/backend_ssl.go index 81b508cd2..14db52aa4 100644 --- a/internal/ingress/controller/store/backend_ssl.go +++ b/internal/ingress/controller/store/backend_ssl.go @@ -36,12 +36,29 @@ import ( // syncSecret synchronizes the content of a TLS Secret (certificate(s), secret // key) with the filesystem. The resulting files can be used by NGINX. func (s *k8sStore) syncSecret(key string) { + s.syncResource(key, "Secret", s.getPemCertificate) +} + +// syncSecret synchronizes the content of a client TLS Secret (certificate, secret +// key) with the filesystem. The resulting files can be used by NGINX. +func (s *k8sStore) syncClientCertSecret(key string) { + s.syncResource(key, "Secret", s.getClientPemCertificate) +} + +// syncCAConfigMap synchronizes the content of a ConfigMap including CAs +// with the filesystem. The resulting file can be used by NGINX. +func (s *k8sStore) syncCAConfigMap(key string) { + s.syncResource(key, "ConfigMap", s.getCAPEMCertificate) +} + +// syncResource synchronizes the content of a K8s resource (Secret, ConfigMap...) +func (s *k8sStore) syncResource(key, resourceKind string, getPermCert func(string) (*ingress.SSLCert, error)) { s.syncSecretMu.Lock() defer s.syncSecretMu.Unlock() - klog.V(3).InfoS("Syncing Secret", "name", key) + klog.V(3).InfoS(fmt.Sprintf("Syncing %s", resourceKind), "name", key) - cert, err := s.getPemCertificate(key) + cert, err := getPermCert(key) if err != nil { if !isErrSecretForAuth(err) { klog.Warningf("Error obtaining X.509 certificate: %v", err) @@ -56,7 +73,7 @@ func (s *k8sStore) syncSecret(key string) { // no need to update return } - klog.InfoS("Updating secret in local store", "name", key) + klog.InfoS("Updating cert in local store", "name", key) s.sslStore.Update(key, cert) // this update must trigger an update // (like an update event from a change in Ingress) @@ -64,7 +81,7 @@ func (s *k8sStore) syncSecret(key string) { return } - klog.InfoS("Adding secret to local store", "name", key) + klog.InfoS("Adding cert to local store", "name", key) s.sslStore.Add(key, cert) // this update must trigger an update // (like an update event from a change in Ingress) @@ -146,27 +163,10 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error klog.V(3).InfoS(msg) case len(ca) > 0: - sslCert, err = ssl.CreateCACert(ca) + sslCert, err = createCASSLCert(secretName, "Secret", string(secret.UID), ca, crl) if err != nil { - return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err) + return nil, err } - - err = ssl.ConfigureCACert(nsSecName, ca, sslCert) - if err != nil { - return nil, fmt.Errorf("error configuring CA certificate: %v", err) - } - - sslCert.CASHA = file.SHA1(sslCert.CAFileName) - - if len(crl) > 0 { - err = ssl.ConfigureCRL(nsSecName, crl, sslCert) - if err != nil { - return nil, err - } - } - // makes this secret in 'syncSecret' to be used for Certificate Authentication - // this does not enable Certificate Authentication - klog.V(3).InfoS("Configuring Secret for TLS authentication", "secret", secretName) default: if auth != nil { return nil, ErrSecretForAuth @@ -191,6 +191,88 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error return sslCert, nil } +// getClientPemCertificate receives a secret, and creates an ingress.SSLCert as return. +// It parses the secret and verifies they keypair. +func (s *k8sStore) getClientPemCertificate(secretName string) (*ingress.SSLCert, error) { + secret, err := s.listers.Secret.ByKey(secretName) + if err != nil { + return nil, err + } + + cert, okcert := secret.Data[apiv1.TLSCertKey] + if !okcert || cert == nil { + return nil, fmt.Errorf("key 'tls.crt' missing from Secret %q", secretName) + } + + key, okkey := secret.Data[apiv1.TLSPrivateKeyKey] + if !okkey || key == nil { + return nil, fmt.Errorf("key 'tls.key' missing from Secret %q", secretName) + } + + // namespace/secretName -> namespace-secretName + nsSecName := strings.ReplaceAll(secretName, "/", "-") + + sslCert, err := ssl.CreateSSLCert(cert, key, string(secret.UID)) + if err != nil { + return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err) + } + + path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert) + if err != nil { + return nil, fmt.Errorf("error while storing certificate and key: %v", err) + } + sslCert.PemFileName = path + + klog.V(3).InfoS(fmt.Sprintf("Configuring Secret %q for TLS encryption (CN: %v)", secretName, sslCert.CN)) + + sslCert.Name = secret.Name + sslCert.Namespace = secret.Namespace + + return sslCert, nil +} + +// getCAPEMCertificate receives a configMap, and creates an ingress.SSLCert as return. +// It parses the configMap +func (s *k8sStore) getCAPEMCertificate(configMapName string) (*ingress.SSLCert, error) { + configmap, err := s.listers.ConfigMap.ByKey(configMapName) + if err != nil { + return nil, err + } + + ca := configmap.Data["ca.crt"] + crl := configmap.Data["ca.crl"] + + return createCASSLCert(configMapName, "ConfigMap", string(configmap.UID), []byte(ca), []byte(crl)) +} + +func createCASSLCert(resourceName, resourceKind, resourceUID string, ca, crl []byte) (*ingress.SSLCert, error) { + nsName := strings.ReplaceAll(resourceName, "/", "-") + + sslCert, err := ssl.CreateCACert(ca) + if err != nil { + return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err) + } + + sslCert.UID = resourceUID + + err = ssl.ConfigureCACert(nsName, ca, sslCert) + if err != nil { + return nil, fmt.Errorf("error configuring CA certificate: %v", err) + } + + sslCert.CASHA = file.SHA1(sslCert.CAFileName) + + if len(crl) > 0 { + err = ssl.ConfigureCRL(nsName, crl, sslCert) + if err != nil { + return nil, err + } + } + klog.V(3).InfoS("Configuring certs for TLS authentication", resourceKind, resourceName) + + return sslCert, nil +} + // sendDummyEvent sends a dummy event to trigger an update // This is used in when a secret change func (s *k8sStore) sendDummyEvent() {