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.
This commit is contained in:
parent
6e24dc68f7
commit
1ffeb2cee1
8 changed files with 49 additions and 29 deletions
|
@ -135,14 +135,14 @@ Please check the [auth](/examples/auth/basic/nginx/README.md) example.
|
||||||
|
|
||||||
### Certificate Authentication
|
### 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:
|
The annotations are:
|
||||||
```
|
```
|
||||||
ingress.kubernetes.io/auth-tls-secret: secretName
|
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
|
ingress.kubernetes.io/auth-tls-verify-depth
|
||||||
|
|
|
@ -74,33 +74,45 @@ func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLC
|
||||||
|
|
||||||
cert, okcert := secret.Data[apiv1.TLSCertKey]
|
cert, okcert := secret.Data[apiv1.TLSCertKey]
|
||||||
key, okkey := secret.Data[apiv1.TLSPrivateKeyKey]
|
key, okkey := secret.Data[apiv1.TLSPrivateKeyKey]
|
||||||
|
|
||||||
ca := secret.Data["ca.crt"]
|
ca := secret.Data["ca.crt"]
|
||||||
|
|
||||||
|
// namespace/secretName -> namespace-secretName
|
||||||
nsSecName := strings.Replace(secretName, "/", "-", -1)
|
nsSecName := strings.Replace(secretName, "/", "-", -1)
|
||||||
|
|
||||||
var s *ingress.SSLCert
|
var s *ingress.SSLCert
|
||||||
if okcert && okkey {
|
if okcert && okkey {
|
||||||
if cert == nil || key == nil {
|
if cert == nil {
|
||||||
return nil, fmt.Errorf("error retrieving cert or key from secret %v: %v", secretName, err)
|
return nil, fmt.Errorf("secret %v has no 'tls.crt'", secretName)
|
||||||
}
|
}
|
||||||
s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
|
if key == nil {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("secret %v has no 'tls.key'", secretName)
|
||||||
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)
|
|
||||||
} 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)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("no keypair or CA cert could be found in %v", 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unexpected error creating pem file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
s, err = ssl.AddCertAuth(nsSecName, ca)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Name = secret.Name
|
s.Name = secret.Name
|
||||||
|
|
|
@ -509,9 +509,13 @@ func (ic *GenericController) getBackendServers(ingresses []*extensions.Ingress)
|
||||||
ca := ic.annotations.CertificateAuth(ing)
|
ca := ic.annotations.CertificateAuth(ing)
|
||||||
if ca != nil {
|
if ca != nil {
|
||||||
server.CertificateAuth = *ca
|
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 {
|
} 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 {
|
for _, path := range rule.HTTP.Paths {
|
||||||
|
@ -671,7 +675,8 @@ func (ic *GenericController) getBackendServers(ingresses []*extensions.Ingress)
|
||||||
return aUpstreams, aServers
|
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) {
|
func (ic GenericController) GetAuthCertificate(secretName string) (*resolver.AuthSSLCert, error) {
|
||||||
if _, exists := ic.sslCertTracker.Get(secretName); !exists {
|
if _, exists := ic.sslCertTracker.Get(secretName); !exists {
|
||||||
ic.syncSecret(secretName)
|
ic.syncSecret(secretName)
|
||||||
|
@ -894,10 +899,12 @@ func (ic *GenericController) createServers(data []*extensions.Ingress,
|
||||||
RequestBuffering: bdef.ProxyRequestBuffering,
|
RequestBuffering: bdef.ProxyRequestBuffering,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generated on Start() with createDefaultSSLCertificate()
|
||||||
defaultPemFileName := fakeCertificatePath
|
defaultPemFileName := fakeCertificatePath
|
||||||
defaultPemSHA := fakeCertificateSHA
|
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)
|
defaultCertificate, err := ic.getPemCertificate(ic.cfg.DefaultSSLCertificate)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defaultPemFileName = defaultCertificate.PemFileName
|
defaultPemFileName = defaultCertificate.PemFileName
|
||||||
|
|
|
@ -54,7 +54,7 @@ type AuthSSLCert struct {
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
// CAFileName contains the path to the secrets 'ca.crt'
|
// CAFileName contains the path to the secrets 'ca.crt'
|
||||||
CAFileName string `json:"caFilename"`
|
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"`
|
PemSHA string `json:"pemSha"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
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{
|
return &ingress.SSLCert{
|
||||||
CAFileName: caFileName,
|
CAFileName: caFileName,
|
||||||
PemFileName: caFileName,
|
PemFileName: caFileName,
|
||||||
|
|
|
@ -132,7 +132,12 @@ The final step is to create a secret with the content of this file. This secret
|
||||||
the TLS Auth directive:
|
the TLS Auth directive:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ kubectl create secret generic caingress --namespace=default --from-file=ca.crt
|
$ kubectl create secret generic caingress --namespace=default --from-file=ca.crt=<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=<ca.crt> --from-file=tls.crt=<tls.crt> --from-file=tls.key=<tls.key>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test HTTP Service
|
## Test HTTP Service
|
||||||
|
|
|
@ -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.
|
* Client Cert: Certificate used by the clients to authenticate themselves with the loadbalancer/backends.
|
||||||
|
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
You need a valid CA File, composed of a group of valid enabled CAs. This MUST be in PEM Format.
|
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
|
## Deployment
|
||||||
|
|
||||||
|
@ -51,8 +50,7 @@ Name: nginx-test
|
||||||
Namespace: default
|
Namespace: default
|
||||||
Address: 104.198.183.6
|
Address: 104.198.183.6
|
||||||
Default backend: default-http-backend:80 (10.180.0.4:8080,10.240.0.2:8080)
|
Default backend: default-http-backend:80 (10.180.0.4:8080,10.240.0.2:8080)
|
||||||
TLS:
|
TLS: tls-secret terminates ingress.test.com
|
||||||
tls-secret terminates ingress.test.com
|
|
||||||
Rules:
|
Rules:
|
||||||
Host Path Backends
|
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
|
$ curl -I -k --key ~/user.key --cert ~/user.cer https://ingress.test.com
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Server: nginx/1.11.9
|
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.
|
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``
|
The curl version used here was ``curl 7.47.0``
|
||||||
|
|
||||||
## Which certificate was used for authentication?
|
## 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.
|
||||||
|
|
|
@ -21,6 +21,5 @@ spec:
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- ingress.test.com
|
- ingress.test.com
|
||||||
# Create this cert as described in 'multi-tls' example
|
secretName: tls-secret
|
||||||
secretName: cert
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue