Caddy phase 2: TLS via Porkbun DNS-01 wildcard certs

- Caddyfile: drop http:// site prefixes, global acme_dns porkbun block,
  HTTPS redirects on the bare-IP and legacy vhosts
- new dankclaw/caddy/Containerfile: xcaddy build with caddy-dns/porkbun
- compose: caddy uses franksdank/caddy-porkbun:latest + Porkbun env vars
- deploy.sh: build the image on first deploy if missing
- .env.example: rename PORKBUN_API_SECRET -> PORKBUN_API_SECRET_KEY
  (the name the caddy-dns/porkbun module expects)
- tests: 7 new caddyfile/compose seams for the TLS config
This commit is contained in:
Frank
2026-08-22 02:39:41 -07:00
parent e9277fa8af
commit 7f4ce06927
6 changed files with 115 additions and 32 deletions
+5 -3
View File
@@ -35,7 +35,9 @@ SIGNAL_ACCOUNT=+15551234567
# Comma-separated E.164 numbers allowed to message the agent.
SIGNAL_ALLOWED_USERS=+15551234567
# Porkbun API (franksdank.cloud zone). Caddy uses these for DNS-01
# Let's Encrypt wildcard cert issuance. Scope the token to this zone only.
# Porkbun API (franksdank.cloud + franksdank.casa zones). Caddy uses these
# for DNS-01 Let's Encrypt wildcard cert issuance. Porkbun API keys are
# account-wide; rotate them here rather than in the dashboard.
# PORKBUN_API_SECRET_KEY is the name the caddy-dns/porkbun module expects.
PORKBUN_API_KEY=changeme
PORKBUN_API_SECRET=changeme
PORKBUN_API_SECRET_KEY=changeme
+40 -28
View File
@@ -15,21 +15,33 @@
# hash (CADDY_BASICAUTH_HASH from the host .env, git-ignored) into
# /home/dank/Containers/caddy/Caddyfile, which the container mounts.
#
# Phase 1 uses explicit http:// site addresses (no TLS yet). Phase 2 drops
# the prefix and adds a wildcard Let's Encrypt cert via DNS-01 (Porkbun).
# TLS: real Let's Encrypt certs. Site addresses carry no http:// prefix,
# so Caddy serves HTTPS (and auto-redirects HTTP). Certs are wildcard
# (dns-01) via the global acme_dns block below, using the Porkbun module
# built into the franksdank/caddy-porkbun image (caddy/Containerfile).
# That needs no inbound ports and works from the LAN, where dnsmasq
# resolves these names to 192.168.0.143.
{
admin off
email admin@franksdank.cloud
# Solve ACME challenges for every site via Porkbun DNS (no port 80/443
# reachability needed). Credentials come from the container environment
# (compose.yaml, from the host .env — never committed).
acme_dns porkbun {
api_key {env.PORKBUN_API_KEY}
api_secret_key {env.PORKBUN_API_SECRET_KEY}
}
}
# --- Bare-IP access (health checks, convenience) ---
:80 {
redir http://dankclaw.franksdank.cloud/ 302
redir https://dankclaw.franksdank.cloud/ 302
}
# --- Homepage (password protected) ---
http://dankclaw.franksdank.cloud {
dankclaw.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
@@ -38,8 +50,8 @@ http://dankclaw.franksdank.cloud {
}
# --- Legacy name: the media host has no Caddy of its own anymore ---
http://dankserver.franksdank.cloud {
redir http://dankclaw.franksdank.cloud/ 302
dankserver.franksdank.cloud {
redir https://dankclaw.franksdank.cloud/ 302
}
# ═══════════════════════════════════════════════════════════════
@@ -47,7 +59,7 @@ http://dankserver.franksdank.cloud {
# ═══════════════════════════════════════════════════════════════
# --- SearXNG (quadlet; behind the Gluetun tunnel, published 8081) ---
http://search.franksdank.cloud {
search.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
@@ -55,12 +67,12 @@ http://search.franksdank.cloud {
}
# --- ComfyUI ---
http://comfyui.franksdank.cloud {
comfyui.franksdank.cloud {
reverse_proxy comfyui:8188
}
# --- OpenWebUI (quadlet on slirp4netns; published 3005) ---
http://chat.franksdank.cloud {
chat.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
@@ -68,7 +80,7 @@ http://chat.franksdank.cloud {
}
# --- Hermes ---
http://hermes.franksdank.cloud {
hermes.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
@@ -76,12 +88,12 @@ http://hermes.franksdank.cloud {
}
# --- Gitea (quadlet; published 3000) ---
http://gitea.franksdank.cloud {
gitea.franksdank.cloud {
reverse_proxy 192.168.0.143:3000
}
# --- Grafana ---
http://grafana.franksdank.cloud {
grafana.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
@@ -89,7 +101,7 @@ http://grafana.franksdank.cloud {
}
# --- Prometheus ---
http://prometheus.franksdank.cloud {
prometheus.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
@@ -97,7 +109,7 @@ http://prometheus.franksdank.cloud {
}
# --- Loki ---
http://loki.franksdank.cloud {
loki.franksdank.cloud {
reverse_proxy loki:3100
}
@@ -106,21 +118,21 @@ http://loki.franksdank.cloud {
# ═══════════════════════════════════════════════════════════════
# --- Plex (existing, kept) ---
http://plex.franksdank.casa {
plex.franksdank.casa {
reverse_proxy 192.168.0.235:32400
}
http://plex.franksdank.cloud {
plex.franksdank.cloud {
reverse_proxy 192.168.0.235:32400
}
# --- Calibre-Web ---
http://books.franksdank.cloud {
books.franksdank.cloud {
reverse_proxy 192.168.0.235:8083
}
# --- RomM ---
http://romm.franksdank.cloud {
romm.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
@@ -128,7 +140,7 @@ http://romm.franksdank.cloud {
}
# --- Copyparty ---
http://files.franksdank.cloud {
files.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
@@ -136,7 +148,7 @@ http://files.franksdank.cloud {
}
# --- Jellyfin ---
http://jellyfin.franksdank.cloud {
jellyfin.franksdank.cloud {
reverse_proxy 192.168.0.235:8096 {
transport http {
versions h2c 1.1
@@ -145,36 +157,36 @@ http://jellyfin.franksdank.cloud {
}
# --- *arr Stack ---
http://radarr.franksdank.cloud {
radarr.franksdank.cloud {
reverse_proxy 192.168.0.235:7878
}
http://sonarr.franksdank.cloud {
sonarr.franksdank.cloud {
reverse_proxy 192.168.0.235:8989
}
http://lidarr.franksdank.cloud {
lidarr.franksdank.cloud {
reverse_proxy 192.168.0.235:8686
}
http://readarr.franksdank.cloud {
readarr.franksdank.cloud {
reverse_proxy 192.168.0.235:8787
}
http://prowlarr.franksdank.cloud {
prowlarr.franksdank.cloud {
reverse_proxy 192.168.0.235:9696
}
http://qbittorrent.franksdank.cloud {
qbittorrent.franksdank.cloud {
reverse_proxy 192.168.0.235:8080
}
# --- Optional: Jackett ---
# http://jackett.franksdank.cloud {
# jackett.franksdank.cloud {
# reverse_proxy 192.168.0.235:9117
# }
# --- Optional: Whisparr ---
# http://whisparr.franksdank.cloud {
# whisparr.franksdank.cloud {
# reverse_proxy 192.168.0.235:6969
# }
+22
View File
@@ -0,0 +1,22 @@
# Caddy with the Porkbun DNS provider (github.com/caddy-dns/porkbun).
#
# The porkbun module lets Caddy solve Let's Encrypt DNS-01 challenges
# directly through the Porkbun API, so the wildcard cert for
# *.franksdank.cloud / *.franksdank.casa can be issued and renewed without
# any port 80/443 reachability from the internet (see the Caddyfile in
# ../Caddyfile and docs/Infrastructure/LAN-DNS.md).
#
# Build on dankclaw (podman rootless, same as every other container here):
# podman build -t franksdank/caddy-porkbun:latest -f dankclaw/caddy/Containerfile .
#
# The first build runs an xcaddy Go compilation and takes a few minutes.
# Rebuild only when Caddy or the porkbun module must be updated.
FROM docker.io/library/caddy:2-builder AS builder
RUN xcaddy build \
--with github.com/caddy-dns/porkbun \
--output /usr/bin/caddy-porkbun
FROM docker.io/library/caddy:2
COPY --from=builder /usr/bin/caddy-porkbun /usr/bin/caddy
+7 -1
View File
@@ -199,12 +199,18 @@ services:
# REPLACE_WITH_HASH placeholder. Certs/config live under
# /home/dank/Containers/caddy (rootless-writable).
caddy:
image: docker.io/library/caddy:latest
# Locally built: stock Caddy + the Porkbun DNS provider
# (dankclaw/caddy/Containerfile). See that file for build instructions.
image: franksdank/caddy-porkbun:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
# Consumed by the Caddyfile's acme_dns block (DNS-01 wildcard certs).
PORKBUN_API_KEY: ${PORKBUN_API_KEY}
PORKBUN_API_SECRET_KEY: ${PORKBUN_API_SECRET_KEY}
volumes:
- /home/dank/Containers/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- /home/dank/Containers/caddy/data:/data