HAProxy Client Certificate Authentication for specific backends

Started by s0mbra, March 27, 2021, 04:30:25 PM

Previous topic - Next topic
Hi y'all,

I worked my way through some of the walkthroughs and ended up with a nice SSL-offloading configuration with Let's Encrypt for all my backends, pretty sweet.

Now I have a few web-services I want to expose to the internet. On some of them I want to use Client Certificate Authentication.

I have been trying to figure this out for a few days and couldn't really find a quick and dirty solution provided by the GUI:

Checks on Condition's I tried, but don't work as expected:

  • SSL Client Certificate is valid
  • SSL Client Certificate verify error result

After digging through the HAProxy documentation: there's a quick way to check if a Client Certificate is used in the communication, I thought adding it here can save some people a day of (re)search :)

Add a 'Condition' through the GUI:

Name: client_cert_used
Description: Check if a Client Certificate is used
Condition type: Custom condition (option-pass-through)
Option pass-through: ssl_c_used  1

After that, add the condition to the rule that selects which backend to use.

Hope it helps someone!

Hi,

first of all: Thanks a lot for sharing your findings!  :D :D
I was just  just googeling cert based auth can be done with OPNsense+HAProxy.

I am wondering: Does "Check if a Client Certificate is used" really mean that the certificate is valid?
Where in the configuration do I specify the CA cert against which the client cert is checked?

cheers
Michael




Hey His.Dudeness,

The problem I was trying to solve was to use client certificates for authentication for SOME of the back-ends. For instance, i have a public website backend that obviously doesn't require a client cert. But other, more secure services do.

So, the client-certificate requirement is configured on the 'Public Service' as 'Optional'. This way you don't need a client-cert for the public website. For the secure services, I add the mentioned 'check' if a client-cert is used, otherwise deny access.

The description 'Check if a Client Certificate is Used' does exactly what it promises, you can configure your CA, CRL on the Public Service to make sure the cert is validated, so that happens before the check anyway.

Hi all,

I'm trying to figure out the configuration provided to do the trick with "standard" published service without CERT client verification and "securized" service with CERT client verification.

It's working with CERT verification with condition match to custom  ssl_c_used  1 and also with client CERT SSL CA verfication with Comon Name like " xxxx".

The fact is in both case, I'm not able to have :
- the standard published service working AND
- the securized service not working
When I put the Revokation list check in place in the public service backend, with the client CERT tag as revoked (compromised reason for instance)

When I test both of them (services) are broken with "ERR_BAD_SSL_CLIENT_AUTH_CERT" in the client browser and with "HTTPS-WAN-frontend/192.168.x.x:443: SSL client certificate not trusted" message in the haproxy logs.

Do you have an idea, how to manage this use case by securizing specific services and none of the standard service, and keeping the possibility to revoke a compromised client CERT used in specific service.

Thank you for your help.

Quote from: s0mbra on September 28, 2021, 01:49:13 PM
So, the client-certificate requirement is configured on the 'Public Service' as 'Optional'. This way you don't need a client-cert for the public website. For the secure services, I add the mentioned 'check' if a client-cert is used, otherwise deny access.

I try to achieve something similar and found your post.

What will happen if the client presents a cert that is not valid and you only check if the cert was presented?
Would it be the right way to combine 'ssl_c_used' with 'ssl_c_verify' in your check?

This is a little bit older post, but for those who searching for possible solution for this, I can offer main:
I just roll up separate public service with 444 port and dedicated it to specific backends which I want to connect with client cert. I dont need those backends on 443, as these arent ment for public. This is simple and fast solution....

We found a solid solution that completely solves the problem — even when TLS session resumption is used by modern browsers (like Chrome or Firefox), which typically don't resend the client certificate.

Instead of using a separate port, we're using a single HAProxy frontend on port 443 and a custom ACL condition:

ssl_c_used ssl_c_verify 0
This reliably checks that a client certificate was used at some point in the SSL session and was successfully verified.

🔧 How to add this in OPNsense:

Go to Services → HAProxy → Rules & Checks → Conditions → Click + Add

Name: client_cert_valid_session (or similar)
Condition type: Custom condition (option pass-through)
Option pass-through:

ssl_c_used ssl_c_verify 0

Make sure your HAProxy frontend has Client Certificate Authentication enabled and the client certificate verification mode set to "Optional".
This is necessary so public services still work without a client certificate, while services that require mTLS can evaluate the certificate if it was provided.

You can now use this condition in your Rules to restrict access to specific backends.

This setup has been working perfectly for us — hope it helps others facing the same challenge!