morphit/ops/systemd/morphit-matrix-bot.service

107 lines
4.6 KiB
Desktop File

[Unit]
Description=Morphit matrix-bot — tails journalctl, classifies operator alerts by tier, DMs to operator MXID via Matrix
Documentation=https://git.agorise.net/agorise/morphit
After=network-online.target morphit-indexer.service morphit-relay.service
Wants=network-online.target
[Service]
Type=simple
User=morphit-matrix-bot
Group=morphit-matrix-bot
WorkingDirectory=/opt/morphit/apps/matrix-bot
# Environment file — NEVER commit a populated version. Operator
# fills MORPHIT_MATRIX_BOT_HOMESERVER, MORPHIT_MATRIX_BOT_ACCESS_TOKEN,
# and MORPHIT_MATRIX_BOT_ALERT_MXID at deploy time. This file is the
# bot's ONLY config source — it does NOT read morphit.config.env (the
# operator-config loader). The MXID it reads is the one written here by
# `morphit-ops matrix set <mxid>`, which also enables + starts this unit;
# `morphit-ops matrix clear` empties it and stops the unit; and
# `morphit-ops upgrade` re-checks it on every upgrade.
#
# Leading `-` makes the file OPTIONAL — systemd won't refuse to
# start the unit if /etc/morphit/matrix-bot.env doesn't exist.
# In that case the bot exits 0 cleanly because no Matrix surfaces
# are configured (see apps/matrix-bot/src/main.ts opt-in gate).
EnvironmentFile=-/etc/morphit/matrix-bot.env
# tsx — same pattern as indexer/relay; no build step.
ExecStart=/usr/bin/env node --enable-source-maps \
/opt/morphit/node_modules/tsx/dist/cli.mjs src/main.ts
KillSignal=SIGTERM
TimeoutStopSec=15
# Restart on crash, BUT NOT on clean exit 0. The bot exits 0 when
# the operator hasn't configured Matrix surfaces (see main.ts opt-in
# gate) — that's the documented default state for instances not using
# Matrix. Using `on-failure` here makes the unit cleanly inert in
# that case instead of restart-looping.
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
# ─── Hardening ──────────────────────────────────────────────────
# Mirrors the indexer/relay hardening posture.
ProtectSystem=strict
ProtectHome=true
# The bot logs to the journal (StandardOutput/StandardError=journal),
# so it only needs its state dir writable. (It does NOT write to
# /var/log/morphit — that path was previously listed here by analogy
# with the indexer/relay, but the bot has no file logging, and listing
# a non-existent path makes systemd fail unit start with 226/NAMESPACE
# on hosts where the indexer/relay haven't created /var/log/morphit.)
ReadWritePaths=/var/lib/morphit-matrix-bot
PrivateTmp=true
NoNewPrivileges=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectClock=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
# Node's V8 JIT needs W+X
MemoryDenyWriteExecute=false
LimitCORE=0
ProtectProc=invisible
# NOTE: deliberately NOT setting `ProcSubset=pid` here, even though the
# indexer/relay units do. Those are *monitored* services; the matrix-bot's
# job is to RUN `journalctl --follow`, and journalctl reads the boot id from
# /proc/sys/kernel/random/boot_id. ProcSubset=pid mounts /proc with
# subset=pid, which hides /proc/sys — making journalctl fail with
# "Failed to get boot id: No such file or directory" and the bot crash-loop.
# /proc/sys remains read-only via ProtectKernelTunables=true (journalctl
# only needs to read it), so dropping ProcSubset costs little hardening here.
# Network — egress only to the Matrix homeserver. Healthcheck
# binds loopback only.
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
# ─── journalctl read access ─────────────────────────────────────
# The bot needs to read journald to tail morphit-indexer + morphit-relay
# units. Two equivalent approaches:
#
# (a) Add the bot user to `systemd-journal` group (recommended —
# least-privilege, no capabilities needed):
# sudo usermod -aG systemd-journal morphit-matrix-bot
#
# (b) Grant the unit CAP_SYSLOG via AmbientCapabilities (if you
# don't want group membership):
# AmbientCapabilities=CAP_SYSLOG
# CapabilityBoundingSet=CAP_SYSLOG
#
# Default below is (a) — group membership. Set up the user with:
# sudo useradd --system --no-create-home --shell /usr/sbin/nologin \
# --groups systemd-journal morphit-matrix-bot
# sudo mkdir -p /var/lib/morphit-matrix-bot
# sudo chown morphit-matrix-bot:morphit-matrix-bot /var/lib/morphit-matrix-bot
# sudo chmod 0750 /var/lib/morphit-matrix-bot
[Install]
WantedBy=multi-user.target