Zwei gravierte Messingschilder auf dunklem Schiefer, links 'TYPO3 14 backend', rechts 'CrowdSec LAPI', verbunden durch ein kurzes Cat6-Patch-Kabel als sichtbare Bridge; daneben Kraftpapier-Labels mit IP-Decisions an einem oxblood Baumwollfaden — Metapher für die Operator-Bridge ins CrowdSec Local API.
AI-generated · gpt-image 2.0

moselwal/crowdsec-bridge — CrowdSec LAPI direkt im TYPO3-Backend.

moselwal/crowdsec-bridge bringt den operativ relevanten Teil der CrowdSec Local API in ein TYPO3-14-Backend-Modul: Decisions anzeigen, IPs sperren und entsperren, Alerts inspizieren, LAPI-Heartbeat prüfen — alles ohne Container-Shell und ohne SSH. Read-only by default, Write nur für die dedizierte Operator-Rolle, jeder Schreibversuch wird forensisch im sys_log erfasst.

Sechs Bausteine

Voraussetzungen

Architektur: strikte DDD-Vier-Schichten

Die Schichten sind per deptrac.yaml in der CI erzwungen. Aufwärts-Importe sind verboten, jeder Commit läuft durch das Gate.

 

Classes/
├── Domain/          # Reine Value Objects, Enums, Contracts. Null externe Dependencies.
├── Application/     # CQRS-Queries und -Commands plus Handler. Darf nur Domain importieren.
├── Infrastructure/  # HTTP-Client, Repositories, Audit-Logger, Cache-Decorator. Darf Application, Domain und Framework-Typen.
└── Presentation/    # Backend-Controller, Fluid-Templates, Event-Listener.

 

