New deployments/ergo/: the Ergo IRC server behind Caddy for Let's Encrypt TLS and the IRC-over-WebSocket endpoint. Docker rather than a native OpenRC/systemd service, because Alpine's apk ergo trails upstream (3.24 ships 2.18.0 against a 2.19.1 security release) and Debian/Alma package it at all -- so native would mean three install paths plus a per-distro ACME client. Both containers run with network_mode: host. IRC bans, throttling and cloaking key on the client's address, and Docker's userland proxy would hide every IPv6 client behind the bridge gateway; host mode also makes the repo's INPUT firewall genuinely govern 80/443/6697. Caddy reaches Ergo over loopback, which is what lets Ergo honour X-Forwarded-For (proxy-allowed-from defaults to localhost) and mark web sessions secure. - deploy.sh generates ircd.yaml ONCE from the pulled image's own default.yaml (version-matched), rewriting the listeners/websockets blocks wholesale rather than patching lines, then asserts hard post-conditions and validates with `ergo run --smoke` in a throwaway container before anything starts. - update.sh: pinned vX.Y.Z tags, GHSA + "### Security" release-note policies, pre-flight against the new image, user NOTICE + grace, stop-consistent DB snapshot, health check (IRC-level, not a bare TCP connect) and rollback that restores the DB only when the schema actually moved. Compatibility-break releases are held for review. certsync copies Caddy's cert pairwise-atomically and verifies the fingerprint served on 6697 after the rehash. - ergoctl: status/users/logs, validated edit+rehash, oper add/passwd/rm, moderation, backup/restore, cert and update passthrough. Talks IRC to the loopback listener over bash /dev/tcp and strips control characters from replies. - Ergo runs as a non-root system user, read-only rootfs, all caps dropped; Caddy keeps only NET_BIND_SERVICE, with admin API and HTTP/3 off. Reviewed adversarially across six lenses; 20 confirmed findings fixed, notably a dead SIGHUP fallback (`rc=$?` after an `if` is always 0), several `set -e` aborts from non-total pipelines, a release-list cache that only ever populated in a subshell, and re-runs that used shell defaults instead of the deployed .env. Verified locally: bash -n, LF endings, the ircd.yaml render against the real 2.19.1 template in both PLAINTEXT modes, the yaml/oper/version/env helpers, and the IRC client against a fake server (registration, oper, rehash success and 400-failure, control-character stripping, server-down paths). Not yet exercised on a Docker host: the containers themselves, ACME issuance and cert sync. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
77 lines
2.6 KiB
Caddyfile
77 lines
2.6 KiB
Caddyfile
# Caddyfile for the Ergo stack. Caddy runs in the HOST network namespace.
|
|
#
|
|
# Roles:
|
|
# 1. Obtain and renew the Let's Encrypt certificate for {$ERGO_DOMAIN}. Ergo's
|
|
# :6697 listener reuses that cert -- `update.sh certsync` copies it into
|
|
# ./ircd and rehashes Ergo (runs every 15 minutes, silent when unchanged).
|
|
# 2. Terminate HTTPS for IRC-over-WebSocket: wss://{$ERGO_DOMAIN}/webirc ->
|
|
# Ergo's plaintext websocket listener on 127.0.0.1:8097. Caddy adds
|
|
# X-Forwarded-For / X-Forwarded-Proto and Ergo trusts them from loopback
|
|
# (proxy-allowed-from: localhost), so web users keep their real IP and are
|
|
# marked secure (+Z).
|
|
# 3. A plain-text landing page with connection details at /.
|
|
#
|
|
# Add your own site config (e.g. a Gamja web client, see README) as
|
|
# conf.d/*.caddy -- deploy.sh installs this file but never touches conf.d/.
|
|
# Apply changes with: docker compose restart caddy
|
|
{
|
|
email {$ACME_EMAIL}
|
|
|
|
# Host networking would put the admin API on the HOST's 127.0.0.1:2019,
|
|
# reachable by every local process and container. The config is static, so
|
|
# turn it off; changes are applied by restarting the container.
|
|
admin off
|
|
|
|
# No HTTP/3: it would bind udp/443 on the host (not in the firewall's port
|
|
# list) and browsers do not run WebSockets over h3 anyway.
|
|
servers {
|
|
protocols h1 h2
|
|
}
|
|
}
|
|
|
|
# Loopback-only health endpoint for the compose healthcheck (no admin API to ask).
|
|
http://127.0.0.1 {
|
|
respond "ok" 200
|
|
}
|
|
|
|
{$ERGO_DOMAIN} {
|
|
encode zstd gzip
|
|
|
|
# IRC over WebSocket. Ergo ignores the request path; Caddy proxies the
|
|
# Upgrade transparently and supplies X-Forwarded-For/-Proto itself.
|
|
handle_path /webirc* {
|
|
reverse_proxy 127.0.0.1:8097
|
|
}
|
|
|
|
# Operator additions (web client, redirects, ...). See conf.d/00-readme.caddy.
|
|
import conf.d/*.caddy
|
|
|
|
handle {
|
|
header Content-Type "text/plain; charset=utf-8"
|
|
respond <<TXT
|
|
{$NETWORK_NAME} -- IRC server
|
|
|
|
Connect with any IRC client:
|
|
server: {$ERGO_DOMAIN}
|
|
port: 6697 (TLS)
|
|
web: wss://{$ERGO_DOMAIN}/webirc (IRC-over-WebSocket endpoint)
|
|
|
|
Register a nickname: /msg NickServ REGISTER <password>
|
|
Powered by Ergo (https://ergo.chat).
|
|
TXT 200
|
|
}
|
|
|
|
header {
|
|
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
|
X-Content-Type-Options "nosniff"
|
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
-Server
|
|
}
|
|
|
|
log {
|
|
output stdout
|
|
format console
|
|
}
|
|
}
|