From cfe0a44b8d7f3a01938e4e9dac65f0482bde88ad Mon Sep 17 00:00:00 2001 From: Kirill Solomko Date: Sun, 28 Jul 2024 11:25:43 +0200 Subject: [PATCH] MT#60585 add user ban by IP address, ban JWT invalid attempts * ban users by IP addresses * ban invalid JWT attempts Change-Id: I0c7746aa751ca96cba0ce4d1388646ba4890a422 --- lib/NGCP/Panel/Controller/Root.pm | 19 +++++++++++++++++++ lib/NGCP/Panel/Utils/Auth.pm | 15 +++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/NGCP/Panel/Controller/Root.pm b/lib/NGCP/Panel/Controller/Root.pm index 389be6e1f3..fe60d6cb4f 100644 --- a/lib/NGCP/Panel/Controller/Root.pm +++ b/lib/NGCP/Panel/Controller/Root.pm @@ -526,6 +526,17 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') { return; } + my $banned = NGCP::Panel::Utils::Auth::user_is_banned($c, $user, $ngcp_realm); + if ($banned) { + my $ip = $c->request->address; + $c->response->status(HTTP_FORBIDDEN); + $c->response->body(encode_json({ + code => HTTP_FORBIDDEN, + message => "Forbidden!" })."\n"); + $c->log->debug("Banned user=$user realm=$ngcp_realm ip=$ip login attempt"); + return; + } + my $auth_user; if ($jwt) { my $realm = $ngcp_realm eq 'admin' ? 'api_admin_jwt' @@ -537,6 +548,7 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') { code => HTTP_FORBIDDEN, message => "Forbidden!" })."\n"); $c->log->info("Invalid JWT"); + NGCP::Panel::Utils::Auth::log_failed_login_attempt($c, $user, $ngcp_realm); return; } $auth_user = $c->user; @@ -561,6 +573,7 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') { code => HTTP_FORBIDDEN, message => "Forbidden!" })."\n"); $c->log->info("Unknown auth_token"); + NGCP::Panel::Utils::Auth::log_failed_login_attempt($c, $user, $ngcp_realm); return; } @@ -573,6 +586,7 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') { code => HTTP_FORBIDDEN, message => "Forbidden!" })."\n"); $c->log->info("Wrong auth_token role"); + NGCP::Panel::Utils::Auth::log_failed_login_attempt($c, $user, $ngcp_realm); return; } @@ -589,6 +603,7 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') { code => HTTP_FORBIDDEN, message => "Forbidden!" })."\n"); $c->log->info("Wrong auth_token role"); + NGCP::Panel::Utils::Auth::log_failed_login_attempt($c, $user, $ngcp_realm); return; } @@ -652,6 +667,7 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') { code => HTTP_FORBIDDEN, message => "User not found" })."\n"); $c->log->info("User not found"); + NGCP::Panel::Utils::Auth::log_failed_login_attempt($c, $user, $ngcp_realm); return; } } else { @@ -733,6 +749,7 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') { code => HTTP_FORBIDDEN, message => "User not found" })."\n"); $c->log->info("User not found"); + NGCP::Panel::Utils::Auth::log_failed_login_attempt($c, $user, $ngcp_realm); return; } $log_user = $auth_user->login; @@ -758,11 +775,13 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') { code => HTTP_FORBIDDEN, message => "User not found" })."\n"); $c->log->info("User not found"); + NGCP::Panel::Utils::Auth::log_failed_login_attempt($c, $user, $ngcp_realm); return; } $log_user = $auth_user->webusername; $log_user_id = $auth_user->uuid; } + NGCP::Panel::Utils::Auth::clear_failed_login_attempts($c, $user, $ngcp_realm); $c->log->debug(sprintf '%s JWT token for user=%s id=%s realm=%s expires_in_secs=%d', $jwt ? 'Re-issue' : 'Issue', diff --git a/lib/NGCP/Panel/Utils/Auth.pm b/lib/NGCP/Panel/Utils/Auth.pm index 4b1c7fccba..34b0116933 100644 --- a/lib/NGCP/Panel/Utils/Auth.pm +++ b/lib/NGCP/Panel/Utils/Auth.pm @@ -493,9 +493,10 @@ sub generate_auth_token { sub user_is_banned { my ($c, $user, $realm) = @_; + my $ip = $c->request->address; my $redis = $c->redis_get_connection({database => $c->config->{'Plugin::Session'}->{redis_db}}); - my $key = "login:ban:$user:$realm"; + my $key = "login:ban:$user:$realm:$ip"; return $redis->exists($key) ? 1 : 0; } @@ -506,10 +507,11 @@ sub log_failed_login_attempt { return unless $c->config->{security}{login}{ban_enable}; my $expire = $c->config->{security}{login}{ban_expire_time} // 0; my $max_attempts = $c->config->{security}{login}{max_attempts} // return; + my $ip = $c->request->address; my $redis = $c->redis_get_connection({database => $c->config->{'Plugin::Session'}->{redis_db}}); - my $key = "login:fail:$user:$realm"; + my $key = "login:fail:$user:$realm:$ip"; my $attempted = ($redis->hget($key, 'attempts') // 0) + 1; $attempted >= $max_attempts ? ban_user($c, $user, $realm) @@ -527,7 +529,8 @@ sub log_failed_login_attempt { sub clear_failed_login_attempts { my ($c, $user, $realm) = @_; - my $key = "login:fail:$user:$realm"; + my $ip = $c->request->address; + my $key = "login:fail:$user:$realm:$ip"; my $redis = $c->redis_get_connection({database => $c->config->{'Plugin::Session'}->{redis_db}}); @@ -541,10 +544,10 @@ sub ban_user { return unless $c->config->{security}{login}{ban_enable}; my $expire = $c->config->{security}{login}{ban_expire_time} // 0; - $expire = 30; + my $ip = $c->request->address; + my $key = "login:ban:$user:$realm:$ip"; - my $key = "login:ban:$user:$realm"; - $c->log->info("ban user=$user realm=$realm for $expire seconds"); + $c->log->info("Ban user=$user realm=$realm ip=$ip for $expire seconds"); my $redis = $c->redis_get_connection({database => $c->config->{'Plugin::Session'}->{redis_db}});