diff --git a/baseTemplate/static/baseTemplate/custom-js/system-status.js b/baseTemplate/static/baseTemplate/custom-js/system-status.js index 9924462f9..37720ec3e 100644 --- a/baseTemplate/static/baseTemplate/custom-js/system-status.js +++ b/baseTemplate/static/baseTemplate/custom-js/system-status.js @@ -1162,19 +1162,8 @@ var dashboardStatsControllerFn = function ($scope, $http, $timeout) { return; // Already processing this IP } - // Check if already blocked - if ($scope.blockedIPs && $scope.blockedIPs[ipAddress]) { - console.log('IP already blocked:', ipAddress); - if (typeof PNotify !== 'undefined') { - new PNotify({ - title: 'Info', - text: `IP address ${ipAddress} is already banned`, - type: 'info', - delay: 3000 - }); - } - return; - } + // Do not early-return when IP is already in blockedIPs: still call the API so the + // backend can close any active connections from this IP (already-banned path). // Set blocking flag to prevent duplicate requests $scope.blockingIP = ipAddress; @@ -1241,11 +1230,12 @@ var dashboardStatsControllerFn = function ($scope, $http, $timeout) { } $scope.blockedIPs[ipAddress] = true; - // Show success notification + // Show success notification (use server message when present, e.g. already-banned + connections closed) if (typeof PNotify !== 'undefined') { + var successText = (responseData.message && responseData.message.length) ? responseData.message : `IP address ${ipAddress} has been permanently banned and added to the firewall. You can manage it in the Firewall > Banned IPs section.`; new PNotify({ title: 'IP Address Banned', - text: `IP address ${ipAddress} has been permanently banned and added to the firewall. You can manage it in the Firewall > Banned IPs section.`, + text: successText, type: 'success', delay: 5000 }); @@ -1361,14 +1351,9 @@ var dashboardStatsControllerFn = function ($scope, $http, $timeout) { return; // Already processing } - if ($scope.blockedIPs[ipAddress]) { - new PNotify({ - title: 'Info', - text: `IP address ${ipAddress} is already banned`, - type: 'info', - delay: 3000 - }); - return; + // Still call API when already in blockedIPs so backend can close active connections + if (!$scope.blockedIPs) { + $scope.blockedIPs = {}; } $scope.blockingIP = ipAddress; @@ -1454,14 +1439,9 @@ var dashboardStatsControllerFn = function ($scope, $http, $timeout) { return; // Already processing } - if ($scope.blockedIPs[ipAddress]) { - new PNotify({ - title: 'Info', - text: `IP address ${ipAddress} is already banned`, - type: 'info', - delay: 3000 - }); - return; + // Still call API when already in blockedIPs so backend can close active connections + if (!$scope.blockedIPs) { + $scope.blockedIPs = {}; } $scope.blockingIP = ipAddress; diff --git a/firewall/firewallManager.py b/firewall/firewallManager.py index d5cba5a81..6db546e11 100644 --- a/firewall/firewallManager.py +++ b/firewall/firewallManager.py @@ -2132,8 +2132,13 @@ class FirewallManager: # Primary path: save to database existing = BannedIP.objects.filter(ip_address=ip, active=True).first() if existing: - msg = 'IP address %s is already banned' % ip - final_dic = {'status': 0, 'error_message': msg, 'error': msg} + # IP already banned: close any active connections from this IP so they cannot keep trying + try: + FirewallUtilities.closeConnectionsFromIP(ip) + except Exception as close_err: + logging.CyberCPLogFileWriter.writeToFile('addBannedIP: closeConnectionsFromIP %s: %s' % (ip, str(close_err))) + msg = 'IP address %s was already banned; any active connections from this IP have been terminated.' % ip + final_dic = {'status': 1, 'message': msg} return HttpResponse(json.dumps(final_dic), content_type='application/json') try: new_ban = BannedIP( @@ -2153,8 +2158,13 @@ class FirewallManager: banned_ips, _ = self._load_banned_ips_store() for banned_ip in banned_ips: if banned_ip.get('ip') == ip and banned_ip.get('active', True): - msg = 'IP address %s is already banned' % ip - final_dic = {'status': 0, 'error_message': msg, 'error': msg} + # IP already banned: close any active connections from this IP so they cannot keep trying + try: + FirewallUtilities.closeConnectionsFromIP(ip) + except Exception as close_err: + logging.CyberCPLogFileWriter.writeToFile('addBannedIP: closeConnectionsFromIP %s: %s' % (ip, str(close_err))) + msg = 'IP address %s was already banned; any active connections from this IP have been terminated.' % ip + final_dic = {'status': 1, 'message': msg} return HttpResponse(json.dumps(final_dic), content_type='application/json') new_banned_ip = { 'id': int(current_time), diff --git a/install/install.py b/install/install.py index 28a9ae38e..975cead87 100644 --- a/install/install.py +++ b/install/install.py @@ -486,7 +486,7 @@ class preFlightsChecks: except Exception as e: self.stdOut(f"Warning: Graphics library fixes failed: {str(e)}", 1) - # Install common dependencies + # Install common dependencies (conntrack-tools for firewall/SSH security connection termination) self.stdOut("Installing common RHEL dependencies...", 1) common_deps = [ 'curl', @@ -500,7 +500,8 @@ class preFlightsChecks: 'cmake', 'pcre2-devel', 'openssl-devel', - 'zlib-devel' + 'zlib-devel', + 'conntrack-tools' ] for dep in common_deps: @@ -706,7 +707,7 @@ class preFlightsChecks: try: self.stdOut("Applying Ubuntu-specific fixes...", 1) - # Install required dependencies + # Install required dependencies (conntrack for firewall/SSH security connection termination) self.stdOut("Installing Ubuntu dependencies...", 1) ubuntu_deps = [ 'software-properties-common', @@ -714,7 +715,8 @@ class preFlightsChecks: 'curl', 'wget', 'gnupg', - 'lsb-release' + 'lsb-release', + 'conntrack' ] for dep in ubuntu_deps: @@ -748,7 +750,7 @@ class preFlightsChecks: try: self.stdOut("Applying Debian-specific fixes...", 1) - # Install required dependencies + # Install required dependencies (conntrack for firewall/SSH security connection termination) self.stdOut("Installing Debian dependencies...", 1) debian_deps = [ 'software-properties-common', @@ -756,7 +758,8 @@ class preFlightsChecks: 'curl', 'wget', 'gnupg', - 'lsb-release' + 'lsb-release', + 'conntrack' ] for dep in debian_deps: diff --git a/install/venvsetup_modules/01_vars_install_required.sh b/install/venvsetup_modules/01_vars_install_required.sh index 419e930b8..4bdc29aa8 100644 --- a/install/venvsetup_modules/01_vars_install_required.sh +++ b/install/venvsetup_modules/01_vars_install_required.sh @@ -384,7 +384,7 @@ if [[ $SERVER_OS == "CentOS" ]] ; then yum clean all yum update -y yum install epel-release -y - yum install -y wget strace htop net-tools telnet curl which bc telnet htop libevent-devel gcc python-devel libattr-devel xz-devel gpgme-devel mariadb-devel curl-devel python-pip git + yum install -y wget strace htop net-tools telnet curl which bc telnet htop libevent-devel gcc python-devel libattr-devel xz-devel gpgme-devel mariadb-devel curl-devel python-pip git conntrack-tools if [[ $DEV == "ON" ]] ; then yum -y install yum-utils yum -y groupinstall development @@ -402,17 +402,17 @@ if [[ $SERVER_OS == "Ubuntu" ]] ; then DEBIAN_VERSION=$(cat /etc/debian_version | cut -d'.' -f1) if [[ $DEBIAN_VERSION -ge 13 ]]; then # Debian 13 (Trixie) package mappings - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python3-mysqldb python3-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python3-gpg python3 python3-setuptools virtualenv python3-dev python3-pip git + DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python3-mysqldb python3-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python3-gpg python3 python3-setuptools virtualenv python3-dev python3-pip git conntrack elif [[ $DEBIAN_VERSION -ge 12 ]]; then # Debian 12 (Bookworm) package mappings - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python3-mysqldb python3-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python3-gpg python3 python3-setuptools virtualenv python3-dev python3-pip git + DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python3-mysqldb python3-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python3-gpg python3 python3-setuptools virtualenv python3-dev python3-pip git conntrack else # Older Debian versions - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git + DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git conntrack fi else # Ubuntu or older Debian with compatible package names - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git + DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git conntrack fi if [[ $DEV == "ON" ]] ; then DEBIAN_FRONTEND=noninteractive apt install -y python3-pip diff --git a/install/venvsetup_monolithic.sh b/install/venvsetup_monolithic.sh index 7116a3f99..80432a4c5 100644 --- a/install/venvsetup_monolithic.sh +++ b/install/venvsetup_monolithic.sh @@ -382,7 +382,7 @@ if [[ $SERVER_OS == "CentOS" ]] ; then yum clean all yum update -y yum install epel-release -y - yum install -y wget strace htop net-tools telnet curl which bc telnet htop libevent-devel gcc python-devel libattr-devel xz-devel gpgme-devel mariadb-devel curl-devel python-pip git + yum install -y wget strace htop net-tools telnet curl which bc telnet htop libevent-devel gcc python-devel libattr-devel xz-devel gpgme-devel mariadb-devel curl-devel python-pip git conntrack-tools if [[ $DEV == "ON" ]] ; then yum -y install yum-utils yum -y groupinstall development @@ -400,17 +400,17 @@ if [[ $SERVER_OS == "Ubuntu" ]] ; then DEBIAN_VERSION=$(cat /etc/debian_version | cut -d'.' -f1) if [[ $DEBIAN_VERSION -ge 13 ]]; then # Debian 13 (Trixie) package mappings - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python3-mysqldb python3-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python3-gpg python3 python3-setuptools virtualenv python3-dev python3-pip git + DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python3-mysqldb python3-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python3-gpg python3 python3-setuptools virtualenv python3-dev python3-pip git conntrack elif [[ $DEBIAN_VERSION -ge 12 ]]; then # Debian 12 (Bookworm) package mappings - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python3-mysqldb python3-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python3-gpg python3 python3-setuptools virtualenv python3-dev python3-pip git + DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python3-mysqldb python3-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python3-gpg python3 python3-setuptools virtualenv python3-dev python3-pip git conntrack else # Older Debian versions - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git + DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git conntrack fi else # Ubuntu or older Debian with compatible package names - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git + DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadb-dev-compat libmariadb-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcom-err2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git conntrack fi if [[ $DEV == "ON" ]] ; then DEBIAN_FRONTEND=noninteractive apt install -y python3-pip diff --git a/install_modules/01_verify_deps.sh b/install_modules/01_verify_deps.sh index c33cbb682..e840b3a03 100644 --- a/install_modules/01_verify_deps.sh +++ b/install_modules/01_verify_deps.sh @@ -92,6 +92,7 @@ install_dependencies() { # AlmaLinux 8 / CentOS 8 / Rocky Linux 8 $PACKAGE_MANAGER install -y ImageMagick gd libicu oniguruma aspell libc-client-devel python3 python3-pip python3-devel 2>/dev/null || true fi + $PACKAGE_MANAGER install -y conntrack-tools 2>/dev/null || print_status "WARNING: conntrack-tools not available, skipping..." echo " ✓ Core packages installed" echo "" @@ -118,6 +119,7 @@ install_dependencies() { apt install -y -qq imagemagick php-gd libicu-dev libonig-dev 2>/dev/null || true apt install -y -qq aspell 2>/dev/null || print_status "WARNING: aspell not available, skipping..." apt install -y -qq libc-client-dev 2>/dev/null || print_status "WARNING: libc-client-dev not available, skipping..." + apt install -y -qq conntrack 2>/dev/null || print_status "WARNING: conntrack not available, skipping..." echo " ✓ Core packages installed" ;; esac diff --git a/modules/deps/debian_deps.sh b/modules/deps/debian_deps.sh index 8852164b5..621f1a305 100644 --- a/modules/deps/debian_deps.sh +++ b/modules/deps/debian_deps.sh @@ -41,8 +41,8 @@ install_essential_packages() { print_status "$BLUE" "Installing essential packages..." - # Common essential packages for all Debian variants - local essential_packages="curl wget git unzip tar gzip bzip2" + # Common essential packages for all Debian variants (conntrack for firewall/SSH security connection termination) + local essential_packages="curl wget git unzip tar gzip bzip2 conntrack" apt install -y -qq $essential_packages 2>/dev/null || { print_status "$YELLOW" "Some essential packages failed to install, continuing..." diff --git a/modules/deps/rhel_deps.sh b/modules/deps/rhel_deps.sh index 080720428..f1c8f1fa3 100644 --- a/modules/deps/rhel_deps.sh +++ b/modules/deps/rhel_deps.sh @@ -133,8 +133,8 @@ install_additional_packages() { print_status "$BLUE" "Installing additional packages..." - # Additional packages that might be needed - local additional_packages="git wget curl unzip tar gzip bzip2" + # Additional packages that might be needed (conntrack-tools for firewall/SSH security connection termination) + local additional_packages="git wget curl unzip tar gzip bzip2 conntrack-tools" $package_manager install -y $additional_packages 2>/dev/null || { print_status "$YELLOW" "Some additional packages not available, continuing..." diff --git a/plogical/firewallUtilities.py b/plogical/firewallUtilities.py index 78c8995c0..69180f21d 100644 --- a/plogical/firewallUtilities.py +++ b/plogical/firewallUtilities.py @@ -182,6 +182,25 @@ class FirewallUtilities: logging.CyberCPLogFileWriter.writeToFile(f"Error checking if IP {ip_address} is blocked: {str(e)}") return False + @staticmethod + def closeConnectionsFromIP(ip_address): + """ + Try to close/drop existing connections from the given IP so the remote host + cannot keep trying (e.g. when IP is already banned and admin clicks Ban again). + Uses conntrack when available; safe to call even if no connections exist. + """ + try: + # conntrack -D -s IP deletes connection tracking entries from this source IP, + # which drops existing TCP connections from that IP (kernel sends RST or they time out). + command = "conntrack -D -s %s 2>/dev/null" % ip_address + result = ProcessUtilities.executioner(command) + logging.CyberCPLogFileWriter.writeToFile("closeConnectionsFromIP %s: conntrack returned %s" % (ip_address, result)) + # executioner returns 1 on success; conntrack may also return 0 when no entries found (still ok) + return True, "Connections from %s have been closed" % ip_address + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile("closeConnectionsFromIP error for %s: %s" % (ip_address, str(e))) + return False, str(e) + @staticmethod def getBlockedIPs(): """ diff --git a/static/baseTemplate/custom-js/system-status.js b/static/baseTemplate/custom-js/system-status.js index 9924462f9..37720ec3e 100644 --- a/static/baseTemplate/custom-js/system-status.js +++ b/static/baseTemplate/custom-js/system-status.js @@ -1162,19 +1162,8 @@ var dashboardStatsControllerFn = function ($scope, $http, $timeout) { return; // Already processing this IP } - // Check if already blocked - if ($scope.blockedIPs && $scope.blockedIPs[ipAddress]) { - console.log('IP already blocked:', ipAddress); - if (typeof PNotify !== 'undefined') { - new PNotify({ - title: 'Info', - text: `IP address ${ipAddress} is already banned`, - type: 'info', - delay: 3000 - }); - } - return; - } + // Do not early-return when IP is already in blockedIPs: still call the API so the + // backend can close any active connections from this IP (already-banned path). // Set blocking flag to prevent duplicate requests $scope.blockingIP = ipAddress; @@ -1241,11 +1230,12 @@ var dashboardStatsControllerFn = function ($scope, $http, $timeout) { } $scope.blockedIPs[ipAddress] = true; - // Show success notification + // Show success notification (use server message when present, e.g. already-banned + connections closed) if (typeof PNotify !== 'undefined') { + var successText = (responseData.message && responseData.message.length) ? responseData.message : `IP address ${ipAddress} has been permanently banned and added to the firewall. You can manage it in the Firewall > Banned IPs section.`; new PNotify({ title: 'IP Address Banned', - text: `IP address ${ipAddress} has been permanently banned and added to the firewall. You can manage it in the Firewall > Banned IPs section.`, + text: successText, type: 'success', delay: 5000 }); @@ -1361,14 +1351,9 @@ var dashboardStatsControllerFn = function ($scope, $http, $timeout) { return; // Already processing } - if ($scope.blockedIPs[ipAddress]) { - new PNotify({ - title: 'Info', - text: `IP address ${ipAddress} is already banned`, - type: 'info', - delay: 3000 - }); - return; + // Still call API when already in blockedIPs so backend can close active connections + if (!$scope.blockedIPs) { + $scope.blockedIPs = {}; } $scope.blockingIP = ipAddress; @@ -1454,14 +1439,9 @@ var dashboardStatsControllerFn = function ($scope, $http, $timeout) { return; // Already processing } - if ($scope.blockedIPs[ipAddress]) { - new PNotify({ - title: 'Info', - text: `IP address ${ipAddress} is already banned`, - type: 'info', - delay: 3000 - }); - return; + // Still call API when already in blockedIPs so backend can close active connections + if (!$scope.blockedIPs) { + $scope.blockedIPs = {}; } $scope.blockingIP = ipAddress;