From 1ffeb2cee1f431a22f417a9882ff13243d6f432d Mon Sep 17 00:00:00 2001 From: Fernando Diaz Date: Tue, 26 Sep 2017 22:46:22 -0500 Subject: [PATCH] Enhance Certificate Logging and Clearup Mutual Auth Docs Adds better logging to errors caused when getting a Certificate. Adds notes and updates documentation for Mutual Authentication. --- controllers/nginx/configuration.md | 4 +-- core/pkg/ingress/controller/backend_ssl.go | 34 +++++++++++++------ core/pkg/ingress/controller/controller.go | 13 +++++-- core/pkg/ingress/resolver/main.go | 2 +- core/pkg/net/ssl/ssl.go | 2 +- examples/PREREQUISITES.md | 7 +++- examples/auth/client-certs/nginx/README.md | 13 +++---- .../client-certs/nginx/nginx-tls-auth.yaml | 3 +- 8 files changed, 49 insertions(+), 29 deletions(-) diff --git a/controllers/nginx/configuration.md b/controllers/nginx/configuration.md index 17299ba9d..d94d31c16 100644 --- a/controllers/nginx/configuration.md +++ b/controllers/nginx/configuration.md @@ -135,14 +135,14 @@ Please check the [auth](/examples/auth/basic/nginx/README.md) example. ### Certificate Authentication -It's possible to enable Certificate based authentication using additional annotations in Ingress Rule. +It's possible to enable Certificate-Based Authentication (Mutual Authentication) using additional annotations in Ingress Rule. The annotations are: ``` ingress.kubernetes.io/auth-tls-secret: secretName ``` -The name of the secret that contains the full Certificate Authority chain that is enabled to authenticate against this ingress. It's composed of namespace/secretName +The name of the secret that contains the full Certificate Authority chain `ca.crt` that is enabled to authenticate against this ingress. It's composed of namespace/secretName. ``` ingress.kubernetes.io/auth-tls-verify-depth diff --git a/core/pkg/ingress/controller/backend_ssl.go b/core/pkg/ingress/controller/backend_ssl.go index 98005f340..5791bcfa1 100644 --- a/core/pkg/ingress/controller/backend_ssl.go +++ b/core/pkg/ingress/controller/backend_ssl.go @@ -74,35 +74,47 @@ func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLC cert, okcert := secret.Data[apiv1.TLSCertKey] key, okkey := secret.Data[apiv1.TLSPrivateKeyKey] - ca := secret.Data["ca.crt"] + // namespace/secretName -> namespace-secretName nsSecName := strings.Replace(secretName, "/", "-", -1) var s *ingress.SSLCert if okcert && okkey { - if cert == nil || key == nil { - return nil, fmt.Errorf("error retrieving cert or key from secret %v: %v", secretName, err) + if cert == nil { + return nil, fmt.Errorf("secret %v has no 'tls.crt'", secretName) } + if key == nil { + return nil, fmt.Errorf("secret %v has no 'tls.key'", secretName) + } + + // If 'ca.crt' is also present, it will allow this secret to be used in the + // 'ingress.kubernetes.io/auth-tls-secret' annotation s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca) if err != nil { - return nil, fmt.Errorf("unexpected error creating pem file %v", err) + return nil, fmt.Errorf("unexpected error creating pem file: %v", err) } - glog.V(3).Infof("found certificate and private key, configuring %v as a TLS Secret (CN: %v)", secretName, s.CN) + + glog.V(3).Infof("found 'tls.crt' and 'tls.key', configuring %v as a TLS Secret (CN: %v)", secretName, s.CN) + if ca != nil { + glog.V(3).Infof("found 'ca.crt', secret %v can also be used for Certificate Authentication", secretName) + } + } else if ca != nil { - glog.V(3).Infof("found only ca.crt, configuring %v as an Certificate Authentication secret", secretName) s, err = ssl.AddCertAuth(nsSecName, ca) + if err != nil { - return nil, fmt.Errorf("unexpected error creating pem file %v", err) + return nil, fmt.Errorf("unexpected error creating pem file: %v", err) } + + // makes this secret in 'syncSecret' to be used for Certificate Authentication + // this does not enable Certificate Authentication + glog.V(3).Infof("found only 'ca.crt', configuring %v as an Certificate Authentication Secret", secretName) + } else { return nil, fmt.Errorf("no keypair or CA cert could be found in %v", secretName) } - if err != nil { - return nil, err - } - s.Name = secret.Name s.Namespace = secret.Namespace return s, nil diff --git a/core/pkg/ingress/controller/controller.go b/core/pkg/ingress/controller/controller.go index f11192785..f6f713540 100644 --- a/core/pkg/ingress/controller/controller.go +++ b/core/pkg/ingress/controller/controller.go @@ -509,9 +509,13 @@ func (ic *GenericController) getBackendServers(ingresses []*extensions.Ingress) ca := ic.annotations.CertificateAuth(ing) if ca != nil { server.CertificateAuth = *ca + // It is possible that no CAFileName is found in the secret + if server.CertificateAuth.CAFileName == "" { + glog.V(3).Infof("secret %v does not contain 'ca.crt', mutual authentication not enabled - ingress rule %v/%v.", server.CertificateAuth.Secret, ing.Namespace, ing.Name) + } } } else { - glog.V(3).Infof("server %v already contains a muthual autentication configuration - ingress rule %v/%v", server.Hostname, ing.Namespace, ing.Name) + glog.V(3).Infof("server %v already contains a mutual authentication configuration - ingress rule %v/%v", server.Hostname, ing.Namespace, ing.Name) } for _, path := range rule.HTTP.Paths { @@ -671,7 +675,8 @@ func (ic *GenericController) getBackendServers(ingresses []*extensions.Ingress) return aUpstreams, aServers } -// GetAuthCertificate ... + +// GetAuthCertificate is used by the auth-tls annotations to get a cert from a secret func (ic GenericController) GetAuthCertificate(secretName string) (*resolver.AuthSSLCert, error) { if _, exists := ic.sslCertTracker.Get(secretName); !exists { ic.syncSecret(secretName) @@ -894,10 +899,12 @@ func (ic *GenericController) createServers(data []*extensions.Ingress, RequestBuffering: bdef.ProxyRequestBuffering, } + // generated on Start() with createDefaultSSLCertificate() defaultPemFileName := fakeCertificatePath defaultPemSHA := fakeCertificateSHA - // Tries to fetch the default Certificate. If it does not exists, generate a new self signed one. + // Tries to fetch the default Certificate from nginx configuration. + // If it does not exists, use the ones generated on Start() defaultCertificate, err := ic.getPemCertificate(ic.cfg.DefaultSSLCertificate) if err == nil { defaultPemFileName = defaultCertificate.PemFileName diff --git a/core/pkg/ingress/resolver/main.go b/core/pkg/ingress/resolver/main.go index 9bb11db40..d4672aa76 100644 --- a/core/pkg/ingress/resolver/main.go +++ b/core/pkg/ingress/resolver/main.go @@ -54,7 +54,7 @@ type AuthSSLCert struct { Secret string `json:"secret"` // CAFileName contains the path to the secrets 'ca.crt' CAFileName string `json:"caFilename"` - // PemSHA contains the SHA1 hash of the 'tls.crt' value + // PemSHA contains the SHA1 hash of the 'ca.crt' or combinations of (tls.crt, tls.key, tls.crt) depending on certs in secret PemSHA string `json:"pemSha"` } diff --git a/core/pkg/net/ssl/ssl.go b/core/pkg/net/ssl/ssl.go index e1cd5b0ff..535c5f5fe 100644 --- a/core/pkg/net/ssl/ssl.go +++ b/core/pkg/net/ssl/ssl.go @@ -271,7 +271,7 @@ func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) { return nil, fmt.Errorf("could not write CA file %v: %v", caFileName, err) } - glog.V(3).Infof("Created CA Certificate for authentication: %v", caFileName) + glog.V(3).Infof("Created CA Certificate for Authentication: %v", caFileName) return &ingress.SSLCert{ CAFileName: caFileName, PemFileName: caFileName, diff --git a/examples/PREREQUISITES.md b/examples/PREREQUISITES.md index 03f9f74a4..7c6bf2fd6 100644 --- a/examples/PREREQUISITES.md +++ b/examples/PREREQUISITES.md @@ -132,7 +132,12 @@ The final step is to create a secret with the content of this file. This secret the TLS Auth directive: ```console -$ kubectl create secret generic caingress --namespace=default --from-file=ca.crt +$ kubectl create secret generic caingress --namespace=default --from-file=ca.crt= +``` + +Note: You can also generate the CA Authentication Secret along with the TLS Secret by using: +```console +$ kubectl create secret generic caingress --namespace=default --from-file=ca.crt= --from-file=tls.crt= --from-file=tls.key= ``` ## Test HTTP Service diff --git a/examples/auth/client-certs/nginx/README.md b/examples/auth/client-certs/nginx/README.md index e8c9e83ac..d3da9d1a6 100644 --- a/examples/auth/client-certs/nginx/README.md +++ b/examples/auth/client-certs/nginx/README.md @@ -16,13 +16,12 @@ the child, except for the root, which has Issuer == Subject. * Client Cert: Certificate used by the clients to authenticate themselves with the loadbalancer/backends. - ## Prerequisites You need a valid CA File, composed of a group of valid enabled CAs. This MUST be in PEM Format. -The instructions are described [here](../../../PREREQUISITES.md#ca-authentication) +The instructions are described [here](../../../PREREQUISITES.md) -Also your ingress must be configured as a HTTPs/TLS Ingress. +Also your ingress must be configured as a HTTPS/TLS Ingress. ## Deployment @@ -51,8 +50,7 @@ Name: nginx-test Namespace: default Address: 104.198.183.6 Default backend: default-http-backend:80 (10.180.0.4:8080,10.240.0.2:8080) -TLS: - tls-secret terminates ingress.test.com +TLS: tls-secret terminates ingress.test.com Rules: Host Path Backends ---- ---- -------- @@ -79,13 +77,12 @@ Server: nginx/1.11.9 $ curl -I -k --key ~/user.key --cert ~/user.cer https://ingress.test.com HTTP/1.1 200 OK Server: nginx/1.11.9 - ``` You must use the full DNS name while testing, as NGINX relies on the Server Name (SNI) to select the correct Ingress to be used. - The curl version used here was ``curl 7.47.0`` ## Which certificate was used for authentication? -In your backend application you might want to know which certificate was used for authentication. For this purpose, we pass the full certificate in PEM format to the backend in the `ssl-client-cert` header. +In your backend application you might want to know which certificate was used for authentication. +For this purpose, we pass the full certificate in PEM format to the backend in the `ssl-client-cert` header. diff --git a/examples/auth/client-certs/nginx/nginx-tls-auth.yaml b/examples/auth/client-certs/nginx/nginx-tls-auth.yaml index ac03d9d7c..c3ef92a8e 100644 --- a/examples/auth/client-certs/nginx/nginx-tls-auth.yaml +++ b/examples/auth/client-certs/nginx/nginx-tls-auth.yaml @@ -21,6 +21,5 @@ spec: tls: - hosts: - ingress.test.com - # Create this cert as described in 'multi-tls' example - secretName: cert + secretName: tls-secret