↓ Zum Hauptinhalt springen
  1. Beiträge/
  2. Kleines Homelab/
  3. k3s-prod: Kubernetes Cluster Konfiguration und Anwendungsbereitstellung/

CrowdSec

Inhaltsverzeichnis

CrowdSec
#

CrowdSec wertet die Access-Logs von Traefik aus, erkennt Angriffsmuster anhand von Szenarien und legt daraus Entscheidungen ab, etwa “IP x ist für 4 Stunden gesperrt”. Umgesetzt werden die Entscheidungen von einem Bouncer, hier einem Traefik-Plugin.

Die App wird stufenweise eingeführt:

Phase Inhalt Stand
0 Access-Log von Traefik als JSON (apps/traefik) live
1 LAPI und Agent, nur erkennen, kein Bouncer diese App
2 Bouncer-Plugin in Traefik an einer einzelnen Route offen
3 Bouncer am Entrypoint websecure und ws-alternate offen
4 AppSec mit Virtual Patching offen

Solange kein Bouncer registriert ist, sperrt CrowdSec niemanden. Alarme und Entscheidungen entstehen trotzdem und lassen sich mit cscli prüfen (Abschnitt Betrieb).

Quelle
#

  • Chart crowdsec aus den CrowdSec-Helm-Charts, Werte in values.yaml. Aufgebaut nach dem HA-Beispiel im README des Charts und der Kubernetes-Anleitung.
  • Datenbank: Chart postgres der CloudPirates-Charts mit dem offiziellen Image postgres, Werte in values-database.yaml.
  • Images crowdsecurity/crowdsec und alpine/kubectl mit festem Tag in values.yaml, Renovate hebt sie.

Parser, Szenarien und Collections lädt der Agent beim Start vom CrowdSec Hub (COLLECTIONS und PARSERS in values.yaml). Sie haben keine pinbaren Versionen, ein Neustart kann neuere Fassungen bringen.

Komponenten
#

Objekt Art Zweck
crowdsec-lapi Deployment, 2 Replikas Local API: speichert Alarme und Entscheidungen, spricht mit der Central API und der Console
crowdsec-agent DaemonSet auf den Control-Plane-Nodes liest /var/log/containers/traefik-*_kube-system_*.log
crowdsec-postgres StatefulSet Datenbank der LAPI, Volume crowdsec-db
crowdsec-capi-register-job Job, Helm-Hook pre-install,pre-upgrade (Argo CD: PreSync) registriert die Instanz einmalig bei der Central API und legt das Secret crowdsec-capi-credentials an
crowdsec-service, crowdsec-agent-service ServiceMonitor Metriken für apps/prometheus

Der Agent läuft nur dort, wo Traefik läuft: Traefik ist per nodeSelector auf die Control-Plane-Nodes festgelegt (apps/traefik/values.yaml). Ändert sich das, muss der nodeSelector des Agents mitziehen.

Die LAPI hat kein eigenes Volume. Alles, was sie behalten muss, liegt in PostgreSQL oder in Secrets. Deshalb laufen zwei Replikas, und ein Update rollt ohne Unterbrechung aus.

Die NetworkPolicy crowdsec-postgres lässt nur die LAPI an die Datenbank.

Konfiguration
#

  • Datenbank, Aufräumregeln (flush) und automatische Registrierung der Agents stehen in config.config.yaml.local in values.yaml, wie im Beispiel der Doku.
  • allowed_ranges der automatischen Registrierung umfasst nur das Pod-Netz des Clusters.
  • Eigene Adressen stehen in der Whitelist zyria-whitelist.yaml (config.parsers.s02-enrich), dieselben wie in der Middleware localnet von apps/traefik. Private Bereiche deckt der Parser crowdsecurity/whitelists ab. Eine Adresse auf der Whitelist erzeugt keinen Alarm, keine Entscheidung und kein Signal an die Central API.

Central API und Console
#

Die Central API ist eingeschaltet. CrowdSec meldet erkannte Angreifer-IPs mit Szenario und Zeitpunkt an die CrowdSec SAS (Frankreich) und bezieht dafür die Community-Blockliste. Anfrageinhalte und Nutzerdaten werden nicht übertragen.

Die Console (app.crowdsec.net) ist vorbereitet: Die LAPI meldet sich dort an, sobald das Secret crowdsec-keys einen ENROLL_KEY enthält. Ohne den Schlüssel überspringt das Startskript die Anmeldung. Über die Console fließen zusätzlich Alarme und Metriken ab. Die angemeldete Instanz muss in der Console einmal angenommen werden.

Secrets
#

