From 3fdf4a3e0dbd55b2b1dce12ca0396dc018c4e0e3 Mon Sep 17 00:00:00 2001 From: Karan Jayachandra Date: Sat, 18 Jul 2026 22:09:05 +0200 Subject: Added the changes for the Adguard fix --- modules/adguard.nix | 58 +++++++++++++++++++++++++++++++++++++---------------- modules/sops.nix | 1 + 2 files changed, 42 insertions(+), 17 deletions(-) (limited to 'modules') diff --git a/modules/adguard.nix b/modules/adguard.nix index b3974d5..b3a7ee2 100644 --- a/modules/adguard.nix +++ b/modules/adguard.nix @@ -34,14 +34,15 @@ enable_dnssec = true; }; - # Users block: username "admin", password from sops secret at activation. - # The activation script below writes the hash into the config before - # AdGuard starts, because mutableSettings=false uses a static config file - # but the password hash must be injected at runtime (it contains a secret). + # Users block: username "admin", password from sops secret. + # The ExecStartPre below writes the hash into the config on every + # service start, because mutableSettings=false uses a static config + # file but the password hash must be injected at runtime (it contains + # a secret). users = [ { name = "admin"; - # Placeholder - replaced at activation time by the script below + # Placeholder - replaced at service start by the ExecStartPre below password = "REPLACED_AT_ACTIVATION"; } ]; @@ -70,17 +71,40 @@ }; }; - # At activation: inject the bcrypt password hash from the sops secret into - # the AdGuard config so the declarative config has the real hash. - system.activationScripts.adguard-password = { - deps = [ "setupSecrets" ]; - text = '' - HASH_FILE="${config.sops.secrets."adguard/password_hash".path}" - CFG="/var/lib/AdGuardHome/AdGuardHome.yaml" - if [ -f "$HASH_FILE" ] && [ -f "$CFG" ]; then - HASH=$(cat "$HASH_FILE") - ${pkgs.gnused}/bin/sed -i "s|REPLACED_AT_ACTIVATION|$HASH|g" "$CFG" - fi - ''; + # AdGuardHome needs a static system user rather than the module's default + # DynamicUser=true: sops-nix chowns the "adguard/password_hash" secret to + # this user *during activation*, which happens while the service is + # stopped (for a restart) - a DynamicUser only exists while its service is + # actually running, so that chown would fail to resolve the user otherwise. + users.users.adguardhome = { + isSystemUser = true; + group = "adguardhome"; + }; + users.groups.adguardhome = { }; + + # mutableSettings = false makes the module's own ExecStartPre unconditionally + # `cp --force` the store-generated config (with the literal placeholder) + # over $STATE_DIRECTORY/AdGuardHome.yaml on every service start. An + # activation script can't win that race, so inject the real hash as a + # second ExecStartPre, ordered after the module's via mkAfter, so it always + # runs right before ExecStart. + # + # Plain bash substring substitution + `>` redirect is used instead of + # `sed -i`: sed -i creates a temp file and chowns/renames it in place, and + # this unit's hardened SystemCallFilter (~@privileged) blocks the chown + # syscall, killing sed with SIGSYS ("Bad system call"). + systemd.services.adguardhome.serviceConfig = { + DynamicUser = lib.mkForce false; + User = "adguardhome"; + Group = "adguardhome"; + ExecStartPre = lib.mkAfter [ + "${pkgs.writeShellScript "adguard-inject-password" '' + set -eu + CFG="$STATE_DIRECTORY/AdGuardHome.yaml" + HASH=$(cat "${config.sops.secrets."adguard/password_hash".path}") + CONTENT=$(cat "$CFG") + printf '%s\n' "''${CONTENT//REPLACED_AT_ACTIVATION/$HASH}" > "$CFG" + ''}" + ]; }; } diff --git a/modules/sops.nix b/modules/sops.nix index 5e0fbf6..a71b370 100644 --- a/modules/sops.nix +++ b/modules/sops.nix @@ -30,6 +30,7 @@ owner = "adguardhome"; group = "adguardhome"; mode = "0400"; + restartUnits = [ "adguardhome.service" ]; }; }; }; -- cgit v1.3.1