100 lines
2.8 KiB
Perl
100 lines
2.8 KiB
Perl
package Lemonldap::NG::Portal::Plugins::CrowdSec;
|
|
|
|
use strict;
|
|
use Mouse;
|
|
use JSON qw(from_json);
|
|
use Lemonldap::NG::Common::UserAgent;
|
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
|
PE_OK
|
|
PE_ERROR
|
|
PE_SESSIONNOTGRANTED
|
|
);
|
|
|
|
our $VERSION = '2.0.12';
|
|
|
|
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
|
|
|
# Entrypoint
|
|
use constant beforeAuth => 'check';
|
|
|
|
has ua => (
|
|
is => 'rw',
|
|
lazy => 1,
|
|
builder => sub {
|
|
|
|
# TODO : LWP options to use a proxy for example
|
|
my $ua = Lemonldap::NG::Common::UserAgent->new( $_[0]->{conf} );
|
|
$ua->env_proxy();
|
|
return $ua;
|
|
}
|
|
);
|
|
has crowdsecUrl => ( is => 'rw' );
|
|
|
|
sub init {
|
|
my ($self) = @_;
|
|
if ( $self->conf->{crowdsecUrl} ) {
|
|
$self->crowdsecUrl( $self->conf->{crowdsecUrl} =~ s#/+$## );
|
|
}
|
|
else {
|
|
$self->logger->warn(
|
|
"crowdsecUrl isn't set, fallback to http://localhost:8080");
|
|
$self->crowdsecUrl('http://localhost:8080');
|
|
}
|
|
$self->logger->notice( 'CrowdSec policy is: '
|
|
. ( $self->conf->{crowdsecAction} ? 'reject' : 'warn' ) );
|
|
return 1;
|
|
}
|
|
|
|
sub check {
|
|
my ( $self, $req ) = @_;
|
|
my $ip = $req->address;
|
|
my $resp = $self->ua->get(
|
|
$self->crowdsecUrl . "/v1/decisions?ip=$ip",
|
|
'Accept' => 'application/json',
|
|
'X-Api-Key' => $self->conf->{crowdsecKey},
|
|
);
|
|
if ( $resp->is_error ) {
|
|
$self->logger->error( 'Bad CrowdSec response: ' . $resp->message );
|
|
$self->logger->debug( $resp->content );
|
|
return PE_ERROR;
|
|
}
|
|
my $content = $resp->decoded_content;
|
|
unless ($content) {
|
|
$self->userLogger->info("$ip isn't known by CrowsSec");
|
|
return PE_OK;
|
|
}
|
|
my $json_hash;
|
|
eval { $json_hash = from_json( $content, { allow_nonref => 1 } ); };
|
|
if ($@) {
|
|
$self->logger->error("Unable to decode CrowdSec response: $content");
|
|
$self->logger->debug($@);
|
|
return PE_ERROR;
|
|
}
|
|
$self->logger->debug("CrowdSec response: $content");
|
|
|
|
# Response is "null" when IP is unknown
|
|
if ($json_hash) {
|
|
|
|
# CrowdSec may return more than one decision
|
|
foreach my $decision (@$json_hash) {
|
|
if ( $decision->{type} and $decision->{type} eq 'ban' ) {
|
|
$self->userLogger->warn( "$ip banned by CrowdSec ('"
|
|
. $decision->{scenario}
|
|
. "' for $decision->{duration})" );
|
|
if ( $self->conf->{crowdsecAction} eq 'reject' ) {
|
|
$self->userLogger->error("$ip rejected by CrowdSec");
|
|
return PE_SESSIONNOTGRANTED;
|
|
}
|
|
else {
|
|
$req->env->{CROWDSEC_REJECT} = 1;
|
|
return PE_OK;
|
|
}
|
|
}
|
|
}
|
|
$self->userLogger->info("$ip not banned by CrowdSec");
|
|
return PE_OK;
|
|
}
|
|
}
|
|
|
|
1;
|