fix(nix): port three NixOS-module divergences from the shipped systemd units
A sweep of the Nix packaging against the units the deb/rpm actually install found three decisions that were made, documented and deliberate everywhere else, and simply not carried into packaging/nix/nixos-module.nix. punktfunk-web — StartLimitIntervalSec=0. The unit's EnvironmentFile for the mgmt token is mandatory ON PURPOSE, so the console genuinely fails until the host's first `serve` writes it. systemd's default rate limit (5 starts / 10 s) against RestartSec=2 then gives up permanently after ~10 s — which on an appliance is exactly the window before the host is ready, so a console enabled before the host's first run stayed dead until someone restarted it by hand. scripts/punktfunk-web.service has carried the override since that defect was found; the Nix module omitted it while its own comment went on promising "Restart retries until the host has created it". punktfunk-web — Restart=always, not on-failure. A console that exits 0 has still stopped serving, and on-failure leaves it down. Matches the shipped unit and web-run.cmd on Windows, both of which relaunch bun on ANY exit. An explicit `systemctl --user stop` is unaffected. punktfunk-scripting — the sandbox was missing entirely. The shipped unit confines the runner with NoNewPrivileges, ProtectSystem= strict, ReadWritePaths=%h /tmp and an AF_UNIX/AF_INET/AF_INET6 address-family restriction, plus PrivateTmp=no (a field report: a private /tmp hides /tmp/vhclient and /tmp/.X11-unix, so a plugin launches its vendor binary and then cannot reach the daemon behind it). The NixOS unit had none of it — so the one unit here that executes arbitrary operator TypeScript by design ran strictly LESS confined on NixOS than on every other channel. Verified by evaluating the module against the pinned nixpkgs and rendering the units: assertions clean, cap_sys_nice=ep on the encode-worker wrapper, firewall 47984/47989/47990/47992/47993/48010, and each unit carrying exactly the directives above. That evaluation is NOT something CI does — measured: `nix flake check` passes a nixosModule containing a nonexistent option, a nonexistent pkgs attribute and a nonexistent lib function, printing "checking NixOS module ... all checks passed!" while never evaluating it against nixpkgs. nix.yml's header claims that leg covers the module. It does not; tracked separately.
This commit is contained in:
@@ -508,6 +508,15 @@ in
|
||||
];
|
||||
wants = [ "punktfunk-web-init.service" ];
|
||||
wantedBy = optional cfg.web.autoStart "default.target";
|
||||
# Retry INDEFINITELY while the host is still writing the mgmt token + identity cert. The
|
||||
# EnvironmentFile below is mandatory on purpose, so the unit genuinely fails until those
|
||||
# exist — and systemd's default rate limit (5 starts / 10 s) against `RestartSec = 2` gives
|
||||
# up permanently after ~10 s, which on an appliance is exactly the window before the host's
|
||||
# first `serve` completes. A console enabled before the host's first run then stayed dead
|
||||
# until someone restarted it by hand. The shipped unit (scripts/punktfunk-web.service) has
|
||||
# carried this since that defect was found; it was missed in the port, while the comment
|
||||
# below went on promising the behaviour it removes.
|
||||
unitConfig.StartLimitIntervalSec = 0;
|
||||
environment = {
|
||||
PUNKTFUNK_MGMT_URL = "https://127.0.0.1:47990";
|
||||
PORT = "47992";
|
||||
@@ -525,7 +534,11 @@ in
|
||||
"-%h/.config/punktfunk/web-password"
|
||||
];
|
||||
ExecStart = "${cfg.web.package}/bin/punktfunk-web-server";
|
||||
Restart = "on-failure";
|
||||
# `always`, not `on-failure`: a console that exits 0 has still stopped serving, and
|
||||
# `on-failure` would leave it down. An explicit `systemctl --user stop` is still honoured
|
||||
# (Restart= never fights that). Matches scripts/punktfunk-web.service and the Windows
|
||||
# web-run.cmd, both of which relaunch bun on ANY exit.
|
||||
Restart = "always";
|
||||
RestartSec = 2;
|
||||
};
|
||||
};
|
||||
@@ -554,6 +567,38 @@ in
|
||||
KillMode = "mixed";
|
||||
KillSignal = "SIGTERM";
|
||||
TimeoutStopSec = 30;
|
||||
|
||||
# Sandbox — the same confinement scripts/punktfunk-scripting.service gives the deb/rpm
|
||||
# installs. The runner `import()`s the operator's own `.ts` files, so this is the one unit
|
||||
# here that executes arbitrary code by design; without these it ran strictly LESS confined
|
||||
# on NixOS than on every other channel. Read-only outside $HOME, no setuid re-escalation,
|
||||
# and only the address families automation actually uses (loopback mgmt API, LAN/IPv6
|
||||
# webhooks, unix sockets).
|
||||
NoNewPrivileges = true;
|
||||
# PrivateTmp deliberately OFF (field report 2026-08-03, the VirtualHere plugin). A
|
||||
# plugin's whole job is integrating with things already running on this box, and on Linux
|
||||
# those talk over /tmp: VirtualHere's client IPC is the FIFO pair /tmp/vhclient +
|
||||
# /tmp/vhclient_response, X11 is /tmp/.X11-unix. A private /tmp hides all of it — the
|
||||
# plugin launches the vendor binary fine and then cannot reach the daemon behind it,
|
||||
# which presents as an error no amount of config fixes.
|
||||
PrivateTmp = false;
|
||||
ProtectSystem = "strict";
|
||||
# ReadWritePaths puts back the write bit ProtectSystem=strict takes away: plugin state and
|
||||
# ~/.config/punktfunk under $HOME, plus the /tmp above. A plugin that must write OUTSIDE
|
||||
# $HOME (a game library on another mount) gets it with
|
||||
# systemctl --user edit punktfunk-scripting → [Service] ReadWritePaths=/mnt/games
|
||||
# ⚠ ProtectSystem is a MOUNT-NAMESPACE option, and for a *user* unit that needs
|
||||
# unprivileged user namespaces. On a kernel/config that restricts those it fails the unit
|
||||
# rather than degrading — drop it via the same drop-in if this box is one of them.
|
||||
ReadWritePaths = [
|
||||
"%h"
|
||||
"/tmp"
|
||||
];
|
||||
RestrictAddressFamilies = [
|
||||
"AF_UNIX"
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user