Use Caddy for local HTTPS (TLS) between front-end reverse proxy and LAN hosts - Wiki - Caddy Community
TL;DL This Wiki explains how to enable https connections between hosts in a LAN with automatically renewed certificates. Just scroll down and go over the example Caddyfiles. They hopefully have enough comment to understa…
Use Caddy for local HTTPS (TLS) between front-end reverse proxy and LAN hosts - Wiki - Caddy Community = 40rem)" rel="stylesheet" data-target="chat_desktop" /> = 40rem)" rel="stylesheet" data-target="discourse-reactions_desktop" /> = 40rem)" rel="stylesheet" data-target="poll_desktop" /> = 40rem)" rel="stylesheet" data-target="desktop_theme" data-theme-id="1" data-theme-name="caddy custom"/> Use Caddy for local HTTPS (TLS) between front-end reverse proxy and LAN hosts Wiki Rob789 (Robbert) February 26, 2021, 10:36pm 1 TL;DL This Wiki explains how to enable https connections between hosts in a LAN with automatically renewed certificates. Just scroll down and go over the example Caddyfiles. They hopefully have enough comment to understand the idea Introduction If you have successfully followed the Wiki Using Caddy as a reverse proxy in a home network by @Matt, you have setup a reverse proxy that provides a TLS encrypted connection from the internet to that reverse proxy. This is mandatory and you shouldn’t want it any less. However, beyond the reverse proxy your connections are non-TLS. This Wiki is about -also- adding TLS between that reverse proxy (frontend) and the hosts behind it (backends) with automatically renewed certificates. Whether this is important, is up to you. Normally, you should be able to trust your own LAN. But there may be situations you don’t want to take a chance. In my case I use the password manager Bitwarden and I didn’t want to send my passwords unencrypted over the LAN. This Wiki assumes you have managed to setup a reverse proxy as explained in the Wiki Using Caddy as a reverse proxy in a home network. From here I have added TLS encryption to the backend(s). Definitions and Requirements The definitions and requirements from the Wiki mentioned above apply for this Wiki too. This Wiki has one extra requirement. You need to setup either a local DNS or split DNS. With your own (split) DNS you can resolve Fully Qualified Domain Names (FQDN) in your LAN rather than using IP addresses. For example: nextcloud.my.example.com → 192.168.0.4 or office.roadrunner → 192.168.0.4 Going into details about DNS or split DNS is out of the scope of this Wiki. Cloudflare has a pretty good explanation what is DNS and more about split DNS can be found here. Two well-known DNS systems are: I use unbound because it is by default available in OPNsense and comes with a handy GUI. Alternatively, you could also fill up your hosts files but I have not tested this myself. It will be much more work to maintain though. Topology The below figure is somewhat similar to the version without TLS in the LAN. There is a frontend host and two backend hosts. Each host can be virtual or physical. The frontend hosts acts as reverse proxy to the outside WWWorld and provides trusted certificates from Let’s Encrypt. The backend hosts are hosting several services. The difference is that the same frontend Caddy service is also setup as a certificate authority using the ACME protocol (powered by smallstep) to provide certificates for the backends. Each backend also has a Caddy service running that requests the certificate and to either act as a web server or reverse proxy (or both) to the desired services on that backend. image516×797 93.5 KB Note that the hosts with docker containers is just presented as a possibility. This Wiki will focus on the 2nd host with Nextcloud on it. DNS To set up the DNS you can follow the same guidance as in Using Caddy as a reverse proxy in a home network. Local (Split) DNS Each host should be given a FQDN to resolve the IP of the corresponding host in the local network. In this example I created: caddy.roadrunner → 192.168.0.2 to reach the frontend (downstream) Caddy service services.roadrunner → 192.168.0.3 to reach the 1st backend (upstream) host with services office.roadrunner → 192.168.2.4 to reach the 2nd backend (upstream) host with services I created domain names for the hosts because pointing directly to the IP addresses didn’t work with the ACME server. I was not able to get certificates for the backends. Services Services can either be installed natively or you can use docker containers. The Caddy services should not be in a docker container because this will complicate the automated certificate renewal. Port forwarding To set up the port forwarding you can follow the same guidance as in Using Caddy as a reverse proxy in a home network. Local HTTPS To enable local HTTPS you need to enable the ACME server in the Frontend Caddy service in the Caddyfile. In the backend you have to point to this server in the Caddyfile to request a certificate. Frontend To enable the ACME server in the frontend, include the acme_server directive in the Caddyfile. # ACME Server caddy.roadrunner { acme_server tls internal } Note that the FQDN caddy.roadrunner is used here. Secondly, you need define or update the FQDN where Caddy listens to and reverse proxies accordingly with TLS. nextcloud.my.example.com { reverse_proxy https://office.roadrunner { header_up Host {upstream_hostport} } } In the above example, connections from nextcloud.my.example.com coming from the WAN are being forwarded to office.roadrunner with TLS enabled. There is one extra line header_up Host {upstream_hostport} which is a host header with placeholder that will override the host header with the host name in the proxy upstream. Read my question topic for more info. Backends There are two ways to configure the backends to request a certificate from the frontend. By setting the TLS directive for each domain that is configured in the Caddyfile, for example; office.roadrunner { tls { ca https://caddy.roadrunner/acme/local/directory ca_root /etc/ssl/certs/root.crt } Here the FQDN office.roadrunner is told to request a certificate from caddy.roadrunner. By setting one directive for the whole Caddyfile in the global section. # Global Option Block # TLS Options acme_ca https://caddy.roadrunner/acme/local/directory acme_ca_root /etc/ssl/certs/root.crt Here a certificate is requested from caddy.roadrunner for each defined domain (sites) in the Caddyfile. To request a certificate, a root certificate from the server is required. (Read this blog from Mike Malone to understand why). Once you have started the Caddy service on the frontend for the first time and there are no errors, a file named root.crt is generated in .local/share/caddy/pki/authorities/local NOTE: The absolute path for the certificate depends on how you run Caddy. Typically when you use systemd the absolute path will be /var/lib/caddy/.local/share/caddy/pki/authorities/local but if you run caddy with caddy start the path .local/share/caddy/pki/authorities/local You will have to copy this file to the backend and point to it in the Caddyfile. In the above example the file root.crt has been copied to /etc/ssl/certs/root.crt will be created in the in the directory you initiated the command (and where the Caddyfile is located). If you are working with SSH terminals, you can just copy / paste the content of root.crt to the other terminal with your favourite txt editor, ie vim. The content will look something like this: -----BEGIN CERTIFICATE----- Mkd2PrifjSodwIBAgIQICdiSD4Yf9/2x0JL2MKkqzAKBggqhkjOPQQDAjAwMS4w LAYDVQQDEyVDYWRkeSBMb2NhbCBBdXRob3JpdHkgLSAyMDIxIEVDQyBSb290MB4X DTIxMDIwMzE3NDKEWRJ#fFJEklejejelWo3oFj3NTYyMlowMDEuMCwGA1UEAxMlQ2FkZHkg TG9jYWwgQXV0aG9yaXR5IC0gMjAyMSBFQ0MgUm9vdDBZMBMGByqGSM49AgEGCCqG SM49AwEHA0IABF6n6K32IongBmQmrFuTKCL0LlNWGY/aLUsWQtb6WazWMtEJxoH/ 9FTO9zoUl5tPGyKU7yty6xFkejelfFDpoa[fpfWPOfkjewBDMA4GA1UdDwEB/wQEAwIBBjAS BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBShK2n24nbgtXrJTNjmfHIPfUTh 4jAKBggqhkjOPQQDAgNIADBFAiEAvAZzPc05PTyL2ilxcwIB5LEq9HDgl/eEtQok Fu1VERUCIC4JxfRKk4PfhRcjBJ8Rz0cjYFJR9fOV90wFo9FEKw[pwq -----END CERTIFICATE----- Now the only thing left to do is to add a service to the defined domain office.roadrunner. You could directly point to a fileserver (Caddy is also a fileserver) or reverse proxy to another service on this backend. In this example I will use Nextcloud. The config is heavily inspired by this great Wiki by @basil office.roadrunner { root * /var/www/html php_fastcgi 127.0.0.1:9000 { env front_controller_active true # Remove index.php form url } file_server header Strict-Transport-Security max-age=31536000; # enable HSTS # .htaccess / data / config / ... shouldn't be accessible from outside @forbidden { path /.htaccess path /data/* path /config/* path /db_structure path /.xml path /README path /3rdparty/* path /lib/* path /templates/* path /occ path /console.php } respond @forbidden 404 } Test After this you should be ready to caddy run or caddy reload if Caddy is already running. When someone enters https://nextcloud.my.example.com in his browser a TLS encrypted connection is made to the frontend host with a certificate provided by Let’s Encrypt. The connection is then forwarded with TLS from the local ACME server to https://office.roadrunner on the backend host. For debugging you can enable the debug option in the global section of each Caddy service. # Global Option Block { debug } Caddyfiles + comments For completeness I have copied two full Caddyfiles for reference. Frontend # Global Option Block { debug # enable debugging. Comment out when everything is working } # ACME Server acme.roadrunner { # defining FQDN for ACME server acme_server # defining the ACME server tls internal } # Nextcloud nextcloud.my.example.com { # defining incoming FQDN reverse_proxy https://office.roadrunner { header_up Host {upstream_hostport} } } Backend # Global Option Block { # General Option debug # TLS Options acme_ca https://acme.roadrunner/acme/local/directory # point to ACME server acme_ca_root /etc/ssl/certs/root.crt # define root certificate } office.roadrunner { # FQDN for the backend root * /var/www/html file_server php_fastcgi 127.0.0.1:9000 { env front_controller_active true # Remove index.php form url } header { Strict-Transport-Security max-age=31536000; # enable HSTS } # .htaccess / data / config / ... shouldn't be accessible from outside @forbidden { path /.htaccess path /data/* path /config/* path /db_structure path /.xml path /README path /3rdparty/* path /lib/* path /templates/* path /occ path /console.php } respond @forbidden 404 } Disclaimer Most of the information in this Wiki can be found in either the official documentation or in the many posts and Wiki’s on this forum. I tried to link as much as possible to the original source. Together with a lot of help from @francislavoie and @matt I was able to get the local https to work. To come In seperate Wiki’s I will post more Caddyfile examples how to host Nextcloud with Collabora, Bitwarden and Papermerge. Caddy is really great in doing this because it’s got everything. Most of the times you can strip down the docker containers and remove Apache, Nginx Trafic and Cerbot and just let (one) Caddy service do the job. 10 Likes HTTPS on LAN - Access via different host Internal CA - Single point of failure? Caddy reverse proxy + Nextcloud + Collabora + Vaultwarden with local HTTPS Caddy as reverse_proxy for another caddy serving wordpress Caddy as reverse proxy and acme-server with my own root-certificate in one docker container? Caddy Lab / Setup Examples Reverse proxy + nginx over https Reverse proxy and self-signed certificate mTLS under FreeBSD Local HTTPS -- Using local ACME endpoint Caddy never stops Caddy Lab / Setup Examples Reverse proxy parsing difficulties mTLS under FreeBSD mTLS under FreeBSD mTLS under FreeBSD mTLS under FreeBSD mTLS under FreeBSD Acme server for local https - on synology Cert_Issuer defaults to LetsEncrypt Redirection issue with multiple chained Caddy reverse proxy servers mTLS between two Caddy reverse proxies TLS Handshake error when reverse proxy requesting certificate Configuring internal tls Internal CA - Single point of failure? TLS between frontend/backend 502 Caddy, Traefik, Firewall && Cloudflare -- threa…