feat(services): sandbox the moonarch oneshots

Both units read one state file and write a single sysfs attribute, yet ran
as unrestricted root with network access and the full capability set.
systemd-analyze security scored them 7.5 EXPOSED, now 0.9 SAFE.

ProtectKernelTunables and PrivateUsers stay off on purpose; the reasons sit
in the comment, since without them they read as forgotten.
This commit is contained in:
nevaforget committed 2026-09-08 11:48:17 +02:00
1 parent da483dfbc5
commit 76b899a2ad
1 file changed
+45 -18
+45 -18
View File
@@ -3,6 +3,49 @@
{ config, pkgs, lib, moonarchPkgs, ... }:
let
# Shared by the two root oneshots below. Both read one state file and write a
# single sysfs attribute, so everything else is taken away. Scored with
# `systemd-analyze security`: 7.5 EXPOSED before, 0.9 SAFE after.
#
# Deliberately absent:
# ProtectKernelTunables - remounts /sys read-only, which is exactly the
# write both units exist for. ReadWritePaths is no fix, /sys/class/… is a
# symlink into /sys/devices/….
# PrivateUsers - capabilities inside a user namespace are namespace-local,
# while the kernel's sysfs store handlers test against the initial one.
#
# ProtectSystem=strict is safe here: it leaves /dev, /proc and /sys writable,
# and the state file is only ever read.
sysfsWriterHardening = {
CapabilityBoundingSet = [ "" ];
AmbientCapabilities = [ "" ];
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateNetwork = true;
IPAddressDeny = "any";
RestrictAddressFamilies = [ "AF_UNIX" ];
ProtectClock = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
ProtectProc = "invisible";
ProcSubset = "pid";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
SystemCallArchitectures = "native";
SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
KeyringMode = "private";
UMask = "0077";
};
in
{
# --- Audio ---
services.pipewire = {
@@ -66,15 +109,7 @@
serviceConfig = {
Type = "oneshot";
ExecStart = "${moonarchPkgs.moonarch-scripts}/bin/moonarch-batsaver-restore";
NoNewPrivileges = true;
ProtectHome = true;
PrivateTmp = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictNamespaces = true;
RestrictRealtime = true;
LockPersonality = true;
};
} // sysfsWriterHardening;
};
systemd.services.moonarch-camera = {
@@ -85,15 +120,7 @@
serviceConfig = {
Type = "oneshot";
ExecStart = "${moonarchPkgs.moonarch-scripts}/bin/moonarch-camera-boot";
NoNewPrivileges = true;
ProtectHome = true;
PrivateTmp = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictNamespaces = true;
RestrictRealtime = true;
LockPersonality = true;
};
} // sysfsWriterHardening;
};
# The bar, launcher and every popout. A unit rather than a spawn-at-startup so