We're proxying a backend that we don't control & the backend is requesting a client certificate when connecting on https.
When we request these pages through the proxy the connection times out with no response from the backend, however the pages work fine if we bypass the proxy. Is this a problem with nginx handling the client certificate or should we be looking elsewhere?
Nginx config:
server {
listen 443 ssl;
ssl on;
ssl_certificate /etc/nginx/ssl/webserver.pem;
ssl_certificate_key /etc/nginx/ssl/webserver.key;
location / {
proxy_pass https://www.backendsite.com;
proxy_set_header X-Forwarded-For $http_x_forwarded_for;
}
}
I'm not sure whether you realize this or not, but what you're doing is a Man-In-The-Middle attack on
www.backendsite.com
.To understand why this can't work in your scenario we are going to need to explain the actual role of certificates and keys in TLS. So here we go.
First of all, we need to establish a key in the clear. We don't have and for efficiency reasons can't have a secure channel, so we use Diffie Hellman as our key exchange protocol. Previously we might have established parameters we use. Modern thinking says that's a bad idea, let's make some up as we go. This is called DHE (Ephemeral Diffie Hellman). If you're feeling really fancy you might do this over EC (EC-DHE).
Now, the question is, how do you know I sent you the parameters I did, using DH? To verify that, we need a digital signature algorithm so you can have some public information about me to verify that the signature made with my private key can only be made by me.
Server key material provides that for the server end - a private key and a public key (contained in the certificate, along with signatures by the CA saying yes, we trust this too).
NORMALLY, the client can just generate a public/private key pair for anonymous connections as it feels like. So under usual circumstances:
However, now the client provides a specific key pair. This key pair is used to protect the parameters of the protocol, so here's what happens:
The reason this doesn't quite work is because nginx is trying to act as a http proxy, taking off and re-applying https as needed, and with trusted certificates established at both ends TLS is preventing MITM by design.
There are some alternative design decisions engineers can take where the backend is under their control. I ended up here because I wished to proxy to a unix socket passing some certificate information onwards. This can be done using these notes on authenticating certificates with nginx and this observation on HTTP headers (don't pass the entire raw certificate).