I have the following certificate chain: Root certificate > Intermediate
certificate > End user certificate.
I've set up nginx as an SSL termination proxy for a backend service that
differentiates it actions based on the serial of the intermediate
certificate and the subject of the end user certificate. Only the root
certificate is available at the (nginx) server, the client will present the
intermediate + end user certificate.
Relevant nginx configuration is as follows:
ssl_client_certificate root_cert.pem; # so only the root certificate
ssl_verify_client on;
ssl_verify_depth 2;
proxy_set_header X-Ssl-Client-Escaped-Cert $ssl_client_escaped_cert; # to
pass it on to the backend service
Connectivity works great: nginx accepts the request if the client (I'm
testing with curl) presents intermediate + end user certificate and passes
it on to the backend service. If the client presents only one of the
certificates, nginx rightly rejects it. So I'm sure curl shares both
certificates with nginx.
Where it goes wrong, is when nginx passes the certificate information to the
backend service. The embedded variable $ssl_client_escaped_cert only seems
to contain the end user certificate and not the intermediate one(s). I did
some logging to check $ssl_client_raw_cert, but that also only contains the
end user certificate.
Is there a way to get the intermediate client certificates included in these
embedded variables?
Posted at Nginx Forum: https://forum.nginx.org/read.php?2,288553,288553#msg-288553
Hello!
On Sat, Jul 04, 2020 at 05:52:09AM -0400, everhardt wrote:
> I have the following certificate chain: Root certificate > Intermediate
> certificate > End user certificate.
>
> I've set up nginx as an SSL termination proxy for a backend service that
> differentiates it actions based on the serial of the intermediate
> certificate and the subject of the end user certificate. Only the root
> certificate is available at the (nginx) server, the client will present the
> intermediate + end user certificate.
>
> Relevant nginx configuration is as follows:
>
> ssl_client_certificate root_cert.pem; # so only the root certificate
> ssl_verify_client on;
> ssl_verify_depth 2;
>
> proxy_set_header X-Ssl-Client-Escaped-Cert $ssl_client_escaped_cert; # to
> pass it on to the backend service
>
> Connectivity works great: nginx accepts the request if the client (I'm
> testing with curl) presents intermediate + end user certificate and passes
> it on to the backend service. If the client presents only one of the
> certificates, nginx rightly rejects it. So I'm sure curl shares both
> certificates with nginx.
>
> Where it goes wrong, is when nginx passes the certificate information to the
> backend service. The embedded variable $ssl_client_escaped_cert only seems
> to contain the end user certificate and not the intermediate one(s). I did
> some logging to check $ssl_client_raw_cert, but that also only contains the
> end user certificate.
>
> Is there a way to get the intermediate client certificates included in these
> embedded variables?
No. Futher, intermediate certs as sent by the client are not
saved by the OpenSSL into session information, so the approach you
are trying to use is not going to work at all, more or less
universally (or at least it won't work with session resumption).
For things to work, you may want to reconsider the approach and
make sure all intermediate certificates are known on the server
instead.
--
Maxim Dounin
http://mdounin.ru/
Thanks for your reply, Maxim! I'll work out an alternative then.
Re. session resumption, I read in the OpenSSL docs
(https://www.openssl.org/docs/man1.1.0/man3/SSL_get0_verified_chain.html)
that OpenSSL is willing to store the chain longer than a single request, but
only if the implementing application (nginx) is managing freeing it at the
proper time (eg. when the session times out):
> If applications wish to use any certificates in the returned chain
indefinitely they must increase the reference counts using X509_up_ref() or
obtain a copy of the whole chain with X509_chain_up_ref().
ps. I now see that HAProxy is also discussing it:
https://www.mail-archive.com/haproxy at formilux.org/msg35607.html
Posted at Nginx Forum: https://forum.nginx.org/read.php?2,288553,288596#msg-288596
Hello!
On Mon, Jul 06, 2020 at 03:55:05PM -0400, everhardt wrote:
> Thanks for your reply, Maxim! I'll work out an alternative then.
>
> Re. session resumption, I read in the OpenSSL docs
> (https://www.openssl.org/docs/man1.1.0/man3/SSL_get0_verified_chain.html)
> that OpenSSL is willing to store the chain longer than a single request, but
> only if the implementing application (nginx) is managing freeing it at the
> proper time (eg. when the session times out):
> > If applications wish to use any certificates in the returned chain
> indefinitely they must increase the reference counts using X509_up_ref() or
> obtain a copy of the whole chain with X509_chain_up_ref().
This quote is about how to use the chain if it is returned. The
problem is that the chain is _not_ returned for resumed sessions,
and there is no way to obtain it for a resumed session as long as
the chain uses intermediate certificates provided by the client.
Saving the chain somewhere once session is established may work as
a band-aid in some simple cases, but certainly not an option in
general for multiple reasons, including the fact that this won't
work with TLS session tickets when there is no server-side state.
--
Maxim Dounin
http://mdounin.ru/
Hi Maxim,
I, naively maybe, thought the following would work. At an incoming request,
nginx checks whether the session is new or resumed.
* new: it retrieves the chain, calls X509_chain_up_ref and stores a mapping
from session ID to the chain pointer
* resumed: it retrieves the session ID, looks up the pointer from the
mapping and retrieves the chain from the pointer
At session timeout nginx should drop the session ID from the mapping and
calls X509_free on each certificate in the chain.
Best,
Rob
Posted at Nginx Forum: https://forum.nginx.org/read.php?2,288553,288600#msg-288600