mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 14:27:27 +00:00
feat(irc): irc.profullstack.com host, OS-user (tilde.town) gate, premium channels
Hostname: serve the network as irc.profullstack.com (new IRC_DOMAIN var, default irc.<root-of-DOMAIN>). Caddy serves an irc.profullstack.com site so it gets a Let's Encrypt cert; ergo-refresh-certs copies that into Ergo for 6697. Needs an A record irc.profullstack.com -> the box (self-signed until it resolves). Members are OS users (tilde.town model): setup.sh reconciles a real OS account per member dir (root-side, on each deploy + the 15-min timer; nologin shell, so identity-only — no shell access). The IRC auth-script now gates on `getent passwd` with uid>=1000 instead of the member dir, so "OS user" == member. Premium channels: free members may /join; creating channels is a premium perk. The ssh irc@ client gains /create #name (premium-gated via ensurePremium): it joins the fresh channel and registers it with ChanServ as the member's founder. v1 gate is route-level (operator-only-creation left off); external-client creation hardening is a follow-up. See docs/irc.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d4ada98b69
commit
5eb1e96480
5 changed files with 194 additions and 87 deletions
|
|
@ -4,21 +4,22 @@
|
|||
# membership. setup.sh installs this to /usr/local/bin/ergo-auth-member and
|
||||
# wires it into /etc/ergo/ircd.yaml (accounts.auth-script).
|
||||
#
|
||||
# "Member" == a user with a home dir under the AgentBBS users dir (created when
|
||||
# someone registers via `ssh join@`). IRC is members-only, so a login is
|
||||
# approved iff the requested account name maps to such a dir. The passphrase is
|
||||
# intentionally IGNORED — membership (a filesystem dir) IS the credential, by
|
||||
# design (see docs/irc.md). Anyone who knows a member's name can connect as
|
||||
# them; that tradeoff was chosen deliberately for this private, TLS-only network.
|
||||
# AgentBBS members are real OS users (tilde.town model; setup.sh provisions an
|
||||
# OS account per member). IRC is members-only, so a login is approved iff the
|
||||
# requested account name is a real OS user with uid >= MIN_UID (which excludes
|
||||
# system accounts like root/ergo/agentbbs). The passphrase is intentionally
|
||||
# IGNORED — membership (being an OS user) IS the credential, by design (see
|
||||
# docs/irc.md). Anyone who knows a member's name can connect as them; that
|
||||
# tradeoff was chosen deliberately for this private, TLS-only network.
|
||||
#
|
||||
# Protocol (Ergo): one JSON object on stdin per attempt, one JSON line on stdout
|
||||
# then exit. Input keys: accountName, passphrase, certfp, ip. Output:
|
||||
# {"success":bool,"accountName":str,"error":str}.
|
||||
#
|
||||
# args: ["<users-dir>"] # defaults to /var/lib/agentbbs/users
|
||||
# args: ["<min-uid>"] # defaults to 1000
|
||||
set -uo pipefail
|
||||
|
||||
USERS_DIR="${1:-/var/lib/agentbbs/users}"
|
||||
MIN_UID="${1:-1000}"
|
||||
|
||||
# Always emit valid JSON and exit 0 — Ergo reads the JSON, not the exit code;
|
||||
# a non-zero exit / no output is treated as a script error, not a clean deny.
|
||||
|
|
@ -35,14 +36,22 @@ acct="$(printf '%s' "$line" | jq -r '.accountName // ""' 2>/dev/null || true)"
|
|||
# certfp-only attempts carry no account name; we don't support cert auth here.
|
||||
[ -n "$acct" ] || deny "membership requires an account name"
|
||||
|
||||
# Defense in depth against path traversal. IRC account names are a restricted
|
||||
# charset anyway, but never let one escape USERS_DIR.
|
||||
# Restrict to plain login names (defense in depth; IRC names are limited anyway).
|
||||
case "$acct" in
|
||||
*[!A-Za-z0-9._-]* | "." | ".." | *..* | */* ) deny "invalid account name" ;;
|
||||
*[!A-Za-z0-9._-]* | "." | ".." ) deny "invalid account name" ;;
|
||||
esac
|
||||
|
||||
if [ -d "$USERS_DIR/$acct" ]; then
|
||||
# Resolve the OS account; getent passwd returns name:passwd:uid:gid:...
|
||||
entry="$(getent passwd "$acct" 2>/dev/null || true)"
|
||||
[ -n "$entry" ] || deny "not a member"
|
||||
|
||||
uid="$(printf '%s' "$entry" | cut -d: -f3)"
|
||||
case "$uid" in
|
||||
''|*[!0-9]*) deny "not a member" ;;
|
||||
esac
|
||||
|
||||
if [ "$uid" -ge "$MIN_UID" ]; then
|
||||
printf '{"success":true,"accountName":"%s"}\n' "$acct"
|
||||
else
|
||||
deny "not a member"
|
||||
deny "system accounts cannot use IRC"
|
||||
fi
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue