This is a quick tutorial for serving the Incus web UI via caddy.
I have the web UI exposed on my local network so I can access it via local IP/domain. I also want to be able to access it outside my LAN just like I access many of my other self-hosted services.
By default, caddy provisions certs and handles HTTPS for you, so when you try to access the web UI via caddy, you’re not using your Incus cert. As such, you won’t be authenticated and Incus will ask you to generate a new cert.
The idea here is to get caddy to use the cert generated and trusted by Incus to authenticate.
WARNING: If your caddy instance is exposed to the public internet, you will be exposing your Incus host as well. Anyone who can connect to caddy will have full access to Incus.
It is assumed that your caddy instance is behind some form of authentication such as caddy-security, Authelia, etc.
Prerequisites
Unless you haven’t set up the web UI at all yet, you probably already generated a cert and told Incus to trust it. We’ll remove this cert so Incus forces us to generate a new one.
Get your cert’s fingerprint so you can remove it:
incus config trust listincus config trust remove <fingerprint>
Generate a new cert
Go the web UI where you’ll be asked to generate a cert. Set a password for the cert (you’ll need it later).
Copy this cert to Incus and trust it:
incus config trust add-certificate incus-ui.crt
Go through the rest of the setup, including the download of the .pfx file, and make sure you can fully access Incus.
Set up your client auth cert and key
Extract the cert and key from the .pfx file (you should be prompted for your cert password):
openssl pkcs12 -in incus-ui.pfx -clcerts -nokeys -out incus-client.crtopenssl pkcs12 -in incus-ui.pfx -nocerts -nodes -out incus-client.key
Copy incus-client.crt and incus-client.key to wherever you’re running caddy.
- If you’re using caddy in docker or something like that, you will of course need to adjust as needed.
Set permissions/owner so caddy can read them:
sudo chmod 644 incus-client.crt incus-client.keysudo chown caddy:caddy incus-client.crt incus-client.key
The files also need to be in a directory caddy can read (this is the caddy user’s home dir):
sudo mv incus-client.crt incus-client.key /var/lib/caddy/
Caddyfile
Because we’re proxying HTTPS → HTTPS, we have to set tls_insecure_skip_verify. Setting tls_insecure_skip_verify has security implications. Use at your own risk.
By using tls_client_auth, we can tell caddy to use our cert to authenticate with Incus, just like we do with our browser. This means all requests made to Incus via caddy will be authenticated by default. This effectively turns off authentication for Incus when accessed via caddy (see the warning at the beginning of this post).
incus.example.com {
reverse_proxy https://incus.lan:8000 {
transport http {
tls_insecure_skip_verify
tls_client_auth /var/lib/caddy/incus-client.crt /var/lib/caddy/incus-client.key
}
}
}
Replace incus.example.com and https://incus.lan:8000 as appropriate.
Restart caddy and you’re done. You should be able to access Incus on your local network and through caddy.