# CrowdSec security engine + nftables bouncer: parses sshd auth attempts from # the journal and bans offending source IPs at the firewall. Log-based (no # inline DPI) so it costs the N300 next to nothing. { config, lib, ... }: let cfg = config.cnx.router; mesh = import ../mesh-hosts.nix { inherit config lib; }; in { config = lib.mkIf cfg.enable { services.crowdsec = { enable = true; autoUpdateService = true; hub.collections = [ "crowdsecurity/linux" "crowdsecurity/sshd" ]; localConfig = { acquisitions = [ { source = "journalctl"; journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ]; labels.type = "syslog"; } ]; # Never ban the ZeroTier mesh — it is the only admin path to these # boxes (no public SSH), so a false positive would lock us out. # Parser-stage whitelist: mesh events are dropped before any scenario. parsers.s02Enrich = [ { name = "cnx/mesh-whitelist"; description = "Whitelist the ZeroTier management mesh"; whitelist = { reason = "ZeroTier mesh is the admin path"; cidr = [ mesh.subnet ]; }; } ]; }; }; services.crowdsec-firewall-bouncer = { enable = true; registerBouncer.enable = true; settings.mode = "nftables"; }; }; }