feat(ergo): add Ergo IRC server deployment with ergoctl admin CLI
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>
This commit is contained in:
@@ -120,12 +120,15 @@ deployments/<name>/ # one folder per stack
|
||||
| [`copyparty`](deployments/copyparty/) | Portable file server — web UI/WebDAV behind Caddy, plus direct **SFTP** + **FTPS**. Ships a security-notices-aware updater. | — |
|
||||
| [`simplex`](deployments/simplex/) | SimpleX SMP + XFTP relay with Tor hidden services + encrypted backups. | globals/age-pubkey.txt |
|
||||
| [`openbao`](deployments/openbao/) | Hardened tape-encryption key store for **Kanrisha** (the LTO tape-archive system — separate repo/host). **Exception: native TLS on the LAN, no Caddy/LE.** raft storage, mlock, self-signed or Smallstep-ACME cert, manual/HSM unseal. | globals/age-pubkey.txt (backups) |
|
||||
| [`ergo`](deployments/ergo/) | IRC server ([Ergo](https://ergo.chat)) with Caddy for Let's Encrypt + the websocket endpoint. **Both containers use host networking** (real client IPs for bans/cloaking; the host firewall applies). Ships `ergoctl` (admin CLI) and a health-checked updater that also syncs Caddy's cert into Ergo. | — |
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Alpine + Docker Compose + Caddy/Let's Encrypt** across every stack, with two
|
||||
exceptions: `squid` (a forward proxy with a local TLS-interception CA) and
|
||||
`openbao` (a same-LAN secrets store with native TLS) — neither uses Caddy/LE.
|
||||
`ergo` keeps Caddy/LE but runs both containers in the **host network
|
||||
namespace** (IRC needs real client IPs), so it has no published ports.
|
||||
- **`build.sh` → `deploy.sh`**: each stack's `build.sh` embeds its
|
||||
`docker-compose.yml` / `Caddyfile` / `.env.example` into a single
|
||||
self-contained `deploy.sh` (base64 tar.gz). That one file can be `scp`'d to a
|
||||
@@ -154,8 +157,8 @@ instance user-data, and the host configures itself on first boot.
|
||||
## Multi-OS notes
|
||||
|
||||
The host-provisioning scripts (`setup-host`, `harden-ssh`, `harden-jumphost`,
|
||||
`sshuser`) and the eight Docker stacks (pocket-id, beszel, headscale, webfinger,
|
||||
squid, copyparty, simplex, openbao) run on Alpine, Debian, and Alma. Distro differences live in
|
||||
`sshuser`) and the nine Docker stacks (pocket-id, beszel, headscale, webfinger,
|
||||
squid, copyparty, simplex, openbao, ergo) run on Alpine, Debian, and Alma. Distro differences live in
|
||||
[`scripts/oslib.sh`](scripts/oslib.sh) — package manager (`apk`/`apt`/`dnf`),
|
||||
init system (OpenRC/systemd), sshd service name, the per-distro `sftp-server`
|
||||
path, hostname, boot hooks, and the sshguard log source/backend.
|
||||
@@ -199,7 +202,9 @@ networking is unaffected. The harden scripts and `cloud-init/base.yml` /
|
||||
- **Docker caveat** — containers published with `-p` (e.g. Caddy's 80/443)
|
||||
reach the host through nat/`FORWARD` and **bypass `INPUT`**, so the firewall
|
||||
neither blocks nor needs to open them; the per-stack rule files are
|
||||
belt-and-braces for any host-bound bind and self-documentation.
|
||||
belt-and-braces for any host-bound bind and self-documentation. The one
|
||||
stack that runs with `network_mode: host` (`ergo`) binds on the host
|
||||
directly, so there the registered ports are the real gate.
|
||||
- **Recovery** — `harden-firewall.sh disable` un-locks you: on iptables it
|
||||
flushes the rules and sets `INPUT` back to `ACCEPT` (persisted); on firewalld it
|
||||
re-opens SSH (the `ssh` service + the configured port) and leaves firewalld
|
||||
|
||||
Reference in New Issue
Block a user