diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..ac652a20f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,48 @@ +# Build artifacts +*.o +*.so +*.a +*.lo +*.dylib +src/ircd +src/unrealircdctl +src/modules/*.so +src/modules/**/*.o + +# Generated config +config.log +config.status +config.cache +autom4te.cache +config.h.in~ +src/Makefile +src/modules/Makefile +src/modules/*/Makefile +src/aclocal.m4 + +# Local working state +config.settings +*.bak +*.swp + +# Cloned client checkout used during cross-repo development +ObsidianIRC/ + +# Bundled .deb produced by Tauri builds +*.deb +*.rpm +*.AppImage + +# CI / IDE noise +.git +.github/workflows +.vscode +.idea +.claude + +# Doc artifacts +doc/doxygen/ + +# Existing Docker output +/docker-compose.override.yml +.env diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..d2c354826 --- /dev/null +++ b/.env.example @@ -0,0 +1,58 @@ +# Copy to .env and edit before running `docker compose up -d`. + +# --- identity --- +SERVER_NAME=irc.example.com +NETWORK_NAME=ObbyNetwork +ADMIN_EMAIL=admin@example.com +MOTD_TEXT=Welcome to ObbyNetwork! + +# --- oper credentials --- +# OPER_NAME / OPER_PASSWORD become the network admin's IRC oper login. +# If OPER_PASSWORD is unset, a random one is generated on first run and +# saved to data/.oper_password (printed to the container log once). +# OPER_MASK restricts where the oper can /OPER from; "*" allows any +# host -- tighten this in production (e.g. *@10.0.0.0/8 or *@bastion). +OPER_NAME=admin +OPER_PASSWORD= +OPER_MASK=* + +# --- ports --- +# SSL_PORT is the port inside the container. SSL_HOST_PORT is the +# port the host publishes; set them differently if you want a custom +# external port. +SSL_PORT=6697 +SSL_HOST_PORT=6697 + +# Plain WebSocket -- always listens internally. Put a TLS-terminating +# reverse proxy in front for production (nginx, Traefik, Caddy). +WS_PORT=8080 +WS_HOST_PORT=8080 + +# --- optional features --- +# Set FILEHOST_URL to enable the filehost { host "..."; }; block used +# by the filehost module for link-preview image rehosting. +FILEHOST_URL= + +# Set RPC_PASSWORD to enable JSON-RPC over TCP. RPC_PORT is internal +# only; route via reverse proxy with TLS to expose it externally. +RPC_PASSWORD= +RPC_PORT=8600 + +# --- cloak keys --- +# Persistent cloak keys. If unset, random keys are generated on every +# fresh conf volume -- fine for a single-node deployment but bad for a +# linked network where cloaks should be stable across restarts and +# servers. Generate with: head -c 32 /dev/urandom | xxd -p -c 64 +CLOAK_KEY1= +CLOAK_KEY2= +CLOAK_KEY3= + +# --- bind-mount overrides (optional) --- +# Set any of these to an absolute host path to use a bind mount +# instead of the named volume. Useful when you need to know exactly +# where the data lives on disk (cert sync, off-host backups). +# CONF_BIND=/srv/obbyircd/conf +# DATA_BIND=/srv/obbyircd/data +# LOGS_BIND=/srv/obbyircd/logs +# TLS_BIND=/srv/obbyircd/tls +# CUSTOM_MODULES_BIND=/srv/obbyircd/custom-modules diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 000000000..a2075edbf --- /dev/null +++ b/compose.yaml @@ -0,0 +1,38 @@ +services: + obbyircd: + build: + context: . + dockerfile: docker/Dockerfile + image: obbyircd:local + container_name: obbyircd + restart: unless-stopped + ports: + - "${SSL_HOST_PORT:-6697}:${SSL_PORT:-6697}" + - "${WS_HOST_PORT:-8080}:${WS_PORT:-8080}" + volumes: + - ${CONF_BIND:-obbyircd_conf}:/home/obbyircd/obby/conf + - ${DATA_BIND:-obbyircd_data}:/home/obbyircd/obby/data + - ${LOGS_BIND:-obbyircd_logs}:/home/obbyircd/obby/logs + - ${TLS_BIND:-obbyircd_tls}:/home/obbyircd/obby/tls + - ${CUSTOM_MODULES_BIND:-obbyircd_custom_modules}:/home/obbyircd/obby/custom-modules + env_file: + - path: .env + required: false + healthcheck: + test: ["CMD-SHELL", "nc -z localhost ${SSL_PORT:-6697}"] + interval: 5s + timeout: 5s + retries: 6 + start_period: 15s + +volumes: + obbyircd_conf: + driver: local + obbyircd_data: + driver: local + obbyircd_logs: + driver: local + obbyircd_tls: + driver: local + obbyircd_custom_modules: + driver: local diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..c72fa1001 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,129 @@ +# syntax=docker/dockerfile:1.6 +# +# ObbyIRCd Docker image. Builds the IRCd straight from this repository, +# so every native module shipped with the fork (account-registration, +# persistence, metadata, filehost, authtoken, smtp, obbyscript, ...) is +# available out of the box. +# +# Build: docker build -f docker/Dockerfile -t obbyircd . +# Compose: docker compose up -d + +FROM alpine:3.19 + +# Build + runtime dependencies. sqlite-dev is needed by the +# account-registration module; the rest cover the core IRCd, TLS, JSON +# logging, PCRE2 regexes, argon2 password hashing, libsodium for the +# RPC subsystem and gettext for the entrypoint's envsubst. +RUN apk add --no-cache \ + build-base \ + openssl-dev \ + openssl \ + pcre2-dev \ + argon2-dev \ + libsodium-dev \ + jansson-dev \ + curl-dev \ + sqlite-dev \ + pkgconf \ + wget \ + tar \ + gettext \ + su-exec \ + netcat-openbsd \ + && rm -rf /var/cache/apk/* + +RUN adduser -D -s /bin/sh obbyircd + +# Copy the entire repository in. .dockerignore keeps build artifacts, +# the ObsidianIRC client checkout, and other dev junk out. +WORKDIR /tmp/obbyircd-source +COPY --chown=obbyircd:obbyircd . . + +# Pre-seed config.settings so ./Config -quick can build non-interactively. +RUN install -d -o obbyircd -g obbyircd \ + /home/obbyircd/obby \ + /home/obbyircd/obby/bin \ + /home/obbyircd/obby/data \ + /home/obbyircd/obby/conf \ + /home/obbyircd/obby/modules \ + /home/obbyircd/obby/logs \ + /home/obbyircd/obby/cache \ + /home/obbyircd/obby/doc \ + /home/obbyircd/obby/tmp \ + /home/obbyircd/obby/lib \ + && cat > /tmp/obbyircd-source/config.settings <<'EOF' +BASEPATH="/home/obbyircd/obby" +BINDIR="/home/obbyircd/obby/bin" +DATADIR="/home/obbyircd/obby/data" +CONFDIR="/home/obbyircd/obby/conf" +MODULESDIR="/home/obbyircd/obby/modules" +LOGDIR="/home/obbyircd/obby/logs" +CACHEDIR="/home/obbyircd/obby/cache" +DOCDIR="/home/obbyircd/obby/doc" +TMPDIR="/home/obbyircd/obby/tmp" +PRIVATELIBDIR="/home/obbyircd/obby/lib" +MAXCONNECTIONS_REQUEST="auto" +NICKNAMEHISTORYLENGTH="2000" +GEOIP="none" +DEFPERM="0600" +SSLDIR="" +REMOTEINC="" +CURLDIR="" +NOOPEROVERRIDE="" +OPEROVERRIDEVERIFY="" +GENCERTIFICATE="0" +SANITIZER="" +EXTRAPARA="" +ADVANCED="" +EOF + +RUN chmod +x Config \ + && chown -R obbyircd:obbyircd /tmp/obbyircd-source /home/obbyircd + +USER obbyircd +RUN ./Config -quick && make -j"$(nproc)" && make install + +USER root + +# Snapshot the freshly-installed conf tree somewhere outside the volume +# mount points so first-run can repopulate a fresh volume. `make +# install` doesn't ship doc/conf/scripts/, so copy the obbyscript +# example separately -- the entrypoint also `mkdir -p`s the scripts +# directory on every start in case the operator wiped it. +RUN mkdir -p /etc/obbyircd/conf-defaults \ + && cp -r /home/obbyircd/obby/conf/. /etc/obbyircd/conf-defaults/ \ + && mkdir -p /etc/obbyircd/conf-defaults/scripts \ + && cp -n /tmp/obbyircd-source/doc/conf/scripts/*.js \ + /etc/obbyircd/conf-defaults/scripts/ 2>/dev/null || true \ + && chmod -R a+rX /etc/obbyircd + +RUN mkdir -p /home/obbyircd/obby/conf \ + /home/obbyircd/obby/data \ + /home/obbyircd/obby/logs \ + /home/obbyircd/obby/tls \ + /home/obbyircd/obby/tmp \ + /home/obbyircd/obby/custom-modules \ + && chown -R obbyircd:obbyircd /home/obbyircd + +COPY docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +COPY docker/obbyircd.conf.template /etc/obbyircd/obbyircd.conf.template +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +ENV SERVER_NAME=irc.example.com \ + NETWORK_NAME=ObbyNetwork \ + ADMIN_EMAIL=admin@example.com \ + SSL_PORT=6697 \ + WS_PORT=8080 \ + RPC_PORT=8600 \ + MOTD_TEXT="Welcome to ObbyIRCd!" + +VOLUME ["/home/obbyircd/obby/conf", \ + "/home/obbyircd/obby/data", \ + "/home/obbyircd/obby/logs", \ + "/home/obbyircd/obby/tls", \ + "/home/obbyircd/obby/custom-modules"] + +WORKDIR /home/obbyircd/obby + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["./bin/obbyircd", "-F"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..0961ca04b --- /dev/null +++ b/docker/README.md @@ -0,0 +1,135 @@ +# ObbyIRCd Docker + +Self-contained image of this fork built straight from the local +source tree. Every native module ObbyIRCd ships with -- account +registration, persistence, metadata, filehost, authtoken, smtp, +obbyscript, ... -- is compiled into the image and ready to load. + +## Quick start + +```bash +cp .env.example .env +# edit .env (SERVER_NAME, NETWORK_NAME, ADMIN_EMAIL, ports, ...) +docker compose up -d +docker compose logs -f obbyircd +``` + +The first time the conf volume is empty, the entrypoint: + +1. Copies `modules.default.conf` and the rest of the bundled conf + files into `/home/obbyircd/obby/conf/`. +2. Renders `obbyircd.conf` from `docker/obbyircd.conf.template` by + substituting environment variables. +3. Issues a self-signed TLS cert valid for 1 day (testing only -- + replace with a real cert before any real traffic). +4. Generates random cloak keys if `CLOAK_KEY1/2/3` weren't supplied. + +Subsequent runs reuse whatever is on disk. Delete +`conf/.docker_initialized` (or remove the conf volume entirely) to +trigger a fresh init. + +## Connecting + +* **SSL/TLS IRC:** `ircs://:6697` +* **WebSocket:** `ws://:8080` -- plaintext. Front this with a + TLS-terminating reverse proxy (nginx / Traefik / Caddy) for + production. + +The default oper credentials in the rendered conf are +`admin / admin123`. Change them before exposing the server. + +## Volumes + +| Volume | Container path | Purpose | +|---------------------------|-------------------------------------------|---------| +| `obbyircd_conf` | `/home/obbyircd/obby/conf` | Configuration. | +| `obbyircd_data` | `/home/obbyircd/obby/data` | Persistent state: account DB (`obsidian.db`), TKL bans, channeldb, persistence ghosts, RPC socket. | +| `obbyircd_logs` | `/home/obbyircd/obby/logs` | Server logs. | +| `obbyircd_tls` | `/home/obbyircd/obby/tls` | `server.cert.pem` + `server.key.pem`. | +| `obbyircd_custom_modules` | `/home/obbyircd/obby/custom-modules` | Drop `.c` files here to compile and load extra third-party modules without rebuilding the image. | + +Set the corresponding `*_BIND` env var in `.env` to an absolute host +path to use a bind mount instead of a named volume. + +## Environment variables + +| Variable | Default | Notes | +|-------------------|------------------------|-------| +| `SERVER_NAME` | `irc.example.com` | The hostname clients connect to. | +| `NETWORK_NAME` | `ObbyNetwork` | Shown in MOTD / `005`. | +| `ADMIN_EMAIL` | `admin@example.com` | Used in the `admin {}` block and `kline-address`. | +| `MOTD_TEXT` | `Welcome to ObbyIRCd!` | Currently informational; the real MOTD lives in `conf/motd.txt`. | +| `OPER_NAME` | `admin` | Network admin oper login. | +| `OPER_PASSWORD` | *(generated)* | If unset, a random secret is generated on first run and persisted to `data/.oper_password` (printed once to the container log). | +| `OPER_MASK` | `*` | Hostmask the oper can /OPER from. Tighten this in production. | +| `SSL_PORT` | `6697` | Internal TLS port. | +| `SSL_HOST_PORT` | `6697` | Host-side port mapping. | +| `WS_PORT` | `8080` | Internal plain WebSocket port. | +| `WS_HOST_PORT` | `8080` | Host-side port mapping. | +| `FILEHOST_URL` | *(unset)* | Set to enable the `filehost {}` block. | +| `RPC_PASSWORD` | *(unset)* | Set to enable JSON-RPC over `RPC_PORT`. | +| `RPC_PORT` | `8600` | Internal only. | +| `CLOAK_KEY1/2/3` | *(generated)* | Set explicitly for stable cloaks across restarts / linked nodes. | + +## obbyscript scripts + +The `obbyscript` module (built in, loaded by `modules.default.conf`) +auto-loads every `.js` file in `conf/scripts/`. The image ships +`scripts/example.js` -- a reference script that registers a `/JSINFO` +command and demonstrates command/event hooks -- as a starting point. + +To add or replace scripts at runtime: + +```bash +docker compose cp my-script.js obbyircd:/home/obbyircd/obby/conf/scripts/ +docker compose exec obbyircd ./bin/obbyircd rehash +# or restart the container if rehash isn't enough for your script's hooks +``` + +If you don't want any scripts loading, delete the contents of +`conf/scripts/` (or remove `loadmodule "obbyscript";` from +`obbyircd.conf`). + +## Custom modules at runtime + +`/home/obbyircd/obby/custom-modules/*.c` is scanned at every container +start; each `.c` file is compiled with the source tree headers from +`/tmp/obbyircd-source/` and installed as `modules/third/.so`. +Failing modules emit a warning and don't block the server starting. + +Copy a module into the running container's volume in a portable way: + +```bash +docker compose cp my-extra-module.c obbyircd:/home/obbyircd/obby/custom-modules/ +docker compose restart obbyircd +# then add `loadmodule "third/my-extra-module";` to obbyircd.conf and: +docker compose exec obbyircd ./bin/obbyircd rehash +``` + +If you'd rather edit modules directly from the host, set +`CUSTOM_MODULES_BIND` in `.env` to a host directory and bind-mount the +volume there: + +```bash +CUSTOM_MODULES_BIND=/srv/obbyircd/custom-modules +# then on the host: +cp my-extra-module.c /srv/obbyircd/custom-modules/ +docker compose restart obbyircd +``` + +## Replacing the TLS cert + +```bash +docker compose cp /path/to/fullchain.pem obbyircd:/home/obbyircd/obby/tls/server.cert.pem +docker compose cp /path/to/privkey.pem obbyircd:/home/obbyircd/obby/tls/server.key.pem +docker compose restart obbyircd +``` + +(Or, if you used `TLS_BIND` to mount the directory from the host, just +overwrite the files there.) + +## Building manually + +```bash +docker build -f docker/Dockerfile -t obbyircd:local . +``` diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100644 index 000000000..f49973e9b --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,197 @@ +#!/bin/sh +# Entrypoint for the ObbyIRCd Docker image. +# +# On first start (when the conf volume is empty) the conf is generated +# from /etc/obbyircd/obbyircd.conf.template by substituting environment +# variables, a self-signed TLS cert is issued (1 day, testing only) and +# random cloak keys are produced. On subsequent starts the existing +# state is left alone. +# +# Drop *.c files into the custom-modules volume to have them compiled +# at startup against this image's source tree and installed as +# third/.so. +# +set -eu + +export SERVER_NAME="${SERVER_NAME:-irc.example.com}" +export NETWORK_NAME="${NETWORK_NAME:-ObbyNetwork}" +export ADMIN_EMAIL="${ADMIN_EMAIL:-admin@example.com}" +export SSL_PORT="${SSL_PORT:-6697}" +export WS_PORT="${WS_PORT:-8080}" +export RPC_PORT="${RPC_PORT:-8600}" +export RPC_PASSWORD="${RPC_PASSWORD:-}" +export FILEHOST_URL="${FILEHOST_URL:-}" +export MOTD_TEXT="${MOTD_TEXT:-Welcome to ObbyIRCd!}" +export OPER_NAME="${OPER_NAME:-admin}" +export OPER_PASSWORD="${OPER_PASSWORD:-}" +# OPER_MASK restricts where the oper can authenticate from. Default * +# is permissive; override for production deployments. +export OPER_MASK="${OPER_MASK:-*}" + +CONF_DIR="/home/obbyircd/obby/conf" +DATA_DIR="/home/obbyircd/obby/data" +TLS_DIR="/home/obbyircd/obby/tls" +LOGS_DIR="/home/obbyircd/obby/logs" +TEMPLATE_FILE="/etc/obbyircd/obbyircd.conf.template" +CONFIG_FILE="$CONF_DIR/obbyircd.conf" +CUSTOM_MOD_DIR="/home/obbyircd/obby/custom-modules" +SOURCE_TREE="/tmp/obbyircd-source" +export TLS_DIR + +echo "Starting ObbyIRCd container..." +echo "Server: $SERVER_NAME" +echo "Network: $NETWORK_NAME" + +# WebSocket listener block (always emitted; use plaintext in container, +# put a TLS-terminating reverse proxy in front for production). +if [ -n "$WS_PORT" ]; then + export WS_CONFIG="loadmodule \"webserver\"; +loadmodule \"websocket\"; +listen { + ip *; + port $WS_PORT; + options { websocket { type text; }; }; +};" +else + export WS_CONFIG="" +fi + +# Optional filehost block. +if [ -n "$FILEHOST_URL" ]; then + export FILEHOST_CONFIG="filehosts { host \"$FILEHOST_URL\"; };" + echo "Filehost: $FILEHOST_URL" +else + export FILEHOST_CONFIG="" +fi + +# Optional JSON-RPC. Off by default; set RPC_PASSWORD to enable. +if [ -n "$RPC_PASSWORD" ]; then + export RPC_CONFIG="include \"rpc.modules.default.conf\"; +listen { ip *; port $RPC_PORT; options { rpc; }; }; +rpc-user admin { match { ip *; } rpc-class full; password \"$RPC_PASSWORD\"; };" + echo "RPC: enabled on port $RPC_PORT" +else + export RPC_CONFIG="" +fi + +# Random cloak keys if the operator didn't supply any. Operators +# running multiple servers should set CLOAK_KEY1/2/3 in .env so all +# nodes agree. +if [ -z "${CLOAK_KEY1:-}" ] || [ -z "${CLOAK_KEY2:-}" ] || [ -z "${CLOAK_KEY3:-}" ]; then + echo "Generating random cloak keys (set CLOAK_KEY1/2/3 in .env to fix them)" + CLOAK_KEY1=$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n') + CLOAK_KEY2=$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n') + CLOAK_KEY3=$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n') +fi +export CLOAK_KEY1 CLOAK_KEY2 CLOAK_KEY3 + +# TLS cert: idempotent. Lives in its own volume, so a fresh tls +# volume must self-heal even when conf is already initialised. +if [ ! -f "$TLS_DIR/server.cert.pem" ] || [ ! -f "$TLS_DIR/server.key.pem" ]; then + echo "Issuing a self-signed TLS cert (valid 1 day -- replace with a real one for production)" + openssl req -x509 -nodes -newkey rsa:2048 -days 1 \ + -keyout "$TLS_DIR/server.key.pem" \ + -out "$TLS_DIR/server.cert.pem" \ + -subj "/CN=$SERVER_NAME" \ + >/dev/null 2>&1 + chmod 600 "$TLS_DIR/server.key.pem" + chmod 644 "$TLS_DIR/server.cert.pem" +fi + +# Oper password: required. Generate a random one on first run if the +# operator didn't supply one and persist it so subsequent restarts +# don't change behind their back. This avoids ever shipping a known +# default like "admin123". +OPER_PASSWORD_FILE="$DATA_DIR/.oper_password" +if [ -z "$OPER_PASSWORD" ]; then + if [ -f "$OPER_PASSWORD_FILE" ]; then + OPER_PASSWORD=$(cat "$OPER_PASSWORD_FILE") + echo "Loaded oper password from $OPER_PASSWORD_FILE" + else + OPER_PASSWORD=$(head -c 18 /dev/urandom | od -An -tx1 | tr -d ' \n') + printf '%s' "$OPER_PASSWORD" > "$OPER_PASSWORD_FILE" + chmod 600 "$OPER_PASSWORD_FILE" + echo "" + echo "===========================================================" + echo "Generated oper credentials (saved to $OPER_PASSWORD_FILE):" + echo " /OPER $OPER_NAME $OPER_PASSWORD" + echo "Set OPER_PASSWORD in .env to a fixed value for stable creds." + echo "===========================================================" + echo "" + fi +fi +export OPER_PASSWORD + +FIRST_RUN_MARKER="$CONF_DIR/.docker_initialized" +if [ ! -f "$FIRST_RUN_MARKER" ]; then + echo "Empty conf volume detected -- populating from /etc/obbyircd/conf-defaults/" + cp -r /etc/obbyircd/conf-defaults/. "$CONF_DIR/" + + # Render to a temp file first; promote only if validation passes + # below. Without this, a bad render would still create the + # marker and lock the operator out of re-rendering after fixing + # the offending env var. + envsubst < "$TEMPLATE_FILE" > "$CONFIG_FILE.tmp" + mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" + + chown -R obbyircd:obbyircd "$CONF_DIR" "$TLS_DIR" "$DATA_DIR" "$LOGS_DIR" + echo "Generated configuration (validation pending)" +else + echo "Reusing existing config (delete $FIRST_RUN_MARKER to regenerate)" +fi + +# obbyscript autoloads every *.js it finds in $CONFDIR/scripts/. +# Make sure the directory exists so the module doesn't log a warning +# on every restart even when the operator hasn't placed any scripts. +mkdir -p "$CONF_DIR/scripts" + +# Always make sure the unprivileged user owns the runtime dirs -- +# fixes the case where someone bind-mounts a host directory the first +# time and root-owned conf files get created during a previous run. +chown obbyircd:obbyircd "$DATA_DIR" "$LOGS_DIR" "$TLS_DIR" 2>/dev/null || true +chown obbyircd:obbyircd "$CONF_DIR/scripts" 2>/dev/null || true +chown obbyircd:obbyircd "$OPER_PASSWORD_FILE" 2>/dev/null || true + +# Compile any user-dropped custom modules. Failures are logged and +# the server still starts. +if [ -d "$CUSTOM_MOD_DIR" ]; then + for src in "$CUSTOM_MOD_DIR"/*.c; do + [ -f "$src" ] || continue + modname=$(basename "$src" .c) + out="/home/obbyircd/obby/modules/third/${modname}.so" + echo "Compiling custom module: $modname" + if su-exec obbyircd gcc -shared -fPIC -DPIC -DDYNAMIC_LINKING \ + -Wl,-export-dynamic -Wl,-z,relro -Wl,-z,now \ + -o "$out" "$src" \ + -I"$SOURCE_TREE/include" -I"$SOURCE_TREE" \ + $(pkg-config --cflags openssl 2>/dev/null || true); then + echo " -> $out" + else + echo " WARNING: $modname failed to compile, skipping" + fi + done +fi + +cd /home/obbyircd/obby + +echo "Validating configuration..." +if ! su-exec obbyircd ./bin/obbyircd -c; then + echo "ERROR: configuration validation failed (see output above)" + echo "Fix the offending environment variable and restart -- the" + echo "container will re-render the conf because no init marker has" + echo "been written yet." + exit 1 +fi + +# Validation passed; promote first-run marker so we don't re-render +# the rendered conf on every restart. +if [ ! -f "$FIRST_RUN_MARKER" ]; then + su-exec obbyircd touch "$FIRST_RUN_MARKER" + echo "First-run initialisation complete" +fi + +echo "Starting ObbyIRCd..." +if [ "$#" -ge 2 ] && [ "$1" = "./bin/obbyircd" ] && [ "$2" = "-F" ]; then + exec su-exec obbyircd "$@" +fi +exec su-exec obbyircd "$@" diff --git a/docker/obbyircd.conf.template b/docker/obbyircd.conf.template new file mode 100644 index 000000000..943ea9f79 --- /dev/null +++ b/docker/obbyircd.conf.template @@ -0,0 +1,120 @@ +/* ObbyIRCd configuration template + * Rendered at first container start by docker-entrypoint.sh: every + * ${...} placeholder is substituted via envsubst. Do not edit this + * file inside a running container -- edit conf/obbyircd.conf directly + * or bake your changes into a fork of this template. + */ + +me { + name "${SERVER_NAME}"; + info "ObbyIRCd"; + sid "001"; +}; + +admin { + "${ADMIN_EMAIL}"; + "ObbyIRCd Administrator"; + "${NETWORK_NAME} Network"; +}; + +class clients { + pingfreq 90; + maxclients 1000; + sendq 200k; + recvq 8000; +}; + +class servers { + pingfreq 60; + maxclients 10; + sendq 20M; + connfreq 100; +}; + +allow { + mask *; + class clients; + maxperip 5; +}; + +listen { + ip *; + port ${SSL_PORT}; + options { tls; }; +}; + +oper ${OPER_NAME} { + class clients; + mask ${OPER_MASK}; + password "${OPER_PASSWORD}"; + operclass netadmin; + swhois "is a Network Administrator"; + vhost netadmin.${SERVER_NAME}; +}; + +include "modules.default.conf"; +include "operclass.default.conf"; +include "snomasks.default.conf"; +include "help/help.conf"; +include "badwords.conf"; +include "spamfilter.conf"; + +loadmodule "cloak_sha256"; + +/* WebSocket listener (always emitted -- terminate TLS at the + * reverse proxy in production). */ +${WS_CONFIG} + +/* Optional filehost {} block (set FILEHOST_URL). */ +${FILEHOST_CONFIG} + +/* Optional JSON-RPC (set RPC_PASSWORD). */ +${RPC_CONFIG} + +set { + kline-address "${ADMIN_EMAIL}"; + modes-on-connect "+iwx"; + modes-on-oper "+xwgs"; + oper-auto-join "#opers"; + options { + hide-ulines; + show-connect-info; + }; + maxchannelsperuser 20; + default-server "${SERVER_NAME}"; + network-name "${NETWORK_NAME}"; + help-channel "#help"; + hiddenhost-prefix "hidden"; + prefix-quit "Quit"; + + tls { + certificate "${TLS_DIR}/server.cert.pem"; + key "${TLS_DIR}/server.key.pem"; + }; + + cloak-keys { + "${CLOAK_KEY1}"; + "${CLOAK_KEY2}"; + "${CLOAK_KEY3}"; + }; + + default-bantime 30d; + modef-default-unsettime 60; +}; + +except ban { + mask { ip { 127.0.0.1; ::1; } } + type { connect-flood; maxperip; } +}; + +account-registration { + min-name-length 3; + max-name-length 20; + min-password-length 8; + max-password-length 200; + require-email yes; + require-terms-acceptance no; + allow-username-changes yes; + allow-password-changes yes; + allow-email-changes yes; +};