diff --git a/services/crowdsec.nix b/services/crowdsec.nix index feeb9d3..355f9d9 100644 --- a/services/crowdsec.nix +++ b/services/crowdsec.nix @@ -11,14 +11,34 @@ # # Before first deploy, create /var/secrets/ntfy-url with your topic URL: # echo 'https://ntfy.sh/nordhammer-' | sudo tee /var/secrets/ntfy-url -# sudo chmod 640 /var/secrets/ntfy-url +# sudo chmod 600 /var/secrets/ntfy-url { config, lib, pkgs, ... }: let - ntfyUrlFile = "/var/secrets/ntfy-url"; - ntfyUrl = - if builtins.pathExists ntfyUrlFile - then lib.removeSuffix "\n" (builtins.readFile ntfyUrlFile) - else "https://ntfy.sh/CHANGE-ME-CREATE-VAR-SECRETS-NTFY-URL"; + # The real URL is injected at service start (see ExecStartPre below) — + # eval-time builtins.readFile can't see /var/secrets under pure flake + # evaluation, which is how the `update` alias builds. + ntfyUrlPlaceholder = "@NTFY_URL@"; + + # The module renders settings.notifications into /etc/crowdsec/notifications/ + # as a symlink into /etc/static (the store). Re-render it from the static + # source with the secret substituted on every service start; nixos-rebuild + # restores the symlink on activation, so this never goes stale. + injectNtfyUrl = pkgs.writeShellScript "crowdsec-inject-ntfy-url" '' + set -euo pipefail + src=/etc/static/crowdsec/notifications/0-nixos-generated.yaml + dst=/etc/crowdsec/notifications/0-nixos-generated.yaml + secret=/var/secrets/ntfy-url + if [ ! -f "$secret" ]; then + echo "WARNING: $secret not found; ntfy notifications will not work" >&2 + exit 0 + fi + url=$(${pkgs.coreutils}/bin/tr -d '\n' < "$secret") + tmp=$(${pkgs.coreutils}/bin/mktemp "$dst.XXXXXX") + ${pkgs.gnused}/bin/sed "s|${ntfyUrlPlaceholder}|$url|g" "$src" > "$tmp" + ${pkgs.coreutils}/bin/chmod 600 "$tmp" + ${pkgs.coreutils}/bin/chown crowdsec:crowdsec "$tmp" + ${pkgs.coreutils}/bin/mv "$tmp" "$dst" + ''; # nixpkgs only builds the agent + cscli; the new module also expects # notification plugins at $out/libexec/crowdsec/plugins/. Compile them @@ -128,7 +148,7 @@ in name = "ntfy_http"; type = "http"; log_level = "info"; - url = ntfyUrl; + url = ntfyUrlPlaceholder; method = "POST"; headers = { Title = "CrowdSec alert"; @@ -163,28 +183,15 @@ in }; }; + # Inject the ntfy topic URL into the rendered notification config before + # every start. "+" runs the script with full privileges (it reads the + # root-owned secret and replaces a root-owned /etc symlink). + systemd.services.crowdsec.serviceConfig.ExecStartPre = [ "+${injectNtfyUrl}" ]; + # Firewall bouncer enforces decisions via nftables; auto-registers with LAPI services.crowdsec-firewall-bouncer = { enable = true; registerBouncer.enable = true; }; - - # The hub keeps tracking upstream master, but nixpkgs stable's crowdsec - # binary is a few versions behind and doesn't know newer expr functions - # (e.g. LookupFile, used by crowdsecurity/http-technology-probing). The - # agent then refuses to load the entire bucket and crashes on startup. - # Strip incompatible scenarios after crowdsec-setup repopulates the hub - # but before crowdsec.service tries to load them. - systemd.services.crowdsec-prune-incompatible-hub-items = { - description = "Remove hub scenarios incompatible with the bundled crowdsec"; - after = [ "crowdsec-setup.service" ]; - before = [ "crowdsec.service" ]; - requiredBy = [ "crowdsec.service" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "${pkgs.coreutils}/bin/rm -f /etc/crowdsec/scenarios/http-technology-probing.yaml"; - }; - }; }; }