This commit is contained in:
mjallen18
2026-04-07 20:36:32 -05:00
parent 928de1837b
commit 3234029ae5
10 changed files with 1039 additions and 2 deletions

View File

@@ -156,9 +156,53 @@ let
bouncerName = "nas-bouncer";
};
# secrets.apiKeyPath = config.sops.secrets."jallen-nas/crowdsec-firewall-bouncer-api-key".path;
settings = {
# The default api_url is derived from the LAPI's listen_uri, which is
# "0.0.0.0:8181" — a valid bind address but not a connectable URL.
# Override to the loopback address the bouncer should actually connect to.
api_url = "http://127.0.0.1:${toString cfg.port}";
};
};
};
# During activation (which runs as root), check whether the machine credential in
# client.yaml exists in the crowdsec SQLite DB. If not (e.g. after a DB wipe),
# clear client.yaml so the subsequent crowdsec-setup ExecStartPre re-registers.
# This runs before switch-to-configuration starts/restarts services, breaking the
# boot-time cycle where the ExecStartPre fix can't apply until the service succeeds.
system.activationScripts.crowdsec-check-machine-creds =
let
machineName = config.services.crowdsec.name;
in
{
text = ''
clientYaml="${cfg.configDir}/crowdsec/client.yaml"
dbPath="/var/lib/crowdsec/state/crowdsec.db"
if [ -f "$dbPath" ]; then
if [ -s "$clientYaml" ]; then
login=$(${pkgs.gnugrep}/bin/grep -oP '(?<=login: ).*' "$clientYaml" || true)
if [ -n "$login" ]; then
found=$(${pkgs.sqlite}/bin/sqlite3 "$dbPath" \
"SELECT COUNT(*) FROM machines WHERE machine_id='$login';" 2>/dev/null || echo "0")
if [ "$found" = "0" ]; then
echo "crowdsec activation: machine '$login' missing from DB resetting credentials"
${pkgs.coreutils}/bin/rm -f "$clientYaml"
# Also remove any stale entry under the configured machine name so
# 'cscli machines add ${machineName} --auto' doesn't fail with "user already exist"
${pkgs.sqlite}/bin/sqlite3 "$dbPath" \
"DELETE FROM machines WHERE machine_id='${machineName}';" 2>/dev/null || true
fi
fi
else
# client.yaml absent/empty ensure no stale name entry blocks re-registration
${pkgs.sqlite}/bin/sqlite3 "$dbPath" \
"DELETE FROM machines WHERE machine_id='${machineName}';" 2>/dev/null || true
fi
fi
'';
deps = [ ];
};
# The upstream crowdsec module uses ReadWritePaths (not StateDirectory) on
# crowdsec.service, meaning it expects /var/lib/crowdsec to exist as a real
# directory (created by tmpfiles). However, crowdsec-firewall-bouncer-register
@@ -181,7 +225,64 @@ let
services = {
crowdsec = {
serviceConfig = lib.mkMerge [
{ DynamicUser = lib.mkForce false; }
{
DynamicUser = lib.mkForce false;
# ProtectSystem=strict (set upstream) makes all paths read-only except
# those in ReadWritePaths. The credentials file lives on the NAS mount
# which is not listed by default, so cscli machines add fails with EROFS.
ReadWritePaths = [ "${cfg.configDir}/crowdsec" ];
# If the machine credentials in client.yaml don't match any machine in the
# SQLite DB (e.g. after a DB wipe while client.yaml persists), crowdsec
# fatals on startup. Detect this mismatch before crowdsec-setup runs:
# read the machine login from client.yaml, query the DB directly, and
# delete client.yaml if the machine is absent so the next crowdsec-setup
# invocation re-registers a fresh machine.
# Use mkBefore so this runs before the upstream crowdsec-setup ExecStartPre
# entries, giving crowdsec-setup a cleared client.yaml to re-register from.
ExecStartPre = lib.mkBefore [
(
"+"
+ (
let
machineName = config.services.crowdsec.name;
in
pkgs.writeShellScript "crowdsec-check-machine-creds" ''
set -euo pipefail
clientYaml="${cfg.configDir}/crowdsec/client.yaml"
dbPath="/var/lib/crowdsec/state/crowdsec.db"
sqlite="${pkgs.sqlite}/bin/sqlite3"
rm="${pkgs.coreutils}/bin/rm"
[ -f "$dbPath" ] || exit 0 # No DB yet; fresh install, nothing to fix
if [ -s "$clientYaml" ]; then
# Credentials file exists verify the login it contains is in the DB
login=$(${pkgs.gnugrep}/bin/grep -oP '(?<=login: ).*' "$clientYaml" || true)
if [ -n "$login" ]; then
found=$("$sqlite" "$dbPath" \
"SELECT COUNT(*) FROM machines WHERE machine_id='$login';" 2>/dev/null || echo "0")
if [ "$found" = "0" ]; then
echo "crowdsec: machine '$login' missing from DB resetting credentials"
"$rm" -f "$clientYaml"
"$sqlite" "$dbPath" \
"DELETE FROM machines WHERE machine_id='${machineName}';" 2>/dev/null || true
fi
fi
else
# Credentials file absent ensure no stale name row blocks machines add
stale=$("$sqlite" "$dbPath" \
"SELECT COUNT(*) FROM machines WHERE machine_id='${machineName}';" 2>/dev/null || echo "0")
if [ "$stale" != "0" ]; then
echo "crowdsec: client.yaml absent but '${machineName}' in DB removing stale row"
"$sqlite" "$dbPath" \
"DELETE FROM machines WHERE machine_id='${machineName}';" 2>/dev/null || true
fi
fi
''
)
)
];
}
(lib.mkIf (cfg.ntfy.enable && cfg.ntfy.envFile != "") {
EnvironmentFile = [ cfg.ntfy.envFile ];
})