From a342c0bce378bd7089601f2eaa3c3d84ca0f0d27 Mon Sep 17 00:00:00 2001 From: Ricardo Pchevuzinske Katz Date: Mon, 6 Feb 2017 16:16:36 -0200 Subject: [PATCH] Adds correct support for TLS Muthual autentication and depth verification modified: controllers/nginx/configuration.md modified: controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl modified: core/pkg/ingress/annotations/authtls/main.go modified: core/pkg/ingress/controller/backend_ssl.go modified: core/pkg/ingress/controller/controller.go modified: core/pkg/ingress/controller/util_test.go modified: core/pkg/ingress/resolver/main.go modified: core/pkg/ingress/types.go modified: core/pkg/net/ssl/ssl.go modified: examples/PREREQUISITES.md new file: examples/auth/client-certs/nginx/README.md new file: examples/auth/client-certs/nginx/nginx-tls-auth.yaml --- controllers/nginx/configuration.md | 23 +++++ .../rootfs/etc/nginx/template/nginx.tmpl | 12 ++- core/pkg/ingress/annotations/authtls/main.go | 46 ++++++--- core/pkg/ingress/controller/backend_ssl.go | 25 +++-- core/pkg/ingress/controller/controller.go | 15 ++- core/pkg/ingress/controller/util_test.go | 4 +- core/pkg/ingress/resolver/main.go | 6 -- core/pkg/ingress/types.go | 4 +- core/pkg/net/ssl/ssl.go | 56 ++++++++--- examples/PREREQUISITES.md | 99 +++++++++++++++++++ examples/auth/client-certs/nginx/README.md | 86 ++++++++++++++++ .../client-certs/nginx/nginx-tls-auth.yaml | 25 +++++ 12 files changed, 349 insertions(+), 52 deletions(-) create mode 100644 examples/auth/client-certs/nginx/README.md create mode 100644 examples/auth/client-certs/nginx/nginx-tls-auth.yaml diff --git a/controllers/nginx/configuration.md b/controllers/nginx/configuration.md index f37ec71e7..5885f587b 100644 --- a/controllers/nginx/configuration.md +++ b/controllers/nginx/configuration.md @@ -44,6 +44,8 @@ The following annotations are supported: |[ingress.kubernetes.io/auth-secret](#authentication)|string| |[ingress.kubernetes.io/auth-type](#authentication)|basic or digest| |[ingress.kubernetes.io/auth-url](#external-authentication)|string| +|[ingress.kubernetes.io/auth-tls-secret](#Certificate Authentication)|string| +|[ingress.kubernetes.io/auth-tls-verify-depth](#Certificate Authentication)|number| |[ingress.kubernetes.io/enable-cors](#enable-cors)|true or false| |[ingress.kubernetes.io/limit-connections](#rate-limiting)|number| |[ingress.kubernetes.io/limit-rps](#rate-limiting)|number| @@ -126,6 +128,27 @@ ingress.kubernetes.io/auth-realm: "realm string" Please check the [auth](examples/auth/README.md) example. +### Certificate Authentication + +It's possible to enable Certificate based authentication using additional annotations in Ingres 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 + +``` +ingress.kubernetes.io/auth-tls-verify-depth +``` + +The validation depth between the provided client certificate and the Certification Authority chain. + +Please check the [tls-auth](examples/auth/client-certs/README.md) example. + + ### Enable CORS To enable Cross-Origin Resource Sharing (CORS) in an Ingress rule add the annotation `ingress.kubernetes.io/enable-cors: "true"`. This will add a section in the server location enabling this functionality. diff --git a/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl b/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl index 11440b195..07b6f5782 100644 --- a/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl +++ b/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl @@ -225,10 +225,11 @@ http { {{ $path := buildLocation $location }} {{ $authPath := buildAuthLocation $location }} - {{ if not (empty $location.CertificateAuth.CertFileName) }} - # PEM sha: {{ $location.CertificateAuth.PemSHA }} - ssl_client_certificate {{ $location.CertificateAuth.CAFileName }}; + {{ if not (empty $location.CertificateAuth.AuthSSLCert.CAFileName) }} + # PEM sha: {{ $location.CertificateAuth.AuthSSLCert.PemSHA }} + ssl_client_certificate {{ $location.CertificateAuth.AuthSSLCert.CAFileName }}; ssl_verify_client on; + ssl_verify_depth {{ $location.CertificateAuth.ValidationDepth }}; {{ end }} {{ if not (empty $authPath) }} @@ -295,6 +296,11 @@ http { proxy_set_header Host $host; + # Pass the extracted client certificate to the backend + {{ if not (empty $location.CertificateAuth.AuthSSLCert.CAFileName) }} + proxy_set_header ssl-client-cert $ssl_client_cert; + {{ end }} + # Pass Real IP proxy_set_header X-Real-IP $remote_addr; diff --git a/core/pkg/ingress/annotations/authtls/main.go b/core/pkg/ingress/annotations/authtls/main.go index 143353249..c4172e51c 100644 --- a/core/pkg/ingress/annotations/authtls/main.go +++ b/core/pkg/ingress/annotations/authtls/main.go @@ -28,11 +28,16 @@ import ( const ( // name of the secret - authTLSSecret = "ingress.kubernetes.io/auth-tls-secret" + annotationAuthTLSSecret = "ingress.kubernetes.io/auth-tls-secret" + annotationAuthTLSDepth = "ingress.kubernetes.io/auth-tls-verify-depth" + defaultAuthTLSDepth = 1 ) -type authTLS struct { - certResolver resolver.AuthCertificate +// AuthSSLConfig contains the AuthSSLCert used for muthual autentication +// and the configured ValidationDepth +type AuthSSLConfig struct { + AuthSSLCert resolver.AuthSSLCert + ValidationDepth int `json:"validationDepth"` } // NewParser creates a new TLS authentication annotation parser @@ -40,29 +45,42 @@ func NewParser(resolver resolver.AuthCertificate) parser.IngressAnnotation { return authTLS{resolver} } -// ParseAnnotations parses the annotations contained in the ingress -// rule used to use an external URL as source for authentication +type authTLS struct { + certResolver resolver.AuthCertificate +} + +// Parse parses the annotations contained in the ingress +// rule used to use a Certificate as authentication method func (a authTLS) Parse(ing *extensions.Ingress) (interface{}, error) { - str, err := parser.GetStringAnnotation(authTLSSecret, ing) + + tlsauthsecret, err := parser.GetStringAnnotation(annotationAuthTLSSecret, ing) if err != nil { - return nil, err + return &AuthSSLConfig{}, err } - if str == "" { - return nil, ing_errors.NewLocationDenied("an empty string is not a valid secret name") + if tlsauthsecret == "" { + return &AuthSSLConfig{}, ing_errors.NewLocationDenied("an empty string is not a valid secret name") } - _, _, err = k8s.ParseNameNS(str) + _, _, err = k8s.ParseNameNS(tlsauthsecret) if err != nil { - return nil, ing_errors.NewLocationDenied("an empty string is not a valid secret name") + return &AuthSSLConfig{}, ing_errors.NewLocationDenied("an empty string is not a valid secret name") } - authCert, err := a.certResolver.GetAuthCertificate(str) + tlsdepth, err := parser.GetIntAnnotation(annotationAuthTLSDepth, ing) + if err != nil || tlsdepth == 0 { + tlsdepth = defaultAuthTLSDepth + } + + authCert, err := a.certResolver.GetAuthCertificate(tlsauthsecret) if err != nil { - return nil, ing_errors.LocationDenied{ + return &AuthSSLConfig{}, ing_errors.LocationDenied{ Reason: errors.Wrap(err, "error obtaining certificate"), } } - return authCert, nil + return &AuthSSLConfig{ + AuthSSLCert: *authCert, + ValidationDepth: tlsdepth, + }, nil } diff --git a/core/pkg/ingress/controller/backend_ssl.go b/core/pkg/ingress/controller/backend_ssl.go index 92c32fc8c..7a84a3d4f 100644 --- a/core/pkg/ingress/controller/backend_ssl.go +++ b/core/pkg/ingress/controller/backend_ssl.go @@ -98,6 +98,8 @@ func (ic *GenericController) syncSecret(k interface{}) error { return nil } +// getPemCertificate receives a secret, and creates a ingress.SSLCert as return. +// It parses the secret and verifies if it's a keypair, or a 'ca.crt' secret only. func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLCert, error) { secretInterface, exists, err := ic.secrLister.Store.GetByKey(secretName) if err != nil { @@ -108,19 +110,24 @@ func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLC } secret := secretInterface.(*api.Secret) - cert, ok := secret.Data[api.TLSCertKey] - if !ok { - return nil, fmt.Errorf("secret named %v has no private key", secretName) - } - key, ok := secret.Data[api.TLSPrivateKeyKey] - if !ok { - return nil, fmt.Errorf("secret named %v has no cert", secretName) - } + cert, okcert := secret.Data[api.TLSCertKey] + key, okkey := secret.Data[api.TLSPrivateKeyKey] ca := secret.Data["ca.crt"] nsSecName := strings.Replace(secretName, "/", "-", -1) - s, err := ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca) + + var s *ingress.SSLCert + if okcert && okkey { + glog.V(3).Infof("Found certificate and private key, configuring %v as a TLS Secret", secretName) + s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca) + } 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) + } else { + return nil, fmt.Errorf("No keypair or CA cert could be found in %v", secretName) + } + if err != nil { return nil, err } diff --git a/core/pkg/ingress/controller/controller.go b/core/pkg/ingress/controller/controller.go index 58e647f9e..9b7d9512f 100644 --- a/core/pkg/ingress/controller/controller.go +++ b/core/pkg/ingress/controller/controller.go @@ -680,16 +680,23 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress // GetAuthCertificate ... func (ic GenericController) GetAuthCertificate(secretName string) (*resolver.AuthSSLCert, error) { + key, err := ic.GetSecret(secretName) + if err != nil { + return &resolver.AuthSSLCert{}, fmt.Errorf("unexpected error: %v", err) + } + if key != nil { + ic.secretQueue.Enqueue(key) + } + bc, exists := ic.sslCertTracker.Get(secretName) if !exists { return &resolver.AuthSSLCert{}, fmt.Errorf("secret %v does not exists", secretName) } cert := bc.(*ingress.SSLCert) return &resolver.AuthSSLCert{ - Secret: secretName, - CertFileName: cert.PemFileName, - CAFileName: cert.CAFileName, - PemSHA: cert.PemSHA, + Secret: secretName, + CAFileName: cert.CAFileName, + PemSHA: cert.PemSHA, }, nil } diff --git a/core/pkg/ingress/controller/util_test.go b/core/pkg/ingress/controller/util_test.go index 8ec84785a..96d46000d 100644 --- a/core/pkg/ingress/controller/util_test.go +++ b/core/pkg/ingress/controller/util_test.go @@ -24,11 +24,11 @@ import ( "k8s.io/ingress/core/pkg/ingress" "k8s.io/ingress/core/pkg/ingress/annotations/auth" "k8s.io/ingress/core/pkg/ingress/annotations/authreq" + "k8s.io/ingress/core/pkg/ingress/annotations/authtls" "k8s.io/ingress/core/pkg/ingress/annotations/ipwhitelist" "k8s.io/ingress/core/pkg/ingress/annotations/proxy" "k8s.io/ingress/core/pkg/ingress/annotations/ratelimit" "k8s.io/ingress/core/pkg/ingress/annotations/rewrite" - "k8s.io/ingress/core/pkg/ingress/resolver" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" ) @@ -136,7 +136,7 @@ func TestMergeLocationAnnotations(t *testing.T) { "Redirect": rewrite.Redirect{}, "Whitelist": ipwhitelist.SourceRange{}, "Proxy": proxy.Configuration{}, - "CertificateAuth": resolver.AuthSSLCert{}, + "CertificateAuth": authtls.AuthSSLConfig{}, "UsePortInRedirects": true, } diff --git a/core/pkg/ingress/resolver/main.go b/core/pkg/ingress/resolver/main.go index 6017c8cb5..a11b35f58 100644 --- a/core/pkg/ingress/resolver/main.go +++ b/core/pkg/ingress/resolver/main.go @@ -37,8 +37,6 @@ type Secret interface { // AuthCertificate resolves a given secret name into an SSL certificate. // The secret must contain 3 keys named: // ca.crt: contains the certificate chain used for authentication -// tls.crt: (ignored) contains the tls certificate chain, or any other valid base64 data -// tls.key: (ignored) contains the tls secret key, or any other valid base64 data type AuthCertificate interface { GetAuthCertificate(string) (*AuthSSLCert, error) } @@ -48,10 +46,6 @@ type AuthCertificate interface { type AuthSSLCert struct { // Secret contains the name of the secret this was fetched from Secret string `json:"secret"` - // CertFileName contains the filename the secret's 'tls.crt' was saved to - CertFileName string `json:"certFilename"` - // KeyFileName contains the path the secret's 'tls.key' - KeyFileName string `json:"keyFilename"` // CAFileName contains the path to the secrets 'ca.crt' CAFileName string `json:"caFilename"` // PemSHA contains the SHA1 hash of the 'tls.crt' value diff --git a/core/pkg/ingress/types.go b/core/pkg/ingress/types.go index 0d8a8b735..82ddcf0d3 100644 --- a/core/pkg/ingress/types.go +++ b/core/pkg/ingress/types.go @@ -27,12 +27,12 @@ import ( cache_store "k8s.io/ingress/core/pkg/cache" "k8s.io/ingress/core/pkg/ingress/annotations/auth" "k8s.io/ingress/core/pkg/ingress/annotations/authreq" + "k8s.io/ingress/core/pkg/ingress/annotations/authtls" "k8s.io/ingress/core/pkg/ingress/annotations/ipwhitelist" "k8s.io/ingress/core/pkg/ingress/annotations/proxy" "k8s.io/ingress/core/pkg/ingress/annotations/ratelimit" "k8s.io/ingress/core/pkg/ingress/annotations/rewrite" "k8s.io/ingress/core/pkg/ingress/defaults" - "k8s.io/ingress/core/pkg/ingress/resolver" ) var ( @@ -274,7 +274,7 @@ type Location struct { // CertificateAuth indicates the access to this location requires // external authentication // +optional - CertificateAuth resolver.AuthSSLCert `json:"certificateAuth,omitempty"` + CertificateAuth authtls.AuthSSLConfig `json:"certificateAuth,omitempty"` // UsePortInRedirects indicates if redirects must specify the port // +optional UsePortInRedirects bool `json:"use-port-in-redirects"` diff --git a/core/pkg/net/ssl/ssl.go b/core/pkg/net/ssl/ssl.go index 5907d5f5e..62d2f6b7e 100644 --- a/core/pkg/net/ssl/ssl.go +++ b/core/pkg/net/ssl/ssl.go @@ -37,6 +37,8 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, pemFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, pemName) tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName) + + glog.V(3).Infof("Creating temp file %v for Keypair: %v", tempPemFile.Name(), pemName) if err != nil { return nil, fmt.Errorf("could not create temp pem file %v: %v", pemFileName, err) } @@ -64,12 +66,12 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, return nil, err } - pembBock, _ := pem.Decode(pemCerts) - if pembBock == nil { + pemBlock, _ := pem.Decode(pemCerts) + if pemBlock == nil { return nil, fmt.Errorf("No valid PEM formatted block found") } - pemCert, err := x509.ParseCertificate(pembBock.Bytes) + pemCert, err := x509.ParseCertificate(pemBlock.Bytes) if err != nil { return nil, err } @@ -97,21 +99,21 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, return nil, errors.New(oe) } - caName := fmt.Sprintf("ca-%v.pem", name) - caFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, caName) - f, err := os.Create(caFileName) + caFile, err := os.OpenFile(pemFileName, os.O_RDWR|os.O_APPEND, 0600) if err != nil { - return nil, fmt.Errorf("could not create ca pem file %v: %v", caFileName, err) + return nil, fmt.Errorf("Could not open file %v for writing additional CA chains: %v", pemFileName, err) } - defer f.Close() - _, err = f.Write(ca) + + defer caFile.Close() + _, err = caFile.Write([]byte("\n")) if err != nil { - return nil, fmt.Errorf("could not create ca pem file %v: %v", caFileName, err) + return nil, fmt.Errorf("could not append CA to cert file %v: %v", pemFileName, err) } - f.Write([]byte("\n")) + caFile.Write(ca) + caFile.Write([]byte("\n")) return &ingress.SSLCert{ - CAFileName: caFileName, + CAFileName: pemFileName, PemFileName: pemFileName, PemSHA: pemSHA1(pemFileName), CN: cn, @@ -125,6 +127,36 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, }, nil } +// AddCertAuth creates a .pem file with the specified CAs to be used in Cert Authentication +// If it's already exists, it's clobbered. +func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) { + + caName := fmt.Sprintf("ca-%v.pem", name) + caFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, caName) + + pemCABlock, _ := pem.Decode(ca) + if pemCABlock == nil { + return nil, fmt.Errorf("No valid PEM formatted block found") + } + + _, err := x509.ParseCertificate(pemCABlock.Bytes) + if err != nil { + return nil, err + } + + err = ioutil.WriteFile(caFileName, ca, 0644) + if err != nil { + return nil, fmt.Errorf("could not write CA file %v: %v", caFileName, err) + } + + glog.V(3).Infof("Created CA Certificate for authentication: %v", caFileName) + return &ingress.SSLCert{ + CAFileName: caFileName, + PemFileName: caFileName, + PemSHA: pemSHA1(caFileName), + }, nil +} + // SearchDHParamFile iterates all the secrets mounted inside the /etc/nginx-ssl directory // in order to find a file with the name dhparam.pem. If such file exists it will // returns the path. If not it just returns an empty string diff --git a/examples/PREREQUISITES.md b/examples/PREREQUISITES.md index a2d78c6b6..5881f9045 100644 --- a/examples/PREREQUISITES.md +++ b/examples/PREREQUISITES.md @@ -36,6 +36,105 @@ $ kubectl create secret tls tls-secret --key tls.key --cert tls.crt secret "tls-secret" created ``` +## CA Authentication +You can act as your very own CA, or use an existing one. As an exercise / learning, we're going to generate our +own CA, and also generate a client certificate. + +These instructions are based in CoreOS OpenSSL [instructions](https://coreos.com/kubernetes/docs/latest/openssl.html) + +### Generating a CA + +First of all, you've to generate a CA. This is going to be the one who will sign your client certificates. +In real production world, you may face CAs with intermediate certificates, as the following: + +```console +$ openssl s_client -connect www.google.com:443 +[...] +--- +Certificate chain + 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com + i:/C=US/O=Google Inc/CN=Google Internet Authority G2 + 1 s:/C=US/O=Google Inc/CN=Google Internet Authority G2 + i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA + 2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA + i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority + +``` + +To generate our CA Certificate, we've to run the following commands: + +```console +$ openssl genrsa -out ca.key 2048 +$ openssl req -x509 -new -nodes -key ca.key -days 10000 -out ca.crt -subj "/CN=example-ca" +``` + +This will generate two files: A private key (ca.key) and a public key (ca.crt). This CA is valid for 10000 days. +The ca.crt can be used later in the step of creation of CA authentication secret. + +### Generating the client certificate +The following steps generates a client certificate signed by the CA generated above. This client can be +used to authenticate in a tls-auth configured ingress. + +First, we need to generate an 'openssl.cnf' file that will be used while signing the keys: + +``` +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name +[req_distinguished_name] +[ v3_req ] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +``` + +Then, a user generates his very own private key (that he needs to keep secret) +and a CSR (Certificate Signing Request) that will be sent to the CA to sign and generate a certificate. + +```console +$ openssl genrsa -out client1.key 2048 +$ openssl req -new -key client1.key -out client1.csr -subj "/CN=client1" -config openssl.cnf +``` + +As the CA receives the generated 'client1.csr' file, it signs it and generates a client.crt certificate: + +```console +$ openssl x509 -req -in client1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client1.crt -days 365 -extensions v3_req -extfile openssl.cnf +``` + +Then, you'll have 3 files: the client.key (user's private key), client.crt (user's public key) and client.csr (disposable CSR). + + +### Creating the CA Authentication secret +If you're using the CA Authentication feature, you need to generate a secret containing +all the authorized CAs. You must download them from your CA site in PEM format (like the following): + +``` +-----BEGIN CERTIFICATE----- +[....] +-----END CERTIFICATE----- +``` + +You can have as many certificates as you want. If they're in the binary DER format, +you can convert them as the following: + +```console +$ openssl x509 -in certificate.der -inform der -out certificate.crt -outform pem +``` + +Then, you've to concatenate them all in only one file, named 'ca.crt' as the following: + + +```console +$ cat certificate1.crt certificate2.crt certificate3.crt >> ca.crt +``` + +The final step is to create a secret with the content of this file. This secret is going to be used in +the TLS Auth directive: + +```console +$ kubectl create secret generic caingress --namespace=default --from-file=ca.crt +``` + ## Test HTTP Service All examples that require a test HTTP Service use the standard http-svc pod, diff --git a/examples/auth/client-certs/nginx/README.md b/examples/auth/client-certs/nginx/README.md new file mode 100644 index 000000000..fed88598a --- /dev/null +++ b/examples/auth/client-certs/nginx/README.md @@ -0,0 +1,86 @@ +# TLS authentication + +This example demonstrates how to enable the TLS Authentication through the nginx Ingress controller. + +## Terminology + +* CA: Certificate authority signing the client cert, in this example we will play the role of a CA. +You can generate a CA cert as show in this doc. + +* CA Certificate(s) - Certificate Authority public key. Client certs must chain back to this cert, +meaning the Issuer field of some certificate in the chain leading up to the client cert must contain +the name of this CA. For purposes of this example, this is a self signed certificate. + +* CA chains: A chain of certificates where the parent has a Subject field matching the Issuer field of +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) + +Also your ingress must be configured as a HTTPs/TLS Ingress. + +## Deployment + +Certificate Authentication is achieved through 2 annotations on the Ingress, as shown in the [example](nginx-tls-auth.yaml). + +|Name|Description|Values| +| --- | --- | --- | +|ingress.kubernetes.io/auth-tls-secret|Sets the secret that contains the authorized CA Chain|string| +|ingress.kubernetes.io/auth-tls-verify-depth|The verification depth Certificate Authentication will make|number (default to 1)| + + +The following command instructs the controller to enable TLS authentication using the secret from the ``ingress.kubernetes.io/auth-tls-secret`` +annotation on the Ingress. Clients must present this cert to the loadbalancer, or they will receive a HTTP 400 response + +```console +$ kubectl create -f nginx-tls-auth.yaml +``` + +## Validation + +You can confirm that the Ingress works. + +```console +$ kubectl describe ing nginx-test +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 +Rules: + Host Path Backends + ---- ---- -------- + * + http-svc:80 () +Annotations: + auth-tls-secret: default/caingress + auth-tls-verify-depth: 3 + +Events: + FirstSeen LastSeen Count From SubObjectPath Type Reason Message + --------- -------- ----- ---- ------------- -------- ------ ------- + 7s 7s 1 {nginx-ingress-controller } Normal CREATE default/nginx-test + 7s 7s 1 {nginx-ingress-controller } Normal UPDATE default/nginx-test + 7s 7s 1 {nginx-ingress-controller } Normal CREATE ip: 104.198.183.6 + 7s 7s 1 {nginx-ingress-controller } Warning MAPPING Ingress rule 'default/nginx-test' contains no path definition. Assuming / + + +$ curl -k https://ingress.test.com +HTTP/1.1 400 Bad Request +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`` diff --git a/examples/auth/client-certs/nginx/nginx-tls-auth.yaml b/examples/auth/client-certs/nginx/nginx-tls-auth.yaml new file mode 100644 index 000000000..d4f18bd0c --- /dev/null +++ b/examples/auth/client-certs/nginx/nginx-tls-auth.yaml @@ -0,0 +1,25 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + # Create this with kubectl create secret generic caingress --from-file=ca.crt --namespace=default + ingress.kubernetes.io/auth-tls-secret: "default/caingress" + ingress.kubernetes.io/auth-tls-verify-depth: "3" + kubernetes.io/ingress.class: "nginx" + name: nginx-test + namespace: default +spec: + rules: + - host: ingress.test.com + http: + paths: + - backend: + serviceName: http-svc:80 + servicePort: 80 + path: / + tls: + - hosts: + - ingress.test.com + # Create this cert as described in 'multi-tls' example + secretName: cert +