diff --git a/common.nix b/common.nix index b484cd0..42d089d 100644 --- a/common.nix +++ b/common.nix @@ -29,7 +29,6 @@ ./services/jellyfin.nix ./services/bazarr.nix ./services/cloudflare-ddns.nix - ./services/crowdsec.nix ./services/fail2ban.nix ]; diff --git a/services/crowdsec.md b/services/crowdsec.md deleted file mode 100644 index eb8ab76..0000000 --- a/services/crowdsec.md +++ /dev/null @@ -1,99 +0,0 @@ -# CrowdSec Setup - -CrowdSec runs as a Docker (OCI) container on FredOS-Mediaserver. The firewall -bouncer runs as a native NixOS service and talks to the containerised LAPI over -localhost:8080. - -## Why Docker? - -The `crowdsec` package in nixpkgs unstable is incomplete — the NixOS module -does not reliably set up the LAPI and hub collections. The official CrowdSec -Docker image is well maintained and always up to date. - -## Architecture - -``` -[journald / log sources] - | - [CrowdSec LAPI] ← Docker container (port 8080 on localhost) - | -[firewall-bouncer] ← Native NixOS service (nftables/iptables) -``` - -## Initial Setup (first deploy) - -After running `nixos-rebuild switch`, the CrowdSec container will be running -but the firewall bouncer has no API key yet. - -**1. Generate a bouncer API key:** - -```bash -docker exec crowdsec cscli bouncers add firewall-bouncer -``` - -Copy the key printed to stdout — it is only shown once. - -**2. Store the key on the machine:** - -```bash -sudo mkdir -p /var/lib/secrets -echo -n "PASTE_KEY_HERE" | sudo tee /var/lib/secrets/crowdsec-bouncer-key -sudo chmod 600 /var/lib/secrets/crowdsec-bouncer-key -sudo chown root:root /var/lib/secrets/crowdsec-bouncer-key -``` - -**3. Restart the bouncer:** - -```bash -sudo systemctl restart crowdsec-firewall-bouncer -sudo systemctl status crowdsec-firewall-bouncer -``` - -The key file at `/var/lib/secrets/crowdsec-bouncer-key` is not managed by Nix -and must be created manually on each new machine. It should never be committed -to git. - -## Re-registering the Bouncer - -If the bouncer loses its registration (e.g. after a container wipe): - -```bash -# Remove the old registration -docker exec crowdsec cscli bouncers delete firewall-bouncer - -# Re-add and capture the new key -docker exec crowdsec cscli bouncers add firewall-bouncer - -# Update the key file and restart -echo -n "NEW_KEY_HERE" | sudo tee /var/lib/secrets/crowdsec-bouncer-key -sudo systemctl restart crowdsec-firewall-bouncer -``` - -## Useful Commands - -```bash -# View active bouncers -docker exec crowdsec cscli bouncers list - -# View active decisions (bans) -docker exec crowdsec cscli decisions list - -# View alerts -docker exec crowdsec cscli alerts list - -# Install/update a collection -docker exec crowdsec cscli collections install crowdsecurity/sshd - -# View installed collections -docker exec crowdsec cscli collections list -``` - -## Persistent Data - -The container mounts the following host paths: - -| Host path | Container path | Purpose | -|----------------------------------|-------------------------|--------------------------| -| `/var/lib/crowdsec/data` | `/var/lib/crowdsec/data`| GeoIP DB, decisions, etc | -| `/var/lib/crowdsec/config` | `/etc/crowdsec` | Config, hub, bouncers | -| `/var/log/crowdsec` | `/var/log/crowdsec` | CrowdSec logs | diff --git a/services/crowdsec.nix b/services/crowdsec.nix deleted file mode 100644 index 4ec5510..0000000 --- a/services/crowdsec.nix +++ /dev/null @@ -1,112 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - # Acquisition config is written to the host config dir before the container - # starts, so it persists across container restarts and reflects Nix config. - acquisYaml = '' - - source: journalctl - journalctl_filter: - - "-u" - - "sshd" - labels: - type: syslog - ''; - - # Generates /run/crowdsec-bouncer/config.yaml at service start, injecting the - # API key from /var/lib/secrets/crowdsec-bouncer-key without it ever entering - # the Nix store. See services/crowdsec.md for key setup instructions. - bouncerPreStart = pkgs.writeShellScript "crowdsec-bouncer-prestart" '' - set -euo pipefail - - KEY_FILE=/var/lib/secrets/crowdsec-bouncer-key - if [ ! -f "$KEY_FILE" ]; then - echo "ERROR: $KEY_FILE not found. See services/crowdsec.md for setup steps." >&2 - exit 1 - fi - - API_KEY=$(cat "$KEY_FILE") - - cat > /run/crowdsec-bouncer/config.yaml << EOF - mode: nftables - pid_dir: /run/crowdsec-bouncer/ - update_frequency: 10s - log_mode: stdout - log_level: info - api_url: http://127.0.0.1:8080 - api_key: $API_KEY - disable_ipv6: false - deny_action: DROP - deny_log: false - nftables: - ipv4: - enabled: true - set-only: false - table: crowdsec - chain: crowdsec-chain - ipv6: - enabled: true - set-only: false - table: crowdsec6 - chain: crowdsec-chain6 - EOF - ''; -in -{ - config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") { - - virtualisation.docker.enable = true; - virtualisation.oci-containers.backend = "docker"; - - # CrowdSec LAPI runs as a Docker container. - # Collections are installed on first boot via the COLLECTIONS env var. - # Journals are mounted read-only so CrowdSec can run journalctl inside the container. - virtualisation.oci-containers.containers.crowdsec = { - image = "crowdsecurity/crowdsec:latest"; - ports = [ "127.0.0.1:8080:8080" ]; - volumes = [ - "/var/lib/crowdsec/data:/var/lib/crowdsec/data" - "/var/lib/crowdsec/config:/etc/crowdsec" - "/var/log/journal:/var/log/journal:ro" - "/run/log/journal:/run/log/journal:ro" - "/etc/machine-id:/etc/machine-id:ro" - ]; - environment = { - COLLECTIONS = "crowdsecurity/linux crowdsecurity/sshd"; - }; - }; - - # Write acquisition config into the host config dir before the container starts. - systemd.services.docker-crowdsec.preStart = '' - mkdir -p /var/lib/crowdsec/config/acquis.d - cat > /var/lib/crowdsec/config/acquis.d/nixos.yaml << 'ACQUIS' - ${acquisYaml} - ACQUIS - ''; - - systemd.tmpfiles.rules = [ - "d /var/lib/crowdsec/data 0750 root root -" - "d /var/lib/crowdsec/config 0750 root root -" - "d /var/lib/secrets 0700 root root -" - ]; - - # Firewall bouncer runs natively. API key is injected at start time from - # /var/lib/secrets/crowdsec-bouncer-key — see services/crowdsec.md. - systemd.services.crowdsec-firewall-bouncer = { - description = "CrowdSec nftables firewall bouncer"; - after = [ "network.target" "docker-crowdsec.service" ]; - wants = [ "docker-crowdsec.service" ]; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Type = "simple"; - RuntimeDirectory = "crowdsec-bouncer"; - ExecStartPre = bouncerPreStart; - ExecStart = "${pkgs.crowdsec-firewall-bouncer}/bin/cs-firewall-bouncer -c /run/crowdsec-bouncer/config.yaml"; - Restart = "on-failure"; - RestartSec = "5s"; - AmbientCapabilities = [ "CAP_NET_ADMIN" "CAP_NET_RAW" ]; - CapabilityBoundingSet = [ "CAP_NET_ADMIN" "CAP_NET_RAW" ]; - }; - }; - }; -}