aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/adguard.nix58
-rw-r--r--modules/sops.nix1
2 files changed, 42 insertions, 17 deletions
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" ];
};
};
};