Files
spinifex/build/systemd/spinifex-northstar.service
Josh Siviour 6f4aa495e6 feat: reconcile systemd unit files on upgrade (#771)
* feat: reconcile systemd unit files on upgrade

Unit files were written once at install time by install_systemd() in
scripts/setup.sh and never re-asserted. A node upgraded by swapping the
spx binary kept whatever units it was installed with, so any fix that
lives in a unit rather than in code never reached it.

This is not theoretical. A node whose spinifex-viperblock.service
predates KillMode=process defaults to KillMode=control-group, so on a
target stop TimeoutStopSec expires mid-flush and systemd cgroup-SIGKILLs
every nbdkit while guest QEMU survives in the daemon's own cgroup: the
guest keeps running with no storage path.

Units are now stamped with a spinifex-unit-version marker, embedded in
the binary via a generated file, and reconciled by spx admin upgrade.
Missing or version-stale units are replaced after a timestamped backup;
a unit at the current version whose content differs is reported as
operator-modified and never overwritten. Drop-ins under <unit>.d are
untouched, so systemctl edit overrides survive.

Nothing is restarted. A daemon-reload is enough for KillMode and
TimeoutStopSec to apply to the next stop, so drift is corrected without
disturbing a running guest.

Adds --dry-run, --units-only and --skip-units to spx admin upgrade.

* test: cover systemd reconcile error paths and the admin upgrade CLI flow

Diff coverage on the unit-migration PR sat at 45.8%, below the 60% gate.
Adds tests for reconcile.go's error/edge paths (unreadable unit, backup
and write failures, daemon-reload failure, malformed version markers,
downgrade-is-conflict) and drives admin_upgrade.go's CLI flow directly
via an injectable exit and a decision matrix extracted into
validateUpgradeFlags/upgradeSummary for testability.
2026-08-12 22:29:48 +10:00

63 lines
1.8 KiB
Desktop File

# spinifex-unit-version: 1
[Unit]
Description=Spinifex Northstar (Authoritative DNS)
PartOf=spinifex.target
After=spinifex-nats.service spinifex-predastore.service
Wants=spinifex-predastore.service
[Service]
EnvironmentFile=-/etc/spinifex/telemetry.env
Type=simple
Slice=spinifex-system.slice
User=spinifex-northstar
Group=spinifex
ExecStart=/usr/local/bin/spx service northstar start
Restart=on-failure
RestartSec=5
# server.Shutdown() is called with an internal 5s context; margin covers the
# NATS unsubscribe/close and process exit that follow it.
TimeoutStopSec=10
OOMScoreAdjust=-500
# Per-service ceiling bounds a leak in this service alone rather than
# tripping the slice-wide cap, which would pick an arbitrary victim.
MemoryHigh=10%
MemoryMax=20%
Environment=SPINIFEX_CONFIG_PATH=/etc/spinifex/spinifex.toml
Environment=SPINIFEX_BASE_DIR=/var/lib/spinifex/northstar/
# Filesystem access
NoNewPrivileges=yes
ProtectSystem=strict
PrivateDevices=yes
ReadOnlyPaths=/etc/spinifex/northstar /etc/spinifex/spinifex.toml /etc/spinifex/ca.pem
ReadWritePaths=/var/lib/spinifex/northstar /var/log/spinifex
# Hardening
ProtectHome=yes
PrivateTmp=yes
RestrictSUIDSGID=yes
LockPersonality=yes
RestrictRealtime=yes
RestrictNamespaces=yes
ProtectClock=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectProc=invisible
SystemCallArchitectures=native
MemoryDenyWriteExecute=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
# RG-9: exactly CAP_NET_BIND_SERVICE so the unprivileged user binds :53 (no root).
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
ProtectHostname=yes
KeyringMode=private
RemoveIPC=yes
LimitCORE=0
LimitNOFILE=65536
LimitNPROC=4096
[Install]
WantedBy=spinifex.target