From 19a7201d1d911c9f25bc112a3e0d2218eb6c14a2 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 2 Sep 2026 11:22:23 +0200 Subject: fix(packaging): ship the example hub config, and stop leaving /etc/meshbay open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three defects, found while answering whether installing the .deb would land where the production server was just moved to by hand. - **The example config was never packaged.** `build-hub.sh` copied `packaging/conf/hub.toml.example` under `if [ -f ]`, and that path does not exist in this repo — so every package ever built shipped no example at all and said nothing about it. The postinst places no config either, on purpose (a shipped hub.toml is overwritten on upgrade; a shipped secret gets run in production), which left an installed hub with nothing to copy from. The file now exists, documents every key `config.py` reads including the captcha `allowed_hosts` the desktop client needs, and the copy is a hard failure rather than a silent skip. - **`/etc/meshbay` was created 0755.** It holds the hub's Ed25519 private key and its database password. The file modes protect the contents, but a world-listable config directory tells anyone with a shell what a hub keeps and where. Now 0750 root:meshbay, in both the deb postinst and the rpm scriptlet; the service reads it by group. - **The rpm would have failed to build on the new file.** `%files` claimed nothing under /etc, and rpmbuild refuses an installed file no line claims. It now declares the directory and the example, with explicit `%attr` and `%config` so an operator's edits become .rpmsave rather than vanishing. Package modes no longer follow the builder's umask either — the same source tree produced 775/664 on a machine with umask 002 and 755/644 with 022. `install -m` sets them. Verified by building: the deb now carries ./etc/meshbay/ at drwxr-x--- with hub.toml.example at 0644, and the embedded postinst tightens the directory as belt and braces rather than as the only thing making it right. The rpm path is unverified — no rpmbuild on this machine. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014UtzVrzM7e2tG9fSpkR9ML --- packaging/build/build-hub.sh | 27 ++++++++---- packaging/conf/hub.toml.example | 73 +++++++++++++++++++++++++++++++ packaging/deb/meshbay-hub/DEBIAN/postinst | 11 ++++- packaging/rpm/meshbay-hub.spec | 13 +++++- 4 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 packaging/conf/hub.toml.example (limited to 'packaging') diff --git a/packaging/build/build-hub.sh b/packaging/build/build-hub.sh index 45d2ab9..be2d363 100755 --- a/packaging/build/build-hub.sh +++ b/packaging/build/build-hub.sh @@ -53,17 +53,26 @@ if [ -f "$REPO/packages/meshbay-hub/alembic.ini" ]; then cp "$REPO/packages/meshbay-hub/alembic.ini" "$ROOT/opt/meshbay-hub/migrations/" fi -# Config example +# Config example. +# +# A hard failure, not an `if [ -f ]`. This was a silent skip against a path +# that did not exist, so every package built shipped no example config at all +# and said nothing about it — and the postinst places no config either, on +# purpose, which left an installed hub with nothing to copy from. +EXAMPLE="$REPO/packaging/conf/hub.toml.example" +[ -f "$EXAMPLE" ] || { echo "!! missing $EXAMPLE" >&2; exit 1; } mkdir -p "$ROOT/opt/meshbay-hub/share" -if [ -f "$REPO/packaging/conf/hub.toml.example" ]; then - cp "$REPO/packaging/conf/hub.toml.example" "$ROOT/opt/meshbay-hub/share/" -fi +install -m 644 "$EXAMPLE" "$ROOT/opt/meshbay-hub/share/" -# Also install to /etc/meshbay/ for discoverability -mkdir -p "$ROOT/etc/meshbay" -if [ -f "$ROOT/opt/meshbay-hub/share/hub.toml.example" ]; then - cp "$ROOT/opt/meshbay-hub/share/hub.toml.example" "$ROOT/etc/meshbay/" -fi +# Also next to the real config, where an operator looks first. Never +# hub.toml itself: an upgrade would overwrite a working deployment. +# +# Modes are set here rather than left to the builder's umask, which decided +# them until now — 775/664 on a machine with umask 002, 755/644 on one with +# 022, from the same source tree. The postinst tightens the directory too, but +# that then repairs the package instead of the package being right. +install -d -m 750 "$ROOT/etc/meshbay" +install -m 644 "$ROOT/opt/meshbay-hub/share/hub.toml.example" "$ROOT/etc/meshbay/" # --- Systemd unit --------------------------------------------------------- mkdir -p "$ROOT/usr/lib/systemd/system" diff --git a/packaging/conf/hub.toml.example b/packaging/conf/hub.toml.example new file mode 100644 index 0000000..d647707 --- /dev/null +++ b/packaging/conf/hub.toml.example @@ -0,0 +1,73 @@ +# MeshBay Hub — example configuration. +# +# Copy to /etc/meshbay/hub.toml and edit. The package deliberately does not +# install a working config: it would either ship a placeholder secret that +# somebody runs in production, or overwrite yours on upgrade. +# +# Read from the first of these that exists: +# /etc/meshbay/hub.toml <- where a packaged hub looks +# ~/.config/meshbay/hub.toml <- a development hub, run as yourself +# +# Every value below can also come from the environment, which is what the +# systemd unit's EnvironmentFile (/etc/meshbay/hub.env) is for. Secrets belong +# there rather than in an `Environment=` line: `systemctl cat` shows a unit to +# any user on the machine. + +[hub] +# This hub's identity, as members and nodes know it. Changing it after anyone +# has joined invalidates what they trust. +id = "hub.example.org" + +# Ed25519 private key. Generate with: +# meshbay-hub --help (see the key subcommands) +# The packaged service runs as `meshbay`, so: +# chown meshbay:meshbay /etc/meshbay/hub_private.pem && chmod 600 it +private_key_path = "/etc/meshbay/hub_private.pem" + +# Accounts granted the admin role at creation. Everything else is set in the UI. +admin_usernames = [] + +[database] +# PostgreSQL in production. The default without this key is an in-memory +# SQLite, which is a test fixture and loses everything on restart. +# Prefer MESHBAY_DATABASE_URL in /etc/meshbay/hub.env — it carries a password. +url = "postgresql+asyncpg://meshbay:CHANGEME@localhost/meshbay_hub" + +[server] +# Loopback: TLS is Caddy's job, and the hub should not be reachable directly. +host = "127.0.0.1" +port = 8000 +workers = 1 + +[jwt] +# The access token is not the session — the refresh token is, and the SPA +# renews against it long before this runs out. What this bounds is a token +# that leaks. +access_token_ttl = 14400 # 4 h +refresh_token_ttl = 2592000 # 30 j + +[captcha] +# reCAPTCHA v2 on registration and password reset, so no mail is ever sent +# before a human has been seen. Absent, or either key empty, disables it +# entirely — which is right for development and for a hub nobody can reach. +# See docs/captcha.md. +site_key = "" +secret_key = "" + +# Hostnames a solved captcha may have been solved on, checked against the one +# `siteverify` reports — what Google observed, not what the client claims. +# +# Leave empty while the reCAPTCHA key does its own origin check: it is then +# already done, one layer up. Set it when you turn that check off in the +# reCAPTCHA console, and the two go together — turning the console check off +# without setting this leaves no origin check anywhere. +# +# The desktop client is why it exists. Its interface ships inside the package +# and is served from `app://meshbay`, so the hostname Google sees is not this +# hub's and never can be; with the console check on, the widget shows +# "Invalid domain for site key" and nothing client-side reaches that decision. +# Add the client's own host only if you distribute it — it is the weak entry, +# since any Electron application can claim the same scheme and host. +# +# allowed_hosts = ["hub.example.org", "localhost", "meshbay"] +allowed_hosts = [] diff --git a/packaging/deb/meshbay-hub/DEBIAN/postinst b/packaging/deb/meshbay-hub/DEBIAN/postinst index 3cf66a9..0484f3b 100644 --- a/packaging/deb/meshbay-hub/DEBIAN/postinst +++ b/packaging/deb/meshbay-hub/DEBIAN/postinst @@ -17,8 +17,15 @@ case "$1" in install -d -o meshbay -g meshbay -m 750 /var/lib/meshbay/hub install -d -o meshbay -g meshbay -m 750 /var/log/meshbay - # Create config directory (files are placed by the admin, not by us) - install -d -m 755 /etc/meshbay + # Create config directory (files are placed by the admin, not by us — + # a shipped hub.toml would be overwritten on upgrade, and a shipped + # secret would be run in production). The example lands in + # /etc/meshbay/hub.toml.example instead. + # 750, not 755: this directory holds the hub's private key and its + # database password. The file modes protect the contents, but a + # world-listable config directory tells anyone with a shell what a + # hub keeps and where. The service reads it by group. + install -d -o root -g meshbay -m 750 /etc/meshbay # Reload systemd if available if [ -d /run/systemd/system ]; then diff --git a/packaging/rpm/meshbay-hub.spec b/packaging/rpm/meshbay-hub.spec index 73df0b9..50bd977 100644 --- a/packaging/rpm/meshbay-hub.spec +++ b/packaging/rpm/meshbay-hub.spec @@ -32,7 +32,11 @@ getent passwd meshbay >/dev/null || \ %systemd_post meshbay-hub.service install -d -o meshbay -g meshbay -m 750 /var/lib/meshbay/hub install -d -o meshbay -g meshbay -m 750 /var/log/meshbay -install -d -m 755 /etc/meshbay +# 750, not 755: this directory holds the hub's private key and its +# database password. The file modes protect the contents, but a +# world-listable config directory tells anyone with a shell what a +# hub keeps and where. The service reads it by group. +install -d -o root -g meshbay -m 750 /etc/meshbay %preun %systemd_preun meshbay-hub.service @@ -47,6 +51,13 @@ install -d -m 755 /etc/meshbay /opt/meshbay-common/venv/bin/meshbay-hub /usr/bin/meshbay-hub %{_unitdir}/meshbay-hub.service +# The example config, and the directory it lands in. Both have to be declared: +# build-hub.sh stages them, and rpmbuild fails the build on an installed file +# no %files line claims. %config so an operator's edits are kept as .rpmsave +# rather than silently replaced — it is under /etc, whatever its name says. +# The mode matches %post; the `meshbay` group exists by then, %pre makes it. +%dir %attr(0750, root, meshbay) /etc/meshbay +%config %attr(0644, root, meshbay) /etc/meshbay/hub.toml.example %changelog * Sun Aug 31 2026 MeshBay Team - %{version}-1 -- cgit v1.2.3