CrowdSec plugin (#2451)
This commit is contained in:
parent
057cfb29e8
commit
b5c0ca94c4
|
@ -31,7 +31,7 @@ use constant DEFAULTCONFBACKENDOPTIONS => (
|
|||
);
|
||||
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|f(?:indUser(?:Exclud|Search)ingAttribute|acebookExportedVar)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar|Macro)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node))|penIdExportedVars)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)s)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
|
||||
our $arrayParameters = qr/^mySessionAuthorizedRWKeys$/;
|
||||
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Claims|JWT))|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:DevOps(?:Download)?|State|User|XSS)|da)|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|(?:wsdlServ|findUs)er)$/;
|
||||
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Claims|JWT))|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:DevOps(?:Download)?|State|User|XSS)|rowdsec|da)|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|(?:wsdlServ|findUs)er)$/;
|
||||
|
||||
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ sub defaultValues {
|
|||
'corsEnabled' => 1,
|
||||
'corsExpose_Headers' => '*',
|
||||
'corsMax_Age' => '86400',
|
||||
'crowdsecAction' => 'reject',
|
||||
'cspConnect' => '\'self\'',
|
||||
'cspDefault' => '\'self\'',
|
||||
'cspFont' => '\'self\'',
|
||||
|
|
|
@ -1138,6 +1138,28 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
|||
'default' => '86400',
|
||||
'type' => 'text'
|
||||
},
|
||||
'crowdsec' => {
|
||||
'type' => 'bool'
|
||||
},
|
||||
'crowdsecAction' => {
|
||||
'default' => 'reject',
|
||||
'select' => [ {
|
||||
'k' => 'reject',
|
||||
'v' => 'Reject'
|
||||
},
|
||||
{
|
||||
'k' => 'warn',
|
||||
'v' => 'Warn'
|
||||
}
|
||||
],
|
||||
'type' => 'select'
|
||||
},
|
||||
'crowdsecKey' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
'crowdsecUrl' => {
|
||||
'type' => 'url'
|
||||
},
|
||||
'cspConnect' => {
|
||||
'default' => '\'self\'',
|
||||
'type' => 'text'
|
||||
|
|
|
@ -1131,6 +1131,29 @@ sub attributes {
|
|||
documentation => 'SSL options given to LWP::UserAgent',
|
||||
},
|
||||
|
||||
# CrowdSec plugin
|
||||
crowdsec => {
|
||||
type => 'bool',
|
||||
documentation => 'CrowdSec plugin activation',
|
||||
},
|
||||
crowdsecAction => {
|
||||
type => 'select',
|
||||
select => [
|
||||
{ k => 'reject', v => 'Reject' },
|
||||
{ k => 'warn', v => 'Warn' },
|
||||
],
|
||||
default => 'reject',
|
||||
documentation => 'CrowdSec action',
|
||||
},
|
||||
crowdsecUrl => {
|
||||
type => 'url',
|
||||
documentation => 'Base URL of CrowdSec local API',
|
||||
},
|
||||
crowdsecKey => {
|
||||
type => 'text',
|
||||
documentation => 'CrowdSec API key',
|
||||
},
|
||||
|
||||
# History
|
||||
failedLoginNumber => {
|
||||
default => 5,
|
||||
|
|
|
@ -775,6 +775,13 @@ sub tree {
|
|||
form => 'simpleInputContainer',
|
||||
nodes => [ 'checkState', 'checkStateSecret', ],
|
||||
},
|
||||
{
|
||||
title => 'CrowdSecPlugin',
|
||||
nodes => [
|
||||
'crowdsec', 'crowdsecAction',
|
||||
'crowdsecUrl', 'crowdsecKey',
|
||||
],
|
||||
},
|
||||
{
|
||||
title => 'checkUsers',
|
||||
help => 'checkuser.html',
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"اسم ملف تعريف الارتباط",
|
||||
"cookieParams":"ملفات تعريف الارتباط",
|
||||
"create":"أحدث",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"الإعدادات الحالية",
|
||||
"customAddParams":"معايير إضافية",
|
||||
"customAuth":"وحدة إثبات الهوية مخصصة",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ",
|
||||
"samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Cookie name",
|
||||
"cookieParams":"Cookies",
|
||||
"create":"Create",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"Current configuration",
|
||||
"customAddParams":"Additional parameters",
|
||||
"customAuth":"Custom authentication module",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"RelayState session timeout",
|
||||
"samlUseQueryStringSpecific":"Use specific query_string method",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Cookie name",
|
||||
"cookieParams":"Cookies",
|
||||
"create":"Create",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"Current configuration",
|
||||
"customAddParams":"Additional parameters",
|
||||
"customAuth":"Custom authentication module",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"RelayState session timeout",
|
||||
"samlUseQueryStringSpecific":"Use specific query_string method",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Cookie name",
|
||||
"cookieParams":"Cookies",
|
||||
"create":"Crear",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"Configuración actual",
|
||||
"customAddParams":"Parámetros adicionales",
|
||||
"customAuth":"Módulo personalizado de autentificación ",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"RelayState session timeout",
|
||||
"samlUseQueryStringSpecific":"Use specific query_string method",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Nom du cookie",
|
||||
"cookieParams":"Cookies",
|
||||
"create":"Créer",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"Clef de l'API",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"URL de base de l'API locale",
|
||||
"currentConfiguration":"Configuration actuelle",
|
||||
"customAddParams":"Paramètres additionnels",
|
||||
"customAuth":"Module d'authentification personnalisé",
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Nome del cookie",
|
||||
"cookieParams":"Cookie",
|
||||
"create":"Crea",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"Configurazione attuale",
|
||||
"customAddParams":"Parametri aggiuntivi",
|
||||
"customAuth":"Personalizza modulo di autenticazione",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"Timeout di sessione di RelayState",
|
||||
"samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string",
|
||||
"samlOverrideIDPEntityID":"Sostituisci l'ID entità quando agisce come IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Nazwa ciasteczka",
|
||||
"cookieParams":"Pliki cookie",
|
||||
"create":"Stwórz",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"Aktualna konfiguracja",
|
||||
"customAddParams":"Dodatkowe parametry",
|
||||
"customAuth":"Niestandardowy moduł uwierzytelniania",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"Limit czasu sesji RelayState",
|
||||
"samlUseQueryStringSpecific":"Użyj określonej metody query_string",
|
||||
"samlOverrideIDPEntityID":"Zastąp identyfikator jednostki podczas działania jako IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Çerez adı",
|
||||
"cookieParams":"Çerezler",
|
||||
"create":"Oluştur",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"Mevcut yapılandırma",
|
||||
"customAddParams":"Ek parametreler",
|
||||
"customAuth":"Özelleştirilmiş doğrulama modülü",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"RelayState oturum zaman aşımı",
|
||||
"samlUseQueryStringSpecific":"Spesifik query_string metodu kullan",
|
||||
"samlOverrideIDPEntityID":"IDP olarak davrandığında Varlık ID'yi geçersiz kıl"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Tên cookie",
|
||||
"cookieParams":"Cookie",
|
||||
"create":"Tạo",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"Cấu hình hiện tại",
|
||||
"customAddParams":"Tham số bổ sung",
|
||||
"customAuth":"Mô đun xác thực tùy chỉnh",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ",
|
||||
"samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Cookie 名称",
|
||||
"cookieParams":"Cookies",
|
||||
"create":"创建",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"Current configuration",
|
||||
"customAddParams":"Additional parameters",
|
||||
"customAuth":"Custom authentication module",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"RelayState session timeout",
|
||||
"samlUseQueryStringSpecific":"Use specific query_string method",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,11 @@
|
|||
"cookieName":"Cookie 名稱",
|
||||
"cookieParams":"Cookies",
|
||||
"create":"建立",
|
||||
"crowdsec":"Activation",
|
||||
"crowdsecAction":"Action",
|
||||
"crowdsecKey":"API key",
|
||||
"CrowdSecPlugin":"CrowdSec",
|
||||
"crowdsecUrl":"Base URL of local API",
|
||||
"currentConfiguration":"目前的設定",
|
||||
"customAddParams":"額外參數",
|
||||
"customAuth":"自訂驗證模組",
|
||||
|
@ -1203,4 +1208,4 @@
|
|||
"samlRelayStateTimeout":"RelayState 工作階段逾時",
|
||||
"samlUseQueryStringSpecific":"使用特定的 query_string 方法",
|
||||
"samlOverrideIDPEntityID":"充當 IDP 覆寫實體 ID"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -111,6 +111,7 @@ lib/Lemonldap/NG/Portal/Plugins/CheckDevOps.pm
|
|||
lib/Lemonldap/NG/Portal/Plugins/CheckState.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/CheckUser.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/CrowdSec.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/DecryptValue.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/FindUser.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/ForceAuthn.pm
|
||||
|
@ -652,6 +653,8 @@ t/61-AdaptativeAuthenticationLevel.t
|
|||
t/61-BruteForceProtection-with-Incremental-lockTimes-and-TOTP.t
|
||||
t/61-BruteForceProtection-with-Incremental-lockTimes.t
|
||||
t/61-BruteForceProtection.t
|
||||
t/61-CrowdSec-warn.t
|
||||
t/61-CrowdSec.t
|
||||
t/61-ForceAuthn.t
|
||||
t/61-GrantSession.t
|
||||
t/61-Session-ActivityTimeout.t
|
||||
|
|
|
@ -37,6 +37,7 @@ our @pList = (
|
|||
'::Plugins::AdaptativeAuthenticationLevel',
|
||||
globalLogoutRule => '::Plugins::GlobalLogout',
|
||||
refreshSessions => '::Plugins::Refresh',
|
||||
crowdsec => '::Plugins::CrowdSec',
|
||||
);
|
||||
|
||||
##@method list enabledPlugins
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
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_ERROR
|
||||
PE_OK
|
||||
PE_SESSIONNOTGRANTED
|
||||
);
|
||||
|
||||
our $VERSION = '2.0.10';
|
||||
|
||||
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;
|
||||
}
|
||||
);
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
if ( $self->conf->{crowdsecUrl} ) {
|
||||
$self->conf->{crowdsecUrl} =~ s#/+$##;
|
||||
}
|
||||
else {
|
||||
$self->logger->warn(
|
||||
"crowdsecUrl isn't set, fallback to http://localhost:8080");
|
||||
$self->conf->{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->conf->{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;
|
69
lemonldap-ng-portal/t/61-CrowdSec-warn.t
Normal file
69
lemonldap-ng-portal/t/61-CrowdSec-warn.t
Normal file
|
@ -0,0 +1,69 @@
|
|||
use Test::More;
|
||||
use strict;
|
||||
use lib 'inc';
|
||||
use IO::String;
|
||||
use Data::Dumper;
|
||||
use LWP::UserAgent;
|
||||
use LWP::Protocol::PSGI;
|
||||
|
||||
BEGIN {
|
||||
require 't/test-lib.pm';
|
||||
}
|
||||
|
||||
my $reject = 1;
|
||||
|
||||
LWP::Protocol::PSGI->register(
|
||||
sub {
|
||||
my $req = Plack::Request->new(@_);
|
||||
my $res = $reject
|
||||
? '[{"duration":"1h2m10.462947715s",
|
||||
"id":1121,
|
||||
"origin":"CAPI",
|
||||
"scenario":"crowdsecurity/iptables-scan-multi_ports",
|
||||
"scope":"ip",
|
||||
"type":"ban",
|
||||
"value":"127.0.0.1/32"}]'
|
||||
: '';
|
||||
return [ 200, [], [$res] ];
|
||||
}
|
||||
);
|
||||
|
||||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'debug',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
crowdsec => 1,
|
||||
crowdsecAction => 'warn',
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
expectOK($res);
|
||||
count(1);
|
||||
|
||||
$reject = 0;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
expectOK($res);
|
||||
count(1);
|
||||
|
||||
clean_sessions();
|
||||
|
||||
done_testing( count() );
|
69
lemonldap-ng-portal/t/61-CrowdSec.t
Normal file
69
lemonldap-ng-portal/t/61-CrowdSec.t
Normal file
|
@ -0,0 +1,69 @@
|
|||
use Test::More;
|
||||
use strict;
|
||||
use lib 'inc';
|
||||
use IO::String;
|
||||
use Data::Dumper;
|
||||
use LWP::UserAgent;
|
||||
use LWP::Protocol::PSGI;
|
||||
|
||||
BEGIN {
|
||||
require 't/test-lib.pm';
|
||||
}
|
||||
|
||||
my $reject = 1;
|
||||
|
||||
LWP::Protocol::PSGI->register(
|
||||
sub {
|
||||
my $req = Plack::Request->new(@_);
|
||||
my $res = $reject
|
||||
? '[{"duration":"1h2m10.462947715s",
|
||||
"id":1121,
|
||||
"origin":"CAPI",
|
||||
"scenario":"crowdsecurity/iptables-scan-multi_ports",
|
||||
"scope":"ip",
|
||||
"type":"ban",
|
||||
"value":"127.0.0.1/32"}]'
|
||||
: '';
|
||||
return [ 200, [], [$res] ];
|
||||
}
|
||||
);
|
||||
|
||||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'debug',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
crowdsec => 1,
|
||||
crowdsecAction => 'reject',
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
expectReject($res);
|
||||
count(1);
|
||||
|
||||
$reject = 0;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
expectOK($res);
|
||||
count(1);
|
||||
|
||||
clean_sessions();
|
||||
|
||||
done_testing( count() );
|
Loading…
Reference in New Issue
Block a user