fix(deploy): make config readable by the service account, and assert modes
Some checks failed
deploy / deploy (push) Failing after 7m11s
Some checks failed
deploy / deploy (push) Failing after 7m11s
Run 5 shipped everything and started all three units, which then failed
identically:
configuration is invalid: /etc/tireless/config.toml: Permission denied
The config went out 0640 root:root, but the services run as `tireless`, which
is not in the root group. `--chown root:tireless` is not available as a fix:
on a fresh host that group does not exist until systemd-sysusers runs later in
the same deploy — the same ordering trap that produced the StateDirectory
change. So 0644 root:root, which is defensible precisely because this file
carries no secrets by design; the one that does, tireless.env, stays
0640 root:tireless and is installed by hand.
Testing that on the host turned up a second, quieter fault: `--chmod` only
applies to files rsync actually transfers, so a redeploy whose config is
byte-identical leaves the old mode in place. A mode fix would have appeared to
work on the deploy that introduced it and silently not applied afterwards. Add
-p to the shared options and switch to --chmod=F644/F755 so every push asserts
the mode it wants rather than hoping the content changed.
Also give tireless-runner an explicit start limit. Its preflight fails
permanently without an agent login, and Restart=on-failure with RestartSec=30s
never trips the default 5-starts-per-10s limiter, so it would have retried
forever. Three attempts in ten minutes, then failed, where systemctl status
says why.
Verified on bob: api and poller active, /v1/ready returns
{"config":"ok","database":"not_implemented","forge":"not_implemented"}, and
`tireless preflight` reports Subscription billing with the OpenCode lane
asserted non-Anthropic.
Refs #9
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TxK1CWPkFXqdcXMJ4hVe6
This commit is contained in:
@@ -105,15 +105,29 @@ jobs:
|
||||
# directory for a single-file copy, and Fedora ships neither
|
||||
# /etc/sysusers.d nor /etc/firewalld/services
|
||||
# (architecture/deployment-gitea-actions.md §6).
|
||||
R=(--rsync-path="sudo rsync --mkpath")
|
||||
# -p so --chmod actually applies. Without it, --chmod only affects
|
||||
# files rsync transfers, so a redeploy whose config is byte-identical
|
||||
# leaves whatever mode the file already had — including a wrong one.
|
||||
R=(-p --rsync-path="sudo rsync --mkpath")
|
||||
B=target/x86_64-unknown-linux-musl/release
|
||||
rsync "${R[@]}" --chmod 0755 "$B/tireless-api" \
|
||||
rsync "${R[@]}" --chmod=F755 "$B/tireless-api" \
|
||||
gitea_ci@"$API_HOST":/usr/local/bin/tireless-api
|
||||
rsync "${R[@]}" --chmod 0755 "$B/tireless-worker" \
|
||||
rsync "${R[@]}" --chmod=F755 "$B/tireless-worker" \
|
||||
gitea_ci@"$API_HOST":/usr/local/bin/tireless-worker
|
||||
rsync "${R[@]}" --chmod 0755 "$B/tireless" \
|
||||
rsync "${R[@]}" --chmod=F755 "$B/tireless" \
|
||||
gitea_ci@"$API_HOST":/usr/local/bin/tireless
|
||||
rsync "${R[@]}" --chmod 0640 config.toml \
|
||||
# 0644 root:root, not 0640: the services run as `tireless`, which is
|
||||
# not in the root group, so 0640 root:root is unreadable to them —
|
||||
# and `--chown root:tireless` cannot be used here because on a fresh
|
||||
# host the group does not exist until systemd-sysusers runs, later in
|
||||
# this same deploy.
|
||||
#
|
||||
# World-readable is acceptable because this file carries no secrets by
|
||||
# design: tokens are named by environment variable rather than written
|
||||
# down, and there is a test asserting no api-key field can appear in it.
|
||||
# The file that does carry secrets is /etc/tireless/tireless.env, which
|
||||
# stays 0640 root:tireless and is installed by hand, never by CI.
|
||||
rsync "${R[@]}" --chmod=F644 config.toml \
|
||||
gitea_ci@"$API_HOST":/etc/tireless/config.toml
|
||||
rsync "${R[@]}" asset/systemd/tireless.sysusers.conf \
|
||||
gitea_ci@"$API_HOST":/etc/sysusers.d/tireless.conf
|
||||
|
||||
@@ -4,6 +4,15 @@ Documentation=https://git.lair.cafe/lair/tireless
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
# Give up rather than loop. The runner's preflight fails permanently when the
|
||||
# service account has no agent login, and that needs a human — so `Restart=on-failure`
|
||||
# with a 30s backoff would retry forever without ever getting closer. The default
|
||||
# rate limit (5 starts / 10s) never trips at that interval, so state it explicitly:
|
||||
# three attempts in ten minutes, then sit in `failed` where `systemctl status`
|
||||
# reports the reason.
|
||||
StartLimitIntervalSec=600
|
||||
StartLimitBurst=3
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=tireless
|
||||
|
||||
Reference in New Issue
Block a user