CluPilotCloud/docker/caddy/vpn.Caddyfile

54 lines
2.3 KiB
Plaintext

# The console, served on the WireGuard hub address.
#
# Runs inside the provisioning container's network namespace, so it listens on
# 10.66.0.1 — the tunnel address itself. A VPN client therefore needs no route
# beyond the management subnet it already has, and the request arrives at the
# application with its real 10.66.0.x source, which is what the console's own
# IP allowlist checks.
#
# The certificate is the one the public Caddy already obtains and renews for
# this hostname. A certificate is bound to the NAME, not to the address that
# serves it, so the same file is valid here. Shared read-only rather than
# duplicated: a second ACME client would be a second thing to renew, and a
# private address cannot satisfy an HTTP challenge in any case.
{
admin off
auto_https off
}
https://{$VPN_INTERNAL_HOST}:443 {
bind {$VPN_HUB_ADDRESS}
tls {$VPN_CERT_PATH} {$VPN_KEY_PATH}
# To the application directly. Resolvable because this container shares the
# provisioning container's namespace, which is on the compose network.
reverse_proxy app:80 {
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto https
header_up Host {host}
}
}
# A health port the deployment can ask, on the hub address and nowhere else.
#
# The site above matches on the console's HOSTNAME. A probe to the bare address
# sends no SNI, is offered no certificate and fails the handshake — which is
# indistinguishable from the gateway being down, and that is precisely the
# mistake that left VPN_READY false on a healthy tunnel. Plain HTTP on a
# separate port removes TLS, SNI and name resolution from the question and
# answers only what is being asked: is this gateway listening, in this network
# namespace, right now.
#
# Not a hole: 10.66.0.1 exists only inside the tunnel, and this port is never
# published. Caddy also refuses to start when the certificate above cannot be
# read, so a health port that answers proves the whole file loaded.
http://{$VPN_HUB_ADDRESS}:{$VPN_HEALTH_PORT:8081} {
# bind, not merely a matching hostname: without it Caddy listens on every
# interface in the namespace and only MATCHES on the address. Nothing is
# published, so it was not reachable from outside either way — but "on the
# hub address and nowhere else" should be what the file actually does.
bind {$VPN_HUB_ADDRESS}
respond /healthz 204
respond 404
}