Reading client certificate details with NGINX - nginx

I want to read the client certificate passed by the user and extract the user information out of the certificate and pass add it as header to every subsequent request made by NGINX using reverse proxy (using proxy pass).
How can I read the client cert information on NGINX ?

Related

How to handle POST/GET with an https URL in proxy?

I wrote a proxy server which works well. But when looking at the log, there are some weird requests like:
POST https://vortex.data.microsoft.com/collect/v1 HTTP/1.1
Also some GET over https. I think only CONNECT is allowed over https, am I wrong? If I am wrong, how to deal with these request? (I just dropped these requests in my app.)
Another thing maybe unrelated is all these requests are related to microsoft from the log.
There isn't any problem handling any HTTP Method with HTTPS within a proxy.
All the requests with https://-protocol will be automatically received and sent to port 443 if not indicated otherwise.
Independently if you have a server where you deployed a HAProxy, NGINX, Apache Web Server or that you literally wrote a proxy like this one in JavaScript, only thing you have to do is to literally proxy the requests to the destination server address.
Regarding the encryption, precisely HTTPS ensures that there are no eavesdroppers between the client and the actual target, so the Proxy would act as initial target and then this would transparently intercept the connection.
Client starts HTTPS session to Proxy
Proxy intercepts and returns its certificate, signed by a CA trusted by the client.
Proxy starts HTTPS session to Target
Target returns its certificate, signed by a CA trusted by the Proxy.
Proxy streams content, decrypt and re-encrypt with its certificate.
Basically it's a concatenation of two HTTPS sessions, one between the client and the proxy and other between the proxy and the final destination.

Possibility to bypass the Certificate Verify in mutual authentication (mTLS)

In the TLS handshake process, the Certificate Verify message will follow the Client Key Exchange message after the server requested a client certificate. The Certificate Verify contains a digital signature computed over all previous handshake messages including the type and length fields of the handshake messages. This process allows the client to prove that it owns the private key of the client certificate it sends to the server.
The idea came from a practical problem. There is an mTLS enabled server that conducts different action policies based on the client certificate received, for example, different welcome pages for different client certificates. If a layer-7 reverse proxy service like the load balancer is placed in front of the proxied server which also requires decrypting the TLS traffic. The proxied server can only get the client certificate information from the HTTP header (for example, set proxy_set_header with $ssl_client_cert variable in NGINX) which requires modifying the logic of the server.
A simple but very troublesome solution is the reverse proxy service stores all the client certificates and their private keys. The reverse proxy service will use the same client certificate it received during the mTLS handshake process to establish the mTLS connection with the proxied server.
Since the reverse proxy service can choose whether to trust the client or not with its own implementation, it is possible to forge the Certificate Verify message by asking the client to send a second Certificate Verify signature when the proxied server needs the reverse proxy service to do so (I know it's like a man-in-the-middle attack)?

How to use Python requests to connect to a server through proxy when both requires different client certificate

I want to connect to a https server using python requests library through a proxy. The code roughly looks like
response = requests.get(SERVER_ENDPOINT, proxies=PROXIES, cert=??)
My problem is, both server and proxy requires client authentication, and unfortunately different CA is used to authenticate server and proxy. Is there a way to pass two CAs when making a request? The documentation doesn't seem to be very clear on this scenario.
Any help is greatly appreciated:)
Method Tried:
Tried the method as suggested in another link Python requests - how to add multiple own certificates, and bundle certs and keys into separate pem files using the code below:
response = requests.get(SERVER_ENDPOINT, proxies=PROXIES, cert=(CERT_BUNDLE, KEY_BUNDLE))
It seems that only the 1st cert and key is used, so I am able to pass client auth at proxy server, but failed auth at destination server.

Working with SSL client certificates embedded in the Request Header

To get around the problem of an SSL-terminating load balancer (it doesn't forward client certs to the application servers), our ISP has configured our environment such that client certificates are forwarded within the HTTP headers to the real servers (as X-Client-Cert).
I will be authenticating clients with a PIN mapped to the serial number of the certificate they've been issued with. But how do I get at the certificate from the custom header?
I assume you know that you can get the certificate data using
String s = Request.Headers["X-Client-Cert"];
The question is now in which format the certificate is added to the header. I would assume that it is encoded in Base64.
byte[] certdata = Convert.FromBase64String(s);
Then you can create the certificate object from it:
X509Certificate cert = new X509Certificate(certdata);
Depending on if the load balancer checks the client certificate for validity (and if it has been singed by the correct root CA) or not you have to check the validity of the certificate yourself or not.
Afterwards you can just read the serial number via cert.GetSerialNumber();

HTTP Spec: Proxy-Authorization and Authorization headers

So I'm trying to implement the following scenario:
An application is protected by Basic Authentication. Let's say it is hosted on app.com
An HTTP proxy, in front of the application, requires authentication as well. It is hosted on proxy.com
The user must therefore provide credentials for both the proxy and the application in the same request, thus he has different username/password pairs: one pair to authenticate himself against the application, and another username/password pair to authenticate himself against the proxy.
After reading the specs, I'm not really sure on how I should implement this. What I was thinking to do is:
The user makes an HTTP request to the proxy without any sort of authentication.
The proxy answers 407 Proxy Authentication Required and returns a Proxy-Authenticate header in the format of: "Proxy-Authenticate: Basic realm="proxy.com".Question: Is this Proxy-Authenticate header correctly set?
The client then retries the request with a Proxy-Authorization header, that is the Base64 representation of the proxy username:password.
This time the proxy authenticates the request, but then the application answers with a 401 Unauthorized header. The user was authenticated by the proxy, but not by the application. The application adds a WWW-Authenticate header to the response like WWW-Authenticate: Basic realm="app.com". Question: this header value is correct right?
The client retries again the request with both a Proxy-Authorization header, and a Authorization header valued with the Base64 representation of the app's username:password.
At this point, the proxy successfully authenticates the request, forwards the request to the application that authenticates the user as well. And the client finally gets a response back.
Is the whole workflow correct?
Yes, that looks like a valid workflow for the situation you described, and those Authenticate headers seem to be in the correct format.
It's interesting to note that it's possible, albeit unlikely, for a given connection to involve multiple proxies that are chained together, and each one can itself require authentication. In this case, the client side of each intermediate proxy would itself get back a 407 Proxy Authentication Required message and itself repeat the request with the Proxy-Authorization header; the Proxy-Authenticate and Proxy-Authorization headers are single-hop headers that do not get passed from one server to the next, but WWW-Authenticate and Authorization are end-to-end headers that are considered to be from the client to the final server, passed through verbatim by the intermediaries.
Since the Basic scheme sends the password in the clear (base64 is a reversible encoding) it is most commonly used over SSL. This scenario is implemented in a different fashion, because it is desirable to prevent the proxy from seeing the password sent to the final server:
the client opens an SSL channel to the proxy to initiate the request, but instead of submitting a regular HTTP request it would submit a special CONNECT request (still with a Proxy-Authorization header) to open a TCP tunnel to the remote server.
The client then proceeds to create another SSL channel nested inside the first, over which it transfers the final HTTP message including the Authorization header.
In this scenario the proxy only knows the host and port the client connected to, not what was transmitted or received over the inner SSL channel. Further, the use of nested channels allows the client to "see" the SSL certificates of both the proxy and the server, allowing the identity of both to be authenticated.

Resources