The history of SSL in HAProxy is very short: around one month ago, we announced the ability for HAProxy to offload SSL from the servers. HAProxy SSL stack comes with some advanced features like TLS extension SNI.
Well, since yesterday afternoon (Tuesday the 2nd), HAProxy can also offload the client certificate management from the server with some advanced features. This is the purpose of today’s article. Again, all the dev is provided by HAProxy Technologies. For the people using the ALOHA Load-Balancer, these features will be included in the next release without GUI integration (which will come later).
Concerning HAProxy, just git clone the latest version or wait for HAProxy-1.5-dev13. When compiling, don’t forget the USE_OPENSSL=yes flag.
Why Client Certificates?
The main purpose of using client-side certificates is to increase the level of authentication. Since HAProxy is often in front of web platform, it is the right place to do this authentication. That way, it could do all the certificate checking before allowing the user to pass through. Then it can process SSL on behalf of server and apply any standard features.
The main purpose of the article is to introduce the new HAProxy features related to SSL client certificates.
Basically, we’ll see how to protect access to our application with client-side certificates and how to properly redirect users to the right page when there is an issue with their certificates.
SSL Client Certificate Generation
Well, we’ll have to create a CA, a server certificate and clients certificates!
Nathan, a nginx user, has written a very nice and well documented article on generating a CA and client certificate here: http://blog.nategood.com/client-side-certificate-authentication-in-ngi.
So I won’t rewrite all the procedure here, just follow Nathan instructions to create your own CA and generate a few client certificates.
All you need is available here:
https://github.com/exceliance/haproxy/blob/master/blog/ssl_client_certificate_management_at_application_level/
Other Stuff
During this article, we’ll use a few client certificates: client1, client2 and client_expired (whose certificate has … expired!).
On the githbu link above, you’ll also find how to generate the PEM file required by HAProxy.
Phase 1: Client Certificate Mandatory
In the configuration below, only users with a client certificate are allowed to get connected on the application. This is achieved by the keywords “verify required“.
frontend ft_ssltests
mode http
bind 192.168.10.1:443 ssl crt ./server.pem ca-file ./ca.crt verify required
default_backend bk_ssltests
backend ssltests
mode http
server s1 192.168.10.101:80 check
server s2 192.168.10.102:80 check
If the client does not provide any certificate, then HAProxy would shut the connection during the SSL handshake. It’s up the user’s software to report the right error…
Testing
Connection with a certificate is allowed:
$ openssl s_client -connect 192.168.10.1:443 -cert ./client1.crt -key ./client1.key
Connection without a certificate is refused:
$ openssl s_client -connect 192.168.10.1:443
[...]ssl handshake failure[...]
Connection with an expired certificate is refused too:
$ openssl s_client -connect 192.168.10.1:443 -cert ./client_expired.crt -key ./client_expired.key
[...]ssl handshake failure[...]
Phase 2: Client Certificate Optional
In the configuration below, all users, those with and those without the certificate, are allowed to get connected. This is achieved by the keyword “verify optional“. We can redirect users to different farm, based on the presence of the certificate or not:
frontend ssltests
mode http
bind 192.168.10.1:443 ssl crt ./server.pem ca-file ./ca.crt verify optional
use_backend sharepoint if { ssl_fc_has_crt } # check if the certificate has been provided and give access to the application
default_backend webmail
backend sharepoint
mode http
server srv1 192.168.10.101:80 check
server srv2 192.168.10.102:80 check
backend webmail
mode http
server srv3 192.168.10.103:80 check
server srv4 192.168.10.104:80 check
If the client does not provide any certificate, HAProxy routes him to the webmail.
If the client provides a certificate, then HAProxy routes him to the application (sharepoint in our example)
If the client provides an expired certificate, then HAProxy refuses the connection like in the phase 1
Phase 3: Client Certificate Optional & Managing Expired Certificates
In the configuration below, all users, those with and those without the certificate are allowed to get connected. This is achieved by the keyword “verify optional“.
The option “crt-ignore-err 10” tells HAProxy to ignore Certificate errors 10 which actually matches the expired certificate.
We can redirect users to different farm based on the presence of the certificate or not and we can propose a dedicated page for users whose certificate has expired with a procedure on how to renew or ask for a new certificate.
frontend ssltests
mode http
bind 192.168.10.1:443 ssl crt ./server.pem ca-file ./ca.crt verify optional crt-ignore-err 10
use_backend static if { ssl_c_verify 10 } # if the certificate has expired, route the user to a less sensitive server to print an help page
use_backend sharepoint if { ssl_fc_has_crt } # check if the certificate has been provided and give access to the application
default_backend webmail
backend static
mode http
option http-server-close
redirect location /certexpired.html if { ssl_c_verify 10 } ! { path /certexpired.html }
server srv5 192.168.10.105:80 check
server srv6 192.168.10.106:80 check
backend sharepoint
mode http
server srv1 192.168.10.101:80 check
server srv2 192.168.10.102:80 check
backend webmail
mode http
server srv3 192.168.10.103:80 check
server srv4 192.168.10.104:80 check
If the client does not provide any certificate, HAProxy routes him to the webmail.
If the client provides a certificate, then HAProxy routes him to the application (sharepoint in our example)
If the client provides an expired certificate, then HAProxy routes him to a static server (non-sensitive) and force the users to show the page which provides the explanation about the expired certificate and how to renew it (it’s up to the admin to write this page).
Phase 4: Client Certificate Optional, Managing Expired Certificates & a Revocation List
In the configuration below, all users, those with and those without the certificate, are allowed to get connected. This is achieved by the keyword “verify optional“.
The option “crt-ignore-err all” tells HAProxy to ignore any client Certificate error.
The option “crl-file ./ca_crl.pem” tells HAProxy to check if the client has not been revoked in the Certificate Revocation List provided in argument.
We can redirect users to different farm based on the presence of the certificate or not, and we can propose a dedicated page for users whose certificate has expired with a procedure on how to renew or ask for a new certificate. We can also present a dedicate page to users whose certificate has been revoked.
frontend ssltests
mode http
bind 192.168.10.1:443 ssl crt ./server.pem ca-file ./ca.crt verify optional crt-ignore-err all crl-file ./ca_crl.pem
use_backend static unless { ssl_c_verify 0 } # if there is an error with the certificate, then route the user to a less sensitive farm
use_backend sharepoint if { ssl_fc_has_crt } # check if the certificate has been provided and give access to the application
default_backend webmail
backend static
mode http
option http-server-close
redirect location /certexpired.html if { ssl_c_verify 10 } ! { path /certexpired.html } # SSL error 10 means "certificate expired"
redirect location /certrevoked.html if { ssl_c_verify 23 } ! { path /certrevoked.html } # SSL error 23 means "Certificate revoked"
redirect location /othererrors.html unless { ssl_c_verify 0 } ! { path /othererrors.html }
server srv5 192.168.10.105:80 check
server srv6 192.168.10.106:80 check
backend sharepoint
mode http
server srv1 192.168.10.101:80 check
server srv2 192.168.10.102:80 check
backend webmail
mode http
server srv3 192.168.10.103:80 check
server srv4 192.168.10.104:80 check
If the client does not provide any certificate, HAProxy routes him to the webmail.
If the client provides a certificate, then HAProxy routes him to the application (sharepoint in our example)
If the client provides an expired certificate, then HAProxy routes him to a static server (non-sensitive) and force the users to show the page which provides the explanation about the expired certificate and how to renew it (it’s up to the admin to write this page).
If the client provides a revoked certificate, then HAProxy routes him to a static server (non-sensitive) and force the users to show the page which provides the explanation about the revoked certificate (it’s up to the admin to write this page).
For any other errors related to the client certificate, then HAProxy routes the user to a static server (non-sensitive) and force the users to show a page explaining there has been an error and how to contact the support (it’s up to the admin to write this page).
Phase 5: Same as Phase 4, but With Multiple CAs, Cert Error in Header & Some ACLs
In the configuration below, all users, those with and those without the certificate, are allowed to get connected. This is achieved by the keyword “verify optional“.
The option “crt-ignore-err all” tells HAProxy to ignore all client Certificate.
The option “crl-file ./ca_crl.pem” tells HAProxy to check if the client has not been revoked in the Certificate Revocation List provided in argument.
The file ca.pem contains 2 CAs: ca and ca2. We can redirect users to different farm based on the presence of the certificate or not, and we can propose a dedicated page for users whose certificate has expired with a procedure on how to renew or ask for a new certificate. We can also present a dedicate page to users whose certificate has been revoked.
frontend ssltests
mode http
bind 192.168.10.1:443 ssl crt ./server.pem ca-file ./ca.pem verify optional crt-ignore-err all crl-file ./ca_crl.pem
use_backend static unless { ssl_c_verify 0 } # if there is an error with the certificate, then route the user to a less sensitive farm
use_backend sharepoint if { ssl_fc_has_crt } # check if the certificate has been provided and give access to the application
default_backend webmail
backend static
mode http
option http-server-close
acl url_expired path /certexpired.html
acl url_revoked path /certrevoked.html
acl url_othererrors path /othererrors.html
acl cert_expired ssl_c_verify 10
acl cert_revoked ssl_c_verify 23
reqadd X-Ssl-Error: 10 if cert_expired
reqadd X-Ssl-Error: 23 if cert_revoked
reqadd X-Ssl-Error: other if ! cert_expired ! cert_revoked
redirect location /certexpired.html if cert_expired ! url_expired
redirect location /certrevoked.html if cert_revoked ! url_revoked
redirect location /othererrors.html if ! cert_expired ! cert_revoked ! url_othererrors
server srv5 192.168.10.105:80 check
server srv6 192.168.10.106:80 check
backend sharepoint
mode http
server srv1 192.168.10.101:80 check
server srv2 192.168.10.102:80 check
backend webmail
mode http
server srv3 192.168.10.103:80 check
server srv4 192.168.10.104:80 check
If the client does not provide any certificate, HAProxy routes him to the webmail.
If the client provides a certificate, then HAProxy routes him to the application (sharepoint in our example)
If the client provides an expired certificate, then HAProxy routes him to a static server (non-sensitive) and force the users to show the page which provides the explanation about the expired certificate and how to renew it (it’s up to the admin to write this page).
If the client provides a revoked certificate, then HAProxy routes him to a static server (non-sensitive) and force the users to show the page which provides the explanation about the revoked certificate (it’s up to the admin to write this page).
For any other errors related to the client certificate, then HAProxy routes the user to a static server (non-sensitive) and force the users to show a page explaining there has been an error and how to contact the support (it’s up to the admin to write this page).
Coming soon…
Later, we’ll improve HAProxy client certificate management with:
Client certificate information in HTTP header
ACLs to match the client information provided in the certificate and take classic decision (routing, blocking, etc….)
Persistence based on the information provided by the certificate (stick tables)
Ability to use a client certificate to get connected (and authenticated) on a server
Don’t hesitate to send your us your wishes!
Related links
How to get SSL with HAProxy getting rid of stunnel, stud, nginx or pound
Enhanced SSL load-balancing with Server Name Indication (SNI) TLS extension