Schlanker CrowdSec-Bouncer fuer PHP ohne Framework-Bindung
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Claude ca1234db32 Reporter: Vorfaelle an die CrowdSec-API melden
Ein Bouncer-Schluessel darf nur lesen. Der Reporter meldet mit den
Zugangsdaten einer Machine eigene Beobachtungen als Alarm samt
Entscheidung - ein Alarm ohne Entscheidung waere nur sichtbar, wuerde aber
niemanden sperren.

Das JWT wird zwischengespeichert; laeuft es ab, meldet sich der Reporter
neu an und sendet erneut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 09:01:24 +02:00
src Reporter: Vorfaelle an die CrowdSec-API melden 2026-09-03 09:01:24 +02:00
tests Reporter: Vorfaelle an die CrowdSec-API melden 2026-09-03 09:01:24 +02:00
.gitignore CrowdSec-Bouncer fuer PHP 2026-09-03 08:45:15 +02:00
composer.json CrowdSec-Bouncer fuer PHP 2026-09-03 08:45:15 +02:00
README.md Reporter: Vorfaelle an die CrowdSec-API melden 2026-09-03 09:01:24 +02:00

CrowdSec Bouncer und Reporter (PHP)

Ein schlanker Bouncer für die lokale CrowdSec-API (LAPI) ohne Composer, ohne Framework, ohne WordPress.

Wozu

CrowdSec sammelt auf dem Server Signale über auffällige IP-Adressen und legt Entscheidungen ab (ban, captcha). Ein Bouncer fragt diese Entscheidungen ab und setzt sie um. Diese Klasse ist genau dieser Frageteil was danach passiert, entscheidet die Anwendung.

Voraussetzung

Auf dem Server läuft CrowdSec, und die Anwendung erreicht dessen LAPI (üblicherweise http://127.0.0.1:8080). Ein Bouncer-Schlüssel entsteht mit:

sudo cscli bouncers add mein-bouncer

Ohne laufende CrowdSec-Instanz bringt die Klasse nichts sie gibt dann immer none zurück.

Verwendung

HTTP und Zwischenspeicher werden hineingereicht, damit die Klasse in jeder Umgebung läuft. In WordPress etwa so:

use Valitype\CrowdSec\Bouncer;

require __DIR__ . '/vendor/crowdsec-bouncer/src/Bouncer.php';

$bouncer = new Bouncer(
    'http://127.0.0.1:8080',
    'BOUNCER_SCHLUESSEL',
    array(
        'http' => static function ( $url, $headers, $timeout ) {
            $response = wp_remote_get( $url, array( 'headers' => $headers, 'timeout' => $timeout ) );

            if ( is_wp_error( $response ) ) {
                throw new Exception( $response->get_error_message() );
            }

            return array(
                'code' => wp_remote_retrieve_response_code( $response ),
                'body' => wp_remote_retrieve_body( $response ),
            );
        },
        'cacheGet' => static function ( $key ) {
            return get_transient( $key );
        },
        'cacheSet' => static function ( $key, $value, $ttl ) {
            set_transient( $key, $value, $ttl );
        },
    )
);

if ( $bouncer->isBanned( $ip ) ) {
    wp_die( 'Zugriff verweigert.', '', array( 'response' => 403 ) );
}

Einstellungen

Schlüssel Standard Bedeutung
http Callback ( $url, $headers, $timeout ): array{code,body}. Pflicht.
cacheGet Callback ( $key ): mixed. Ohne ihn wird jede Anfrage durchgereicht.
cacheSet Callback ( $key, $value, $ttl ): void.
timeout 2 Sekunden. Kurz halten die LAPI ist lokal.
cacheTtl 60 Sekunden, die eine Entscheidung gilt.
fallback none Was gilt, wenn die LAPI nicht antwortet.

Grundsatz

Im Zweifel durchlassen. Eine nicht erreichbare LAPI, ein Zeitüberschreiten oder eine unverständliche Antwort führen zu none, nicht zu einer Sperre. Wer das anders will, setzt fallback auf ban und sollte sich sicher sein.

Rückgaben

getDecision( $ip ) liefert ban, captcha oder none. Liegt zu einer IP sowohl ban als auch captcha vor, gewinnt ban.

Verbindung prüfen

$result = $bouncer->testConnection();
// array( 'status' => 'ok'|'error', 'message' => '...' )

Vorfälle melden

Ein Bouncer-Schlüssel darf nur lesen. Wer eigene Beobachtungen an CrowdSec melden will, braucht Zugangsdaten einer Machine:

sudo cscli machines add mein-melder --password 'geheim'

Damit meldet Reporter einen Alarm samt Entscheidung:

use Valitype\CrowdSec\Reporter;

$reporter = new Reporter(
    'http://127.0.0.1:8080',
    'mein-melder',
    'geheim',
    array(
        'http'     => $post,   // ( $url, $headers, $timeout, $body ): array{code,body}
        'cacheGet' => 'get_transient',
        'cacheSet' => $set,
        'scenario' => 'meine-app/spam',
        'duration' => '4h',
    )
);

$reporter->report( $ip, array( 'message' => 'Formular-Spam' ) );

Der Alarm enthält die Entscheidung ausdrücklich mit. Ein Alarm ohne Entscheidung taucht zwar in cscli alerts list auf, sperrt aber niemanden CrowdSec leitet daraus von sich aus nichts ab.

Das JWT aus POST /v1/watchers/login wird zwischengespeichert; läuft es ab, meldet sich der Reporter automatisch neu an und sendet erneut.

Prüfen lässt sich die Anmeldung mit testConnection().

Lizenz

GPL-2.0-or-later