Secret Herkunft Schlüssel Zweck
crowdsec-keys secretGenerator in kustomization.yaml postgres-password Superuser postgres
CUSTOM_USER, CUSTOM_DB, CUSTOM_PASSWORD Rolle und Datenbank crowdsec
csLapiSecret Signierschlüssel der LAPI
registrationToken Token für die automatische Registrierung der Agents
ENROLL_KEY optional, Anmeldung an der Console
crowdsec-capi-credentials angelegt vom Job crowdsec-capi-register-job online_api_credentials.yaml Zugang zur Central API, nicht im Repository

Das Chart würde csLapiSecret und registrationToken selbst erzeugen, per lookup und Zufallswert. Unter Kustomize liefert lookup nichts, jedes Rendern durch Argo CD ergäbe neue Werte. Deshalb kommen beide aus crowdsec-keys (secrets.externalSecret in values.yaml).

Angelegt wird crowdsec-keys mit diesem Befehl aus dem Wurzelverzeichnis des Repositories. Er erzeugt die Werte, ohne sie auszugeben, und setzt .secretGenerator neu. Für eine bestehende Datenbank nur zusammen mit einem leeren Datenverzeichnis verwenden, denn PostgreSQL liest die Passwörter nur beim ersten Start:

 1cd "$(git rev-parse --show-toplevel)/apps/crowdsec" \
 2  && export ADMIN_PW="$(openssl rand -hex 24)" OWNER_PW="$(openssl rand -hex 24)" \
 3       LAPI_SECRET="$(openssl rand -hex 32)" REG_TOKEN="$(openssl rand -hex 24)" \
 4  && yq -i '.secretGenerator = [
 5       {"name": "crowdsec-keys", "namespace": "crowdsec", "literals": [
 6         "postgres-password=" + strenv(ADMIN_PW),
 7         "CUSTOM_USER=crowdsec",
 8         "CUSTOM_DB=crowdsec",
 9         "CUSTOM_PASSWORD=" + strenv(OWNER_PW),
10         "csLapiSecret=" + strenv(LAPI_SECRET),
11         "registrationToken=" + strenv(REG_TOKEN)]}]
12     | (.secretGenerator[].literals[] | select(test("password=|PASSWORD=|csLapiSecret=|registrationToken="))) line_comment = "gitleaks:allow"' kustomization.yaml \
13  && unset ADMIN_PW OWNER_PW LAPI_SECRET REG_TOKEN \
14  && git diff --stat kustomization.yaml

Den Enroll-Key der Console (app.crowdsec.net, “Security Engines”, “Enroll command”) ergänzt dieser Befehl. Er fragt den Schlüssel verdeckt ab und hängt ihn an crowdsec-keys an:

1cd "$(git rev-parse --show-toplevel)/apps/crowdsec" \
2  && printf 'Enroll key: ' && read -rs ENROLL && echo \
3  && test -n "${ENROLL}" \
4  && export ENROLL \
5  && yq -i '(.secretGenerator[] | select(.name == "crowdsec-keys") | .literals) += ["ENROLL_KEY=" + strenv(ENROLL)]
6     | (.secretGenerator[].literals[] | select(test("^ENROLL_KEY="))) line_comment = "gitleaks:allow"' kustomization.yaml \
7  && unset ENROLL \
8  && git diff --stat kustomization.yaml

crowdsec-capi-credentials gehört nicht zu Argo CD und bleibt beim Entfernen der App stehen. Wird es gelöscht, registriert der Job die Instanz beim nächsten Sync neu.

Betrieb
#

Alle Befehle laufen in einem LAPI-Pod:

1kubectl -n crowdsec exec deploy/crowdsec-lapi -- cscli alerts list
2kubectl -n crowdsec exec deploy/crowdsec-lapi -- cscli decisions list
3kubectl -n crowdsec exec deploy/crowdsec-lapi -- cscli machines list
4kubectl -n crowdsec exec deploy/crowdsec-lapi -- cscli metrics

Eine Entscheidung gegen eine eigene Adresse aufheben und die Adresse danach in zyria-whitelist.yaml aufnehmen:

1kubectl -n crowdsec exec deploy/crowdsec-lapi -- cscli decisions delete --ip 192.0.2.10

Ob der Agent die Traefik-Zeilen versteht, zeigt cscli metrics im Agent-Pod (Abschnitt “Acquisition Metrics”, Spalten “Lines parsed” und “Lines unparsed”). Das allgemeine Log von Traefik steht als Text im selben Stream und landet unter “unparsed”.

1kubectl -n crowdsec exec ds/crowdsec-agent -- cscli metrics show acquisition parsers

Installation
#

Die App wird von Argo CD über manifests.yaml ausgerollt. Lokal prüfen:

1kubectl kustomize --enable-helm apps/crowdsec
Fabrice Kirchner
Autor
Fabrice Kirchner
stolzer Vater, Nerd, Admin