Manages one Caddyfile entry per subdomain (URL -> IP:Port, plus optional
HTTP Basic Auth) and applies changes with a hot-reload through the Caddy
admin API, so an edit is live in ~0.3s with no restart and no certificate
churn.
Safety properties:
- every change is validated by the real Caddy binary before it is written
- a rejected reload restores the previous Caddyfile
- site blocks containing directives cadprox does not model are preserved
byte-for-byte, shown read-only, and never rewritten
- the Caddyfile is the only source of state, so a hand-edited file cannot
silently diverge from what the UI shows
- password hashes never leave the server; the session cookie is HttpOnly
and SameSite=Strict, and mutating requests need a custom header
app.py is a single standard-library file. The image is built on caddy:2.11.4
so validation, adaptation and hashing use the same binary that serves
traffic, which is why basic-auth hashes are bcrypt: Caddy's argon2id hashes
load fine but its basic-auth provider only verifies bcrypt.
Tests: 28 unit tests (Caddy-backed ones skip without the binary) and a 32
assertion end-to-end script that runs against a throwaway Caddy.
39 lines
1.5 KiB
YAML
39 lines
1.5 KiB
YAML
# cadprox - web UI for the Caddy reverse proxy configuration
|
|
#
|
|
# network_mode: host is required, not stylistic. The Caddy admin API listens on
|
|
# 127.0.0.1:2019 on the host loopback and Caddy itself is in host mode, so this
|
|
# is the only way cadprox can reach it to hot-reload without restarting Caddy.
|
|
#
|
|
# The Caddyfile is mounted read-write because cadprox is the writer. Caddy keeps
|
|
# its own read-only mount, so a cadprox edit does not disturb the running
|
|
# container until the admin API accepts the new config.
|
|
|
|
services:
|
|
cadprox:
|
|
build: .
|
|
image: cadprox:1.0
|
|
container_name: cadprox
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
|
|
volumes:
|
|
# The managed Caddyfile. Read-write: cadprox writes it, Caddy reads it.
|
|
- /home/bdm/caddy:/caddy
|
|
# State, audit log and the generated admin credential.
|
|
- ./data:/data
|
|
|
|
environment:
|
|
CADPROX_CADDYFILE: /caddy/Caddyfile
|
|
CADPROX_CADDY_ADMIN: http://127.0.0.1:2019
|
|
CADPROX_CADDY_BIN: caddy
|
|
CADPROX_DATA: /data
|
|
CADPROX_PORT: "8090"
|
|
CADPROX_ADMIN_USER: admin
|
|
# Leave CADPROX_ADMIN_PASSWORD unset to auto-generate one on first boot.
|
|
# The generated password is written to the container log exactly once.
|
|
# CADPROX_ADMIN_PASSWORD: "change-me-at-least-8-chars"
|
|
#
|
|
# Or keep the password in a root-only file instead of the compose file, so
|
|
# it never ends up in `docker inspect` output or a committed config.
|
|
# CADPROX_ADMIN_PASSWORD_FILE: /run/secrets/cadprox-password
|