From 8084f25e6f246cf1918671c1351b5aa7992ed8c8 Mon Sep 17 00:00:00 2001 From: Ryan Yin Date: Mon, 2 Nov 2026 01:00:13 +0800 Subject: [PATCH] refactor(youko): disable HDD spindown from a udev rule Apply hdparm -B 255 -S 0 whenever a disk appears, which also covers the re-add after a bridge reset, instead of a boot oneshot plus an hourly timer. Drop -J 0: hdparm has always aborted it without --please-destroy-my-drive, and wdidle3 already reads disabled on both disks (it is persistent firmware state). This was the last user of mylib.systemdHardening.privilegedOneshot, so remove the preset and its test group. Assisted-by: Claude Code (claude-opus-5-5) --- hosts/12kingdoms-youko/default.nix | 46 ++++--------------- lib/systemdHardening.nix | 31 ------------- .../security-service-hardening/expected.nix | 19 -------- .../tests/security-service-hardening/expr.nix | 22 --------- 4 files changed, 8 insertions(+), 110 deletions(-) diff --git a/hosts/12kingdoms-youko/default.nix b/hosts/12kingdoms-youko/default.nix index 9e236e1d..a09655af 100644 --- a/hosts/12kingdoms-youko/default.nix +++ b/hosts/12kingdoms-youko/default.nix @@ -1,5 +1,6 @@ { config, + lib, pkgs, mylib, myvars, @@ -13,6 +14,10 @@ let # disko config does (the /dev/sdX names are not stable). hddPublic = "/dev/disk/by-id/ata-WDC_WD40EJRX-89T1XY0_WD-WCC7K0XDCZE6"; hddEncrypted = "/dev/disk/by-id/ata-WDC_WD40EZRZ-22GXCB0_WD-WCC7K7VV9613"; + hddSerials = lib.concatMapStringsSep "|" (lib.removePrefix "/dev/disk/by-id/ata-") [ + hddPublic + hddEncrypted + ]; coreModule = mylib.genVmHostModule { inherit pkgs hostName; @@ -105,48 +110,13 @@ in ]; # Keep the bridge powered: a suspended device is what most often triggers a - # reset on resume. + # reset on resume. Spindown is another trigger, so turn off APM/standby on the + # HDDs each time they appear (a bridge reset re-adds them). services.udev.extraRules = '' ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="152d", ATTR{idProduct}=="0567", TEST=="power/control", ATTR{power/control}="on" + ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="${hddSerials}", RUN+="${pkgs.hdparm}/bin/hdparm -B 255 -S 0 $devnode" ''; - # A spindown/spinup is a common trigger for the bridge resetting, so disable - # APM/standby on the HDDs. sdb in particular has ~1M load cycles (WD - # Intellipark), so stopping the idle head-parking also saves its mechanism. - # Read the settings back into the journal, and re-assert them hourly: the - # bridge or the disk's own firmware can drop them. - systemd.services.hdd-no-spindown = { - description = "Disable APM/standby on the USB HDDs"; - after = [ "local-fs.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = mylib.systemdHardening.privilegedOneshot // { - Type = "oneshot"; - RemainAfterExit = true; - CapabilityBoundingSet = [ "CAP_SYS_RAWIO" ]; - PrivateDevices = false; - DeviceAllow = [ "block-sd rw" ]; - }; - script = '' - for d in ${hddPublic} ${hddEncrypted}; do - echo "== $d ==" - # -B 255: APM off; -S 0: no standby timer; -J 0: WD idle3 (Intellipark) off - ${pkgs.hdparm}/bin/hdparm -B 255 -S 0 -J 0 "$d" || true - ${pkgs.hdparm}/bin/hdparm -B "$d" || true - ${pkgs.hdparm}/bin/hdparm -J "$d" || true - ${pkgs.hdparm}/bin/hdparm -C "$d" || true - done - ''; - }; - - systemd.timers.hdd-no-spindown = { - wantedBy = [ "timers.target" ]; - timerConfig = { - OnBootSec = "10min"; - OnUnitActiveSec = "1h"; - Persistent = true; - }; - }; - # Watch the disks with SMART. UDMA_CRC_Error_Count growth is the early warning # for the bridge/link problem. services.smartd = { diff --git a/lib/systemdHardening.nix b/lib/systemdHardening.nix index 272417c7..5d333dad 100644 --- a/lib/systemdHardening.nix +++ b/lib/systemdHardening.nix @@ -42,37 +42,6 @@ rec { ]; }; - # For short-lived root oneshots that perform one narrowly scoped privileged - # operation. Callers add only the capabilities and device access they need. - # ProtectKernelTunables is deliberately unset because it makes /sys read-only. - privilegedOneshot = { - AmbientCapabilities = ""; - CapabilityBoundingSet = ""; - LockPersonality = true; - MemoryDenyWriteExecute = true; - NoNewPrivileges = true; - PrivateDevices = true; - PrivateMounts = true; - PrivateNetwork = true; - PrivateTmp = true; - ProcSubset = "pid"; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectProc = "invisible"; - ProtectSystem = "strict"; - RemoveIPC = true; - RestrictAddressFamilies = [ "AF_UNIX" ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - UMask = "0077"; - }; - # Apply a preset at mkOptionDefault priority, so nixpkgs module values win. mkDefaults = lib.mapAttrs (_: lib.mkOptionDefault); } diff --git a/outputs/x86_64-linux/tests/security-service-hardening/expected.nix b/outputs/x86_64-linux/tests/security-service-hardening/expected.nix index 04fe4a54..246fc288 100644 --- a/outputs/x86_64-linux/tests/security-service-hardening/expected.nix +++ b/outputs/x86_64-linux/tests/security-service-hardening/expected.nix @@ -9,17 +9,6 @@ let "~@clock @cpu-emulation @debug @module @mount @obsolete @privileged @raw-io @reboot @swap" ]; }; - - privilegedOneshot = { - CapabilityBoundingSet = ""; - DeviceAllow = null; - NoNewPrivileges = true; - PrivateDevices = true; - PrivateNetwork = true; - ProtectKernelTunables = null; - ProtectSystem = "strict"; - RestrictAddressFamilies = [ "AF_UNIX" ]; - }; in { # Only 80/443. @@ -32,12 +21,4 @@ in whisper-asr = sandbox // { PrivateDevices = false; }; - - privilegedOneshot = { - hdd-no-spindown = privilegedOneshot // { - CapabilityBoundingSet = [ "CAP_SYS_RAWIO" ]; - DeviceAllow = [ "block-sd rw" ]; - PrivateDevices = false; - }; - }; } diff --git a/outputs/x86_64-linux/tests/security-service-hardening/expr.nix b/outputs/x86_64-linux/tests/security-service-hardening/expr.nix index 1577eff8..cf7acb9b 100644 --- a/outputs/x86_64-linux/tests/security-service-hardening/expr.nix +++ b/outputs/x86_64-linux/tests/security-service-hardening/expr.nix @@ -12,23 +12,6 @@ let lib.genAttrs names ( n: lib.getAttrs keys outputs.nixosConfigurations.${host}.config.systemd.services.${n}.serviceConfig ); - - privilegedKeys = [ - "CapabilityBoundingSet" - "DeviceAllow" - "NoNewPrivileges" - "PrivateDevices" - "PrivateNetwork" - "ProtectKernelTunables" - "ProtectSystem" - "RestrictAddressFamilies" - ]; - privilegedUnit = - host: name: - let - cfg = outputs.nixosConfigurations.${host}.config.systemd.services.${name}.serviceConfig; - in - lib.genAttrs privilegedKeys (key: cfg.${key} or null); in units "youko" [ "caddy" @@ -36,8 +19,3 @@ units "youko" [ "vmalert-homelab" ] // units "aquamarine" [ "whisper-asr" ] -// { - privilegedOneshot = { - hdd-no-spindown = privilegedUnit "youko" "hdd-no-spindown"; - }; -}