# crowdsec/ (episode 12) CrowdSec next to fail2ban on the same box, both reading the same logs. This folder holds the files that differ from a stock install. Everything was measured on the demo box; the numbers are in `HARDENING.md` (episode 12). CrowdSec runs on the **host**, not in the stack: it reads the sshd journal directly and drives the host firewall. Caddy talks to it over the Docker bridge. ## Install (Debian 13) ``` curl -fsSL https://install.crowdsec.net | sh apt install crowdsec ``` **Port 8080 trap.** The Local API defaults to `127.0.0.1:8080`. On this stack the NetBird dashboard already publishes `127.0.0.1:8080`, so the installer prints one line ("Not attempting to start crowdsec, port 8080 is already used") and leaves the service **inactive**. Move it: ``` sed -i 's#listen_uri: 127.0.0.1:8080#listen_uri: 0.0.0.0:8090#' /etc/crowdsec/config.yaml sed -i 's#url: http://127.0.0.1:8080#url: http://127.0.0.1:8090#' /etc/crowdsec/local_api_credentials.yaml ``` (`0.0.0.0` only because the Caddy container has to reach it, see below. If you skip the Caddy bouncer, keep `127.0.0.1:8090`.) **First start pulls nothing.** The very first community-blocklist pull ran before the scenario list was registered ("scenario list is empty, will not pull yet", 0 entries). A `systemctl restart crowdsec` pulled 15,000 IPs a minute later. Check with `cscli decisions list --origin CAPI | wc -l`. ## Log sources and collections ``` cscli collections install crowdsecurity/caddy cp acquis.d/caddy.yaml /etc/crowdsec/acquis.d/ ``` The installer already adds `crowdsecurity/sshd` and a journald source for `ssh.service`. ## Firewall bouncer ``` apt install crowdsec-firewall-bouncer-iptables ``` The package registers itself with the Local API. Then apply `firewall-bouncer.yaml` (mode `nftables`) and restart it. Why not the default iptables mode: it only inserts into `INPUT`. SSH is blocked, but every Docker-published port (80, 443, 2222) stays reachable for a banned IP, the same hole as fail2ban in episode 10. Adding `DOCKER-USER` fixes that until the next `ufw reload`, which flushes the chain and nobody puts the rule back. ## Caddy bouncer and AppSec (WAF) 1. Build the Caddy image with the modules (`caddy/Dockerfile`), elsewhere, and load it on the box. 2. AppSec collections, plus the rules the default config references but the collections do not install (without them crowdsec **does not start**: "no appsec-rules found for pattern crowdsecurity/experimental-*", then the same for `generic-*`): ``` cscli collections install crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules cscli appsec-rules install crowdsecurity/experimental-no-user-agent \ crowdsecurity/generic-freemarker-ssti crowdsecurity/generic-wordpress-uploads-listing \ crowdsecurity/generic-wordpress-uploads-php cp acquis.d/appsec.yaml /etc/crowdsec/acquis.d/ crowdsec -t && systemctl restart crowdsec ``` Run `crowdsec -t` first. A failed restart means no Local API; the firewall keeps its current sets, but no new decisions arrive. 3. Let the Docker network in, and only that network: ``` ufw allow proto tcp from 172.18.0.0/16 to 172.17.0.1 port 8090,7422 ``` The container reaches the host as `host.docker.internal` (`extra_hosts: host-gateway` in `compose.yml`, which is `172.17.0.1`). From the internet both ports stay filtered. The mesh interface `wt0` is allowed wholesale by episode 5, so mesh peers can reach both; each still needs an API key. 4. `cscli bouncers add caddy-bouncer -o raw` and put the key into `.env` as `CROWDSEC_BOUNCER_KEY`, then `docker compose up -d caddy`. The `vpn.` site does not import the `crowdsec` snippet: it carries the NetBird gRPC streams, and a WAF round-trip on every stream message is not what you want between your peers. ## Replaying old logs (and the forensic-mode trap) `crowdsec -dsn file:///path/to/log -type syslog` is documented as replay mode. With CrowdSec 1.8.1 it shut down one second after reading the file and dropped every event still in the pipeline: 47 or 49 alerts on two identical runs, none after 02:07 of a 24 h log. Feeding the same file through a FIFO that stays open 60 s after the last line gives the full, repeatable result (133 alerts, twice): ``` mkfifo /tmp/replay.fifo (cat sshd.log; sleep 60) > /tmp/replay.fifo & crowdsec -c replay.yaml -order-event -dsn file:///tmp/replay.fifo -type syslog ``` Use a separate config (`replay.yaml`) with its own database, its own `listen_uri` and **no `online_client`**, or the replayed alerts land in your live database and go out as signals. ## What goes out to CrowdSec With the default config, every alert from an unmodified hub scenario is pushed as a signal: scenario name and hash, timestamp, your machine id, and **the attacking IP**. Custom or modified ("tainted") scenarios and manual decisions are not pushed unless you enroll in the console. Your own penetration tests are real alerts too: switch `sharing: false` under `api.server.online_client` while you attack your own box, or you report your test machine.