Der CQRS-Schnitt ist flach, aber konsistent: jede Backend-Interaktion läuft über ein *Query-/*Command-Objekt plus dedizierten *Handler. Handler bekommen Abhängigkeiten per Konstruktor-Injection — final readonly durchgängig.

Der HTTP-Client implementiert Psr\Http\Client\ClientInterface und wird per Constructor injiziert. Domain-Code ist Framework-frei und trivial unit-testbar.

Der optionale Cache ist ein Decorator über LapiDecisionRepository, der nur dann in den DI-Container gebunden wird, wenn moselwal/keyvalue-store installiert und cacheEnabled=1 gesetzt ist (siehe Configuration/Services.php). Ohne das Paket greift ein NullDecisionCacheInvalidator-No-op — die Write-Handler brauchen keine Runtime-Verzweigung.

Konfiguration: Bouncer-Key und Extension-Optionen

Bouncer-API-Key im CrowdSec-Container erzeugen:

 

cscli bouncers add typo3-bridge

 

Die Bridge sucht den Key in genau dieser Reihenfolge:

  1. Docker-Secret-Datei /run/secrets/crowdsec_api_key — empfohlen für Production, SOPS-verschlüsselt und als Docker-Secret gemountet.
  2. Environment-Variable CROWDSEC_LAPI_KEY.
  3. $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['crowdsec_bridge']['lapiKey'] — nur Development, niemals Production.

Läuft keine dieser Quellen, zeigt das Modul beim ersten Aufruf einen klaren Konfigurationsfehler.

Extension-Konfiguration

Verwaltet über Admin Tools → Settings → Extension Configuration → crowdsec_bridge.

lapiBaseUrl

Default crowdsec. Voll qualifizierte URL zur CrowdSec Local API. Muss vom TYPO3-Container erreichbar sein.

lapiTimeout

Default 5. HTTP-Timeout in Sekunden für alle LAPI-Calls.

allowDeleteForeignDecisions

Default 0. Wenn 1, dürfen TYPO3-Admins (nicht reguläre Operator) auch von CrowdSec/cscli angelegte Decisions löschen. Reguläre crowdsec_operator-Mitglieder dürfen das nie, egal welche Einstellung.

cacheEnabled

Default 1. Aktiviert den Decision-List-Cache. Braucht moselwal/keyvalue-store; ohne das Paket bleibt die Einstellung wirkungslos.

cacheTtl

Default 10 Sekunden. Cache-Lebensdauer. Gültiger Bereich 1–300; Werte außerhalb werden beim Construct geclamped und im TYPO3-Log vermerkt.

Berechtigungen und Sicherheitsmodell

Die Bridge legt keine Backend-Gruppen automatisch an. Drei Rollen werden einmalig unter System → Backend-Benutzer → Gruppen erstellt und mit Modul-Zugriff belegt:

crowdsec_viewer — Read-only

Decisions-Liste, Alerts-Liste, IP-Quick-Check, LAPI-Status.

crowdsec_operator — Operator

Alle Viewer-Rechte plus neue Decisions anlegen und eigene typo3-origin Decisions löschen.

TYPO3-Admin ($BE_USER->isAdmin() === true)

Alle Operator-Rechte plus Löschen von Foreign-Origin-Decisions, wenn allowDeleteForeignDecisions=1.

Alle Access-Checks laufen server-seitig. Die UI blendet Buttons aus, die ein Nutzer nicht aufrufen darf; der Controller erzwingt jeden Check zusätzlich, sodass direkte URL-Manipulation in HTTP 403 endet.

Defence-in-Depth gegen Writes

Kein Secret-Material wird je in den Browser gerendert. Der Bouncer-API-Key lebt ausschließlich in PHP-State, aus Docker-Secret, ENV oder TYPO3_CONF_VARS (in dieser Reihenfolge).

Audit-Trail im sys_log

Die Bridge schreibt strukturierte Einträge ins sys_log. Im System → Log-Modul nach diesen type-Werten filtern, um Aktivität zu inspizieren:

crowdsec_bridge:quickcheck

Jeder IP-Quick-Check (Treffer oder kein Treffer). Channel security, Severity info.

crowdsec_bridge:decision_add

Erfolgreiche oder fehlgeschlagene Add-Versuche. Channel security, Severity info bzw. warning.

crowdsec_bridge:decision_delete

Erfolgreich, not-found oder fehlgeschlagen. Channel security, Severity info bzw. warning.

crowdsec_bridge:write_denied

Write-Versuch durch einen Nutzer ohne Operator-Rolle. Channel security, Severity warning.

crowdsec_bridge:delete_denied

Delete-Versuch gegen eine Foreign-Origin-Zeile ohne Admin-Override. Channel security, Severity warning.

crowdsec_bridge:csrf_denied

Write-Versuch mit fehlendem oder ungültigem Form-Token. Channel security, Severity warning.

crowdsec_bridge:duplicate_submit

Re-Submit eines bereits konsumierten Form-Tokens. Channel security, Severity warning.

Der volle Audit-Kontext (Action, Status, Decision-Daten, ursprüngliche Origin, Latenz, Fehlermeldung) wird als JSON in die log_data-Spalte serialisiert — ready für Downstream-Analyse.

Optionaler Decision-List-Cache

Mit installierter moselwal/keyvalue-store und cacheEnabled=1 cached die Bridge paginierte Decision-List-Antworten mit kurzer TTL (Default 10 Sekunden). Typischer Effekt: wiederholte F5-Reloads bleiben unter 50 ms.

Invalidierung mit Generation-Counter

Atomares INCR auf einen einzigen KVS-Key. Nach jedem erfolgreichen Add oder Delete wird der Counter inkrementiert — alle vorher gecachten Keys werden in O(1) unerreichbar, ohne Scan. Alte Einträge laufen über TTL ab.

Failure-Handling

Jeder KVS-Fehler (Backend down, Timeout, Malformed Payload) degradiert still auf direkte LAPI-Calls. Der erste Fehler eines Requests landet auf warning im TYPO3-Logger; weitere Fehler innerhalb desselben Requests werden unterdrückt, um Log-Spam zu vermeiden.

Was nicht gecached wird

Installation

composer require moselwal/crowdsec-bridge
vendor/bin/typo3 extension:setup

 

Optional den Decision-List-Cache zuschalten:

 

composer require moselwal/keyvalue-store
vendor/bin/typo3 cache:flush

 

Docker-Compose-Beispiel

Minimaler Snippet, der die Bridge an einen CrowdSec-Container und einen optionalen Redis-Cache koppelt:

 

services:
  app:
    image: typo3:14
    depends_on:
      - crowdsec
      - redis
    networks:
      - internal
    secrets:
      - crowdsec_api_key
    environment:
      CROWDSEC_LAPI_KEY_FILE: /run/secrets/crowdsec_api_key

  crowdsec:
    image: crowdsecurity/crowdsec:latest
    networks:
      - internal
    expose:
      - "8080"

  redis:
    image: redis:7-alpine
    networks:
      - internal
    expose:
      - "6379"

networks:
  internal:

secrets:
  crowdsec_api_key:
    file: ./secrets/dev/crowdsec_api_key

 

Production: Secret-Datei mit SOPS und age verschlüsseln, über Docker-Secrets mounten, weder Redis noch CrowdSec-Ports nach außen exposen.

Nächster Schritt

CrowdSec ins TYPO3-Backend ziehen?

Wenn Sie CrowdSec bereits einsetzen und Decisions plus Alerts ohne Container-Shell direkt im TYPO3-Backend bedienen wollen — mit klarer Rollentrennung, Foreign-Origin-Schutz und vollständigem Audit-Trail im sys_log — sprechen Sie uns an. Wir koordinieren Bouncer-Setup, Backend-Gruppen, optionalen Decision-Cache und Operator-Onboarding.

CrowdSec-Bridge besprechen

Oder direkt schreiben: kontakt@moselwal.de

Setzen wir ein bei …

Dieses Paket gehört zur Operator-Schicht jeder TYPO3-Plattform, die hinter Caddy mit CrowdSec steht — Decisions und Alerts direkt im Backend statt im Container-Shell. In der betreuten Variante: AI-Ready CMS as a Service mit CrowdSec-Bouncer, Decision-Cache via keyvalue-store und sauber konfigurierten Backend-Rollen.