Reputation: 76
The project I'm working on is an application that is deployed onto a Kubernetes cluster and uses a smartcard PKI scheme for authentication. This cluster is shared between several applications and not all of these applications need (or even should have) the client cert verification for PKI. So we are using the ingress-nginx helm chart to handle ingress into the cluster, then directing to a second reverse proxy that proxies to the application services (web app, api server, etc.). Both proxies have SSL certificates.
Initially, we were using Ingress annotations and mounting the CA certificates into the ingress-nginx deployment in order to handle the client certificate verification, but now we are trying to handle all of the certificate verification on the second proxy so that we have more control over it. Ingress-nginx is a great tool, but it abstracts away a lot of the server config.
Currently, the problem I'm seeing is that the first proxy (ingress-nginx) is receiving the requests and correctly proxying them on to the second proxy. However, because ingress-nginx doesn't have the ssl_client_verify
directive, it doesn't request the client's certificate. When the request reaches the second proxy (which does have ssl_client_verify
), this proxy simply returns a 400 and says that the client never sent a certificate (which it didn't).
How can I tell the second proxy to request the certificate from the first in such a way that the first then requests the certificate from the user? Or if there is a simpler solution, I'm open to that as well.
Our ingress object for the ingress-nginx controller looks like this: (the hostname is populated with kustomize)
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: proxy
namespace: web
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/issuer: cert-issuer
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
tls:
- hosts:
- placeholder.com
secretName: web-cert
rules:
- host: placeholder.com
http:
paths:
- backend:
serviceName: proxy
servicePort: 443
path: /
and the server configuration I'm using for the second proxy looks like this: (environment variables are substituted on container start for the proxy image)
# nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/default.conf;
client_max_body_size 0;
}
# default.conf
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name ${PROXY_DOMAIN} www.${PROXY_DOMAIN};
ssl_certificate /etc/nginx/certs/certificate.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_verify_client on;
ssl_verify_depth 4;
ssl_client_certificate /etc/nginx/certs/DoDRoots.crt;
# Inform the proxyed app which user had connected to this TLS endpoint
add_header X-Client-Verified $ssl_client_verify;
add_header X-CLient-Certificate $ssl_client_escaped_cert;
include /etc/nginx/conf.d/shared_locations.conf;
}
server {
listen 80;
server_name ${PROXY_DOMAIN} www.${PROXY_DOMAIN};
return 301 https://$server_name$request_uri;
}
# shared_locations.conf
location / {
proxy_pass http://${DOMAIN_FRONT}:${PORT_FRONT}/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api/ {
proxy_pass http://${DOMAIN_BACK}:${PORT_BACK}/api/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Upvotes: 1
Views: 1033
Reputation: 123561
How can I tell the second proxy to request the certificate from the first in such a way that the first then requests the certificate from the user?
This is not possible. There is no way to terminate the TLS connection at the first proxy while at the same time passing through the client certificate at the TLS level. Apart from that the TLS handshake on the first proxy is finished before the TLS handshake with the second proxy is even started, e.g. there is no way to let the second proxy signal the requirement of a client certificate.
Upvotes: 2