-
Notifications
You must be signed in to change notification settings - Fork 68
feat(bot-irc): TLS-only ircd deploy kit for irc.profullstack.com #751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| # irc.profullstack.com — TLS-only ircd deployment | ||
|
|
||
| Runbook for hosting the Profullstack IRC endpoint on a DigitalOcean droplet | ||
| using [Ergo](https://github.com/ergochat/ergo) (single-binary Go ircd). | ||
|
|
||
| **Design goal: accept only SSL/TLS connections.** This is enforced *not* by DNS | ||
| (a hostname carries no port) but by binding a single TLS listener on `6697` and | ||
| never opening the cleartext `6667` port — at the firewall and in the ircd config. | ||
|
|
||
| Pinned: Ergo **v2.18.0**. | ||
|
|
||
| --- | ||
|
|
||
| ## 1. DNS | ||
|
|
||
| ``` | ||
| A irc.profullstack.com -> <droplet-ipv4> | ||
| AAAA irc.profullstack.com -> <droplet-ipv6> # optional | ||
| ``` | ||
|
|
||
| ## 2. Firewall (this is what makes it SSL-only at the network edge) | ||
|
|
||
| ```bash | ||
| ufw allow 22/tcp | ||
| ufw allow 6697/tcp # IRC over TLS | ||
| ufw allow 80/tcp # ONLY needed during cert issuance/renewal (http-01) | ||
| ufw enable | ||
| # 6667 is never opened -> plaintext IRC is unreachable from the internet | ||
| ``` | ||
|
|
||
| ## 3. TLS certificate (Let's Encrypt) | ||
|
|
||
| ```bash | ||
| apt-get update && apt-get install -y certbot | ||
| certbot certonly --standalone -d irc.profullstack.com | ||
| # -> /etc/letsencrypt/live/irc.profullstack.com/{fullchain,privkey}.pem | ||
| ``` | ||
|
|
||
| Install the renewal hook so Ergo reloads its cert after each renewal: | ||
|
|
||
| ```bash | ||
| install -m 0755 certbot-deploy-hook.sh \ | ||
| /etc/letsencrypt/renewal-hooks/deploy/reload-ergo.sh | ||
| ``` | ||
|
|
||
| ## 4. Install Ergo | ||
|
|
||
| ```bash | ||
| useradd -r -s /usr/sbin/nologin ergo || true | ||
| mkdir -p /opt/ergo | ||
| cd /opt/ergo | ||
| VER=v2.18.0 | ||
| curl -fsSL "https://github.com/ergochat/ergo/releases/download/${VER}/ergo-${VER}-linux-x86_64.tar.gz" \ | ||
| | tar xz --strip-components=1 | ||
| cp default.yaml ircd.yaml # start from the shipped default, then apply §5 | ||
|
Comment on lines
+52
to
+55
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The |
||
| ``` | ||
|
|
||
| Give the `ergo` user read access to the certs: | ||
|
|
||
| ```bash | ||
| # certs are root-only by default; a group read grant is the least-privilege option | ||
| groupadd -f tls-cert | ||
| usermod -aG tls-cert ergo | ||
| setfacl -R -m g:tls-cert:rX /etc/letsencrypt/live /etc/letsencrypt/archive | ||
| ``` | ||
|
|
||
| ## 5. ircd.yaml overlay (the SSL-only bit) | ||
|
|
||
| Edit `/opt/ergo/ircd.yaml`. The only changes that matter for this goal are the | ||
| server name and the **listeners** block — define one TLS listener on `6697` and | ||
| remove the default plaintext `:6667` listener entirely: | ||
|
|
||
| ```yaml | ||
| server: | ||
| name: irc.profullstack.com | ||
|
|
||
| listeners: | ||
| # NO ":6667" entry. The plaintext socket simply does not exist. | ||
| ":6697": | ||
| tls: | ||
| cert: /etc/letsencrypt/live/irc.profullstack.com/fullchain.pem | ||
| key: /etc/letsencrypt/live/irc.profullstack.com/privkey.pem | ||
| # optional hardening: | ||
| # min-tls-version: 1.2 | ||
|
|
||
| # If you ever bind a localhost-only plaintext listener for an internal bot, | ||
| # restrict it to loopback so it is never exposed: | ||
| # "127.0.0.1:6667": {} | ||
| ``` | ||
|
|
||
| Do **not** use STARTTLS on a cleartext port — it is vulnerable to TLS-stripping. | ||
| Implicit TLS on 6697 with no 6667 is strictly simpler and safer. | ||
|
|
||
| Smoke-test the config in the foreground before wiring up systemd (Ergo | ||
| auto-creates its datastore on first run — no `initdb` step needed): | ||
|
|
||
| ```bash | ||
| sudo -u ergo /opt/ergo/ergo run --conf /opt/ergo/ircd.yaml | ||
| # watch for "listening on" :6697 and no TLS errors, then Ctrl-C | ||
| ``` | ||
|
|
||
| ## 6. systemd | ||
|
|
||
| ```bash | ||
| cp ergo.service /etc/systemd/system/ergo.service | ||
| systemctl daemon-reload | ||
| systemctl enable --now ergo | ||
| systemctl status ergo | ||
| ``` | ||
|
|
||
| ## 7. Verify it is TLS-only | ||
|
|
||
| ```bash | ||
| # TLS handshake succeeds: | ||
| openssl s_client -connect irc.profullstack.com:6697 -servername irc.profullstack.com </dev/null 2>/dev/null | head | ||
|
|
||
| # plaintext is refused / times out (no listener, firewall closed): | ||
| nc -vz -w5 irc.profullstack.com 6667 # expect: connection refused / timed out | ||
| ``` | ||
|
|
||
| ## 8. Connect sh1pt's IRC bot | ||
|
|
||
| `@profullstack/sh1pt-bot-irc` already supports implicit TLS — set `tls: true` | ||
| and the port defaults to 6697 (see `../src/index.ts`): | ||
|
|
||
| ```ts | ||
| import { IrcBot } from "@profullstack/sh1pt-bot-irc"; | ||
|
|
||
| const bot = new IrcBot({ | ||
| server: "irc.profullstack.com", | ||
| tls: true, // port omitted -> 6697 | ||
| nick: "sh1pt", | ||
| channels: ["#sh1pt"], | ||
| }); | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #!/usr/bin/env bash | ||
| # Installed to /etc/letsencrypt/renewal-hooks/deploy/reload-ergo.sh | ||
| # certbot runs every deploy hook after a successful renewal. Reload Ergo so it | ||
| # picks up the new cert without dropping client connections (SIGHUP = rehash). | ||
| set -euo pipefail | ||
|
|
||
| # Only act when the irc.profullstack.com cert was (re)issued. | ||
| case "${RENEWED_LINEAGE:-}" in | ||
| */irc.profullstack.com) systemctl reload ergo ;; | ||
| *) : ;; # some other cert renewed; nothing to do | ||
| esac |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| [Unit] | ||
| Description=Ergo IRC server (irc.profullstack.com) | ||
| After=network-online.target | ||
| Wants=network-online.target | ||
|
|
||
| [Service] | ||
| Type=simple | ||
| User=ergo | ||
| Group=ergo | ||
| WorkingDirectory=/opt/ergo | ||
| ExecStart=/opt/ergo/ergo run --conf /opt/ergo/ircd.yaml | ||
| ExecReload=/bin/kill -HUP $MAINPID | ||
| Restart=on-failure | ||
| RestartSec=2s | ||
| LimitNOFILE=1048576 | ||
|
|
||
| # Hardening | ||
| NoNewPrivileges=true | ||
| ProtectSystem=strict | ||
| ProtectHome=true | ||
| PrivateTmp=true | ||
| ReadWritePaths=/opt/ergo | ||
| # allow reading the Let's Encrypt cert tree (symlinks live -> archive) | ||
| ReadOnlyPaths=/etc/letsencrypt | ||
|
|
||
| [Install] | ||
| WantedBy=multi-user.target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing the
pnpmVersioninput is a backward-incompatible change: any consumer that currently passespnpmVersionin their action invocation will have that input silently dropped (or error, depending on the action runner). Under semver this warrants a major version bump (2.0.0), not 1.1.0. The same applies tonode-pnpm-test. Existing users who discover the upgrade path from a changelog or registry entry will assume 1.x → 1.1.0 is safe to adopt.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!