We are using Kubernetes and we need to check the client cert. What we need is to configure the Nginx ingress controller in order to pass the client certificate (if present) to the back-end service. We successfully configured it in our test environment. Here we have the following version:
We used this ingress configuration:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: login-cns-produzione-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/configuration-snippet: "proxy_set_header X-SSL-CERT $ssl_client_escaped_cert;"
nginx.ingress.kubernetes.io/server-snippet: "ssl_verify_client optional_no_ca ;"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- login-cns.it
rules:
- host: login-cns.it
http:
paths:
- path: /
backend:
serviceName: login-cns-produzione-service
servicePort: 443
In our production environment, something is newer than the test. These are the versions in production:
The issue in production is that although we specify the directive optional_no_ca
the ingress controller asks for the client certificate and tries to validate it and it fails
Do you have any idea about why this happens?
Thank you
Angelo
EDIT
By reading here it seems I should use these annotations. But by adding them, the client certificate is not asked by Nginx
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: login-cns-test-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
# Enable client certificate authentication
nginx.ingress.kubernetes.io/auth-tls-verify-client: "optional_no_ca"
# Create the secret containing the trusted ca certificates
nginx.ingress.kubernetes.io/auth-tls-secret: "wso2is-collaudo/iamcoll-it"
# Specify the verification depth in the client certificates chain
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"
# Specify if certificates are passed to upstream server
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
nginx.ingress.kubernetes.io/auth-tls-error-page: "/cns/saml/dettagli-utente"
spec:
tls:
- hosts:
- login-cns-test.it
rules:
- host: login-cns-test.it
http:
paths:
- path: /
backend:
serviceName: login-cns-test-service
servicePort: 443
The very strange thing is that with the previous annotation, I have some X.509 authentications successful and others unsuccessful. I really can't explain the issue.
More info about the versions:
Any tips would be very appreciated... I'm fighting on this issue for several days
EDIT 2
If I use this configuration:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: login-cns-test-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header X-SSL-CERT $ssl_client_escaped_cert;
error_page 495 496 497 /cns/saml/dettagli-utente;
nginx.ingress.kubernetes.io/server-snippet: |
ssl_verify_client optional_no_ca;
if ($ssl_client_verify != SUCCESS) {
return 495;
}
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- login-cns-test.it
rules:
- host: login-cns-test.it
http:
paths:
- path: /
backend:
serviceName: login-cns-test-service
servicePort: 443
The client certificate is asked but, then, Nginx fails on certificate verification and it stops... It doesn't allow the request to be routed to the default backend where I have all the business logic to check and verify the certificate
EDIT 3
In my nginx ingress controller logs I see:
2021/06/14 19:17:29 [crit] 9793#9793: *89059516 SSL_do_handshake() failed (SSL: error:0407E068:rsa routines:RSA_verify_PKCS1_PSS_mgf1:bad signature error:1417B07B:SSL routines:tls_process_cert_verify:bad signature) while SSL handshaking, client: XXX.YY.ZZ.WW, server: 0.0.0.0:443
So far, so good... I want that my Java application checks the certificate. But all the flow stops here. It seems I can't correctly configure the Nginx ingress controller error_page
directive. Maybe should I configure the Nginx ingress controller ConfigMap (https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/configmap.md#custom-http-errors)?
The first reason is simply because Nginx is battle tested and does the first level of screening. If for instance, the client fails to present a valid certificate, the request will not be forwarded to the appserver. Hence this is a nice safety net from possible bugs in the appserver code.
Navigate to your nginx directory, and make a new directory for your site certificates (The ones you want to be publicly signed by a CA such as SSLTrust.AU)
Since Nginx will pass on various HTTP Headers to the appserver, we can use them to implement access control. When a request hits the appserver, it will check the HTTP header Ssl-Client-Verify is set to ‘SUCCESS’. If it isn’t, the request will be rejected with an error message.
Create a symlink to enable your site in nginx: Lastly, restart nginx: Double click the .PFX file, select “Current User”. If you set a passphrase on the PFX above, enter it here. Otherwise, leave blank and hit next.
Even after specifying the optional_no_ca
parameter, it is necessary to provide the client certificate and because of using this parameter, it is not mandatory that the certificate is signed by the trusted CA 1 2.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With