Caddy primer
I recently took the time to try out Caddy in my homelab, and kinda liked it. This is a short primer mostly for myself.
Serving this site #
First of, this is my Caddyfile for this site:
{
auto_https off
}
http://solitary-thunder-2257.fly.dev {
redir https://www.monotux.tech{uri} permanent
}
http://monotux.tech {
redir https://www.monotux.tech{uri} permanent
}
http://www.monotux.tech {
root * /usr/share/caddy
file_server
}
Turning off TLS is a quirk of using fly.io, otherwise this should be fairly easy to understand.
The Dockerfile
in use:
FROM klakegg/hugo:0.101.0-ext-onbuild AS hugo
FROM caddy:2
COPY --from=hugo /target/ /usr/share/caddy
COPY ./Caddyfile /etc/caddy/Caddyfile
Limiting by IP #
I have prometheus metrics enabled for some of my externally accessible
services, but I don’t want anyone else to read my metrics. This is
one way to limit /metrics
to only internal addresses:
service.example.com {
handle /metrics {
@denied not remote_ip 192.0.2.0/24
respond @denied "Nope" 403
reverse_proxy 192.0.3.1:1234
}
handle {
reverse_proxy 192.0.3.1:1234
}
}
If you just want to block everyone for a vhost:
service.example.com {
@denied not remote_ip 192.0.2.0/24
respond @denied "Nope" 403
reverse_proxy 192.0.3.1:1234
}
Redirecting #
old.example.com {
redir https://new.example.com{uri} permanent
}
example.com {
redir https://www.example.com{uri} permanent
}
Basic auth #
Generate new hash with caddy hash-password
.
example.com {
basicauth /secret/* {
Bob $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
}
}
TLS with self-signed certificate to backend #
I’m using TLS (with a self-signed certificate) from my caddy server to my internal systems, due to reasons.
foobar.example.com {
@denied not remote_ip 192.0.2.0/24
respond @denied "Nope" 403
reverse_proxy https://foobar.internal {
header_up Host {upstream_hostport}
transport http {
tls
tls_trusted_ca_certs /config/root_ca.crt
}
}
}
websockets / zigbee2mqtt #
I’m not sure this is the easiest way to do this, but here I want to proxy all
requests to /api
to a websocket, and /
over HTTP. In this case it’s a
zigbee2mqtt container running in the same docker network as caddy. I’m
also using my internal certificate authority in this example.
Not that this is without authentication.
{
email me@example.com
acme_ca https://ca.example.com/acme/acme/directory
acme_ca_root /config/root_ca.crt
}
zigbee2mqtt.example.com {
@websockets {
header Connection *Upgrade*
header Upgrade websocket
path /api/*
}
reverse_proxy @websockets zigbee2mqtt:8080/api
reverse_proxy zigbee2mqtt:8080
}
The docker-compose.yml
file in question:
version: '3'
services:
zigbee2mqtt:
container_name: zigbee2mqtt
restart: unless-stopped
image: koenkk/zigbee2mqtt
volumes:
- /path/to/zigbee2mqtt/data:/app/data
- /run/udev:/run/udev:ro
environment:
- TZ=Europe/Stockholm
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
caddy:
container_name: caddy
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- /path/to/caddy/Caddyfile:/etc/caddy/Caddyfile
- /path/to/caddy/data:/data
- /path/to/caddy/config:/config