So, I have some good news and some bad news. The good news is that Caddy mTLS works perfectly on FreeBSD. The bad news is that I haven’t been able to successfully roll it into the WordPress backend I have set up. I’ll summarise the good news in this post first and discuss the bad news in the next post.
Assumptions
The first assumption is that I have set up two FreeBSD jails; a frontend reverse proxy server in one jail and a web server in the second jail. Caddy is installed in both jails using the package manager pkg install caddy
. At the time of preparing this post, I’m working with these versions:
# freebsd-version
12.2-RELEASE
# pkg info caddy
caddy-2.3.0_1
If using the DNS challenge, the frontend Caddy binary will need to be replaced using xcaddy
to build a version of Caddy with a supported DNS provider module.
The second assumption this thread makes is that Caddy is serving subdomains of domain.com
using a wildcard certificate. The map
handler is used in the Caddyfile to facilitate managing the subdomains.
Local DNS resolver
The example below assumes acme.lan
resolves to the frontend jail IP and test.lan
resolves to backend jail IP.
Frontend jail considerations
Key frontend Caddyfile constructs required for mTLS:
...
# Internal CA
acme.lan {
acme_server
tls internal
}
...
*.domain.com {
...
map {labels.2} {backend} {mtls} {
# HOSTNAME BACKEND mTLS #COMMENT
#---------------------------------------------------------------
...
test test.lan:443 yes # test.domain.com
...
route {
...
# Secure backend communication
@mtls expression `{mtls} == "yes"`
reverse_proxy @mtls {backend} {
header_up Host {http.reverse_proxy.upstream.hostport}
header_up X-Forwarded-Host {host}
transport http {
tls
}
}
# Unsecured backend communication
@nomtls expression `{mtls} == "no"`
reverse_proxy @nomtls {backend}
...
}
}
Next, and this is the secret ingredient to make mTLS work for FreeBSD, the root certificate for the internal CA has to be added to the system trust.
cat /var/db/caddy/data/caddy/pki/authorities/local/root.crt >> /usr/local/share/certs/ca-root-nss.crt
Limitations
The arrangement breaks if the ca_root_css
package is upgraded in the frontend jail. When this happens, the local CA root certificate will have to be added to the system trust again.
Backend jail considerations
Key backend Caddyfile constructs required for mTLS:
{
...
acme_ca https://acme.lan/acme/local/directory
acme_ca_root /etc/ssl/certs/root.crt
}
test.lan {
...
}
Remember to add the local CA root certificate from the frontend to /etc/ssl/certs/
in the backend,
Next, for test.lan
, I set up a static file server:
test.lan {
root * /usr/local/www/caddy
file server browse
}
I can reliably and consistently access the file server through test.domain.com
. The Caddy reverse proxy provides automatic HTTPS, and mTLS ensures that the path between the frontend and backend Caddy servers is encrypted.