HTTPS Routing based on Client Certificates values with F5 Distributed Cloud
Want to use Client Certificates to authorize and route traffic to different destinations? This article can help you identify how to do it with F5 Distributed Cloud HTTP LB, XFCC, and Header Insert/Remove
Introduction
You have given your end users client certificates to validate their identity while they connect to different apps using mTLS. And potentially, as an extra mile, you want to use part of their "valid Client Certificate information” to do some conditional routing/filtering.
In this article, I will talk about how to set up F5 Distributed Cloud to enable mTLS and use Client Certificate information. I will also talk about where to route traffic. I will not cover the architecture related to a PKI, Certificate Authorities, mTLS.
What you need
- Valid Client Certificates (not outdated, coming from a new and reachable Certificate Authority)
- F5 Distributed Cloud Account
- HTTP LB object (with HTTPS enabled)
- Backend servers/applications (Origin Servers in F5 Distributed Cloud terminology)
What you can use for testing
I personally like the tool XCA, developed and maintained by Christian Hohnstädt (xca - https://hohnstaedt.de/xca )
With this tool, you can locally create a Certificate Authority, all the CA chains, and client certificates attached to this CA.
I have been using a domain created quickly from NO-IP, and added it as a Primary DNS within F5XC.
To expedite the test quicker, i have disabled the Certificate Authority “check” of my certificates, which is not something I would recommend for production and internet-facing applications.
Let's decompose the flow
- Client connect to "https://www.mydomain.com" which points to F5XC HTTP(s) LB (Anycast IP:Port)
- After ClientHello, Server advertise its certificates (ServerHello) and asks the client is required to provide a valid "client certificate".
- Client provides its client certificate with some additional details in the certificate that can be captured and used by F5XC LB
- In the example, the client provides a CommonName as extensions in the X.509 certificate with "app1.mydomain.com".
- The HTTP(s)LB inserts the client certificate attributes inside XFCC header (X-Forwarded-Client-Cert)
- I have selected the "Subject" for this test
- After being added as a header, the "ROUTE" picks it and send it to the relevant Origin Pool (app1* sent to pool-app1, app2* sent to pool-app2 and so on
- And as i take security in account, and I don’t like sending outside, what is not mandatory, I also have an advanced option that removes the XFCC just added at point 5 and 6, so we don’t send this externally to the backend servers.
- For the test, I am just adding a response header. This means that on the client side, I can confirm that the load balancing and routing decisions based on Client Certificate worked correctly.
Here after the commands I have issued on my client’s laptop:
Targeting the same destination website with a client certificate that contains an attribute "CN=app1.mydomain.com"
curl -k --cert app1.vertlavenir.net.crt --key app1.vertlavenir.net.pem --cacert Pipomolo_Root_ca.crt https://www20.vertlavenir.net -vvv
Or the "CN=app2.mydomain.com"
curl -k --cert app2.vertlavenir.net.crt --key app2.vertlavenir.net.pem --cacert Pipomolo_Root_ca.crt https://www20.vertlavenir.net -vvv
And the result:
curl -k --cert app1.vertlavenir.net.crt --key app1.vertlavenir.net.pem --cacert Pipomolo_Root_ca.crt https://www20.vertlavenir.net -vvv
* Host www20.vertlavenir.net:443 was resolved.
* IPv6: (none)
* IPv4: 72.19.3.131
* Trying 72.19.3.131:443...
* Connected to www20.vertlavenir.net (72.19.3.131) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Request CERT (13):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Certificate (11):
* (304) (OUT), TLS handshake, CERT verify (15):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF
* ALPN: server accepted h2
* Server certificate:
* subject: C=FR; ST=IleDeFrance; L=Suresnes; O=F5; OU=Dev; CN=www20.vertlavenir.net; emailAddress=pipomolo@pipomolo.com
* start date: Jul 10 15:16:00 2024 GMT
* expire date: Jul 10 15:16:00 2025 GMT
* issuer: C=FR; ST=IleDeFrance; L=SURESNES; O=InteralIT; OU=Dev; CN=RootCADev; emailAddress=pipomolo@pipomolo.com
* SSL certificate verify result: self signed certificate in certificate chain (19), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://www20.vertlavenir.net/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: www20.vertlavenir.net]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.6.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: www20.vertlavenir.net
> User-Agent: curl/8.6.0
> Accept: */*
>
< HTTP/2 200
< date: Wed, 14 Aug 2024 14:02:38 GMT
< expires: -1
< cache-control: private, max-age=0
< content-type: text/html; charset=ISO-8859-1
< content-security-policy-report-only: object-src 'none';base-uri 'self';script-src 'nonce-4bTclXw9UovdpGR0XwqMcQ' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
< p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
< server: volt-adc
< x-xss-protection: 0
< x-frame-options: SAMEORIGIN
< set-cookie: AEC=AVYB7cqFQ_RReSO0E3XB5KMovQa_NFNyS8mCw7H_AopyrAGzlxsufSOMbS8; expires=Mon, 10-Feb-2025 14:02:38 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax
< set-cookie: __Secure-ENID=21.SE=mQvcfvR181KCyA_XEHvqE7D2VF_y5cBx05ha-GBc1befXmptdwLDvnH3RSiNC4nqJg2DBhfek__DymrMwFwTZcSJkjTIUNhRBRZf_izSXHDlGbJhu3KAkhKEVTzaEJjM-k7IFRexD5SjqS6KxFYDzqbSdEhCO7LBE3NbZC7jkjjZABtQ3U9nYEKeKtSQXoN2MoA0tHqWCoHnEFI8_XqK37sA_L77EW1wKQ; expires=Sun, 14-Sep-2025 06:20:56 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
< accept-ranges: none
< vary: Accept-Encoding
< x-envoy-upstream-service-time: 82
< application: app1
< x-volterra-location: pa2-par
<
Conclusion
With the XFCC header insertion of X.509 Client Certificate, and the ability from the HTTP LB Routes, to do conditional routing based on headers, it is quite simple to use the client certificate to route to different destinations. This concept can be expanded to WAF policy attachment (if you want to apply different policies based on client certificate for example).