Search code examples
nginxsslkuberneteskubernetes-ingressnginx-ingress

NGINX Ingress controller, SSL and optional_no_ca


I have obtained a cert from name.com.

➜ tree .     
.
├── ca.crt
├── vpk.crt
├── vpk.csr
└── vpk.key

How I created the secrets

I added ca.crt content at the end of vpk.crt file.

(⎈ | vpk-dev-eks:argocd)
➜ k create secret tls tls-secret --cert=vpk.crt --key=vpk.key --dry-run -o yaml | kubectl apply -f -

(⎈ | vpk-dev-eks:argocd)
➜ kubectl create secret generic ca-secret --from-file=ca.crt=ca.crt --dry-run -o yaml | kubectl apply -f -

This is my ingress:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: websockets-ingress
  namespace: development
  annotations:
    kubernetes.io/ingress.class: "nginx"
    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: "development/ca-secret"
    # 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"
    argocd.argoproj.io/sync-wave: "10"
spec:
  tls:
    - hosts:
      - backend-dev.project.com
      secretName: tls-secret
  
  rules:
  - host: backend-dev.project.com
    http:
      paths:
      - path: /ws/
        backend:
          serviceName: websockets-service
          servicePort: 443

The cert is properly validated, I can connect via various CLI WebSocket clients and https://www.ssllabs.com/ssltest gives me "A+"

However if I set

nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"

then everything stops working and I get 400 error on the nginx ingress controller side (POD logs).

I am confused from the official docs:

The optional_no_ca parameter (1.3.8, 1.2.5) requests the client certificate but does not require it to be signed by a trusted CA certificate. This is intended for the use in cases when a service that is external to nginx performs the actual certificate verification. The contents of the certificate is accessible through the $ssl_client_cert variable.

So what exactly "optional_no_ca" is doing and why "on" fails the requests?


Solution

  • Optional_no_ca does the optional client certificate validation and it does not fail the request when the client certificate is not signed by the CAs from auth-tls-secret. Even after specifying the optional_no_ca parameter, it is necessary to provide the client certificate. As mentioned in the document 1, the actual certificate verification is done when the service is external to Nginx.

    When you set nginx.ingress.kubernetes.io/auth-tls-verify-client:on, it requests a client certificate that must be signed by a certificate that is included in the secret key ca.crt of the secret specified by nginx.ingress.kubernetes.io/auth-tls-secret: secretName.

    If not so, then certificate verification will fail and result in a status code 400 (Bad Request). Check this for further information.