diff --git a/README.md b/README.md index 739c32c..674928d 100644 --- a/README.md +++ b/README.md @@ -1,132 +1,256 @@ -# **Fail2Ban UI** +# Fail2Ban UI -πŸš€ **Fail2Ban-UI** is a Swiss-made **web-based management interface** for [Fail2Ban](https://www.fail2ban.org/). -It provides an intuitive dashboard to **monitor, configure, and manage Fail2Ban** instances in real time, supporting both local and remote Fail2ban servers. +
-Developed by **[Swissmakers GmbH](https://swissmakers.ch)**. +**Enterprise-Grade Intrusion Detection System Management Platform** -## **✨ Features** +[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![Go Version](https://img.shields.io/badge/Go-1.21+-00ADD8?logo=go)](https://golang.org/) +[![Platform](https://img.shields.io/badge/Platform-Linux-lightgrey)](https://www.linux.org/) -βœ… **Multi-Server Management** -- Manage **multiple Fail2ban servers** from a single interface -- Support for **local**, **SSH**, and **API agent** connections -- Add, edit, delete, and configure remote Fail2ban instances -- Switch between servers seamlessly via server selector -- Test connections before saving server configurations +*Swiss-made open-source solution for centralized Fail2Ban management across distributed infrastructure* -βœ… **Real-time Dashboard** -- View **all active Fail2Ban jails** and **banned IPs** in a clean UI -- Displays **live ban events** from all configured servers -- **Internal log overview** with ban events stored in SQLite database -- Statistics per server and global overview -- Track ban events with country information and log lines +[Features](#-features) β€’ [Quick Start](#-quick-start) β€’ [Documentation](#-documentation) β€’ [Screenshots](#-screenshots) β€’ [Security Notes](#-security-notes) -βœ… **Ban & Unban Management** -- **Unban IPs** directly via the UI (works for local and remote servers) -- **Search** for banned IPs across all active jails -- View ban history with detailed information (IP, jail, hostname, country, logs) - -βœ… **Fail2Ban Configuration Management** -- **Edit & Save** active Fail2Ban jail/filter configs (local and remote) -- **Manage Jails**: Enable/disable jails on local and remote servers -- **Filter Debug**: Test filters against log lines using `fail2ban-regex` -- Get automatic **email alerts** for specific country-based bans -- Configure own SMTP settings for email alerts (STARTTLS only) -- Adjust default ban time, find time, and set ignore IPs -- Auto-detects changes and prompts for **reload** to apply -- Enable debug-mode for detailed module logs - -βœ… **SSH Remote Management** -- Connect to remote Fail2ban servers via SSH -- Automatic deployment of custom action for ban notifications -- Passwordless sudo support for seamless operation -- Configurable SSH key selection per server -- Test SSH connections before saving - -βœ… **API Agent Support** (Future) -- Connect to remote Fail2ban instances via lightweight API agents -- Secure communication using shared secrets -- Push ban notifications from remote servers to the UI - -βœ… **Persistent Storage** -- **SQLite database** for storing server configurations and ban events -- All settings and server configurations persist across restarts -- Automatic migration from legacy JSON-based settings - -βœ… **Mobile-Friendly & Responsive UI / Fast** -- Optimized for **mobile & desktop** -- Powered by **Tailwind CSS** (works offline when built locally) -- **Go-based backend** ensures minimal resource usage -- Parallel execution for improved performance on remote connections - -βœ… **Systemd & SELinux Support** -- **Run as a systemd service** (Standalone or Container) -- **Supports SELinux** for secure execution (also container version) - -βœ… **Internationalization** -- Multi-language support (English, German, French, Italian, Spanish) -- Easy to add new languages via JSON translation files - -## **πŸ“Έ Screenshots** -Some images from the UI in action: - -| Dashboard | Server Management | Filter Debug | -|-----------|-------------------|--------------| -| ![Dashboard](./screenshots/0_Dashboard.jpg) | ![Server Management](./screenshots/1_Dashboard_search.jpg) | ![Filter Debug](./screenshots/3_Dashboard_edit_filter.jpg) | - -πŸ“Œ **More screenshots are found [here](./screenshots/)** +
--- -## **πŸ“₯ Installation & Deployment** +## 🎯 Overview -Fail2Ban-UI can be currently deployed in **two main ways**: -**1️⃣ Running from local source** -**2️⃣ Running as a container** +**Fail2Ban UI** is a production-ready, enterprise-grade web-based management platform to create a distributed Fail2Ban intrusion detection system. Designed for organizations managing single or multible fail2ban instances. It provides centralized ban-control, real-time monitoring and alerting across all servers. -### **πŸ”Ή Method 1: Running from Local Source** -To install and run directly on the system: -πŸ“Œ **[Follow the basic systemd setup guide](./deployment/systemd/README.md)** +### Why we Need Fail2Ban UI -```bash -git clone https://github.com/swissmakers/fail2ban-ui.git /opt/fail2ban-ui -cd /opt/fail2ban-ui +Modern enterprises face increasing security challenges with generally distributed infrastructure, cloud deployments, and multi-location operations. Traditional Fail2Ban management requires a manual SSH access to individual servers, manual configuration changes, and lacks centralized visibility. **Fail2Ban UI solves these challenges** by providing: -# Build Tailwind CSS for production (optional but recommended) -# This requires Node.js and npm to be installed -./build-tailwind.sh +- **Centralized Management**: Control multible Fail2Ban instances from a single interface +- **Real-Time Visibility**: Monitor ban / security events across your entire proxy / webserver infrastructure (also planned to ingest all via connector to SIEM) +- **Operational Efficiency**: Reduce administrative overhead with automated workflows e.g. auto-banning for recurring IPs, directly on the firewall. -# Build Go application -go build -o fail2ban-ui ./cmd/server/main.go -... -``` - -**πŸ“Œ Note on Tailwind CSS:** -- For **production/offline use**, build Tailwind CSS using `./build-tailwind.sh` before building the application -- This creates a local CSS file at `pkg/web/static/tailwind.css` that works offline -- The build script uses **Tailwind CSS v3** (latest v3.x, matches CDN version) -- If the local file is not found, the application will automatically fall back to the CDN (requires internet connection) -- The build script requires **Node.js** and **npm** to be installed -- The script works for both fresh installations and existing development environments +**Developed by [Swissmakers GmbH](https://swissmakers.ch)** β€” Trusted by enterprises swisswide for mission-critical security infrastructure. --- -### **πŸ”Ή Method 2: Running as a Container** -For an easy containerized deployment: -πŸ“Œ **[Follow the basic container deployment guide](./deployment/container/README.md)** +## πŸš€ Current Features -You can either build the image locally or pull the **official, automatically built image** from the Swissmakers registry. -Every merge into the `main` branch triggers a new build that is published at: +### 🏒 Multi-Server Management + +**Centralized Control Across Distributed Fail2Ban Instances** + +- **Unified Dashboard**: Monitor and manage multiple Fail2Ban servers from a single interface +- **Flexible Connectivity**: Support for local, SSH, and API agent connections form Fail2Ban-UI to other Fail2Ban Instances +- **Connection Testing**: Validate backend connectivity before activate them on the management UI + +**Use Cases:** +- Multi-datacenter deployments with several reverse proxies +- Hybrid cloud environments +- Also integrate external Webservers + +### πŸ“Š Real-Time Security Intelligence + +**Malicious Actor Visibility and Analytics** + +- **Live Event Monitoring**: Real-time ban events from all configured / connected servers +- **Historical Analysis**: SQLite-based event storage with advanced querying +- **Geographic Intelligence**: Country-based threat analysis and visualization (Will be later send to SIEM - planned) +- **Recurring Threat Detection**: Identify persistent attackers across time windows +- **Ban Insights Dashboard**: Aggregated statistics +- **Event Correlation**: Track attack patterns across multiple servers and jails (planned with Elasticsearch) + +**Business Value:** +- Faster threat response times +- Proactive security posture management +- Compliance reporting and audit trails + +### πŸ›‘οΈ Advanced Ban Management + +**IP Blocking and Unblocking** + +- **Cross-Jail Search**: Find banned IPs across all active jails instantly +- **Bulk Operations**: Manage multiple bans simultaneously +- **Ban History**: Complete audit trail with timestamps, ban-reasons, and context like whois information of the attacker +- **Whitelist Management**: Configure trusted IPs and networks +- **Automatic Unban**: Time-based ban expiration with configurable policies +- **Permanent Ban for Reccuring IPs**: Set a threshold in the Settings, after what amount of Bans a IP is automatically banned permanently on Firewall (Currently Supported Mikrotik/PFSense) + + +### βš™οΈ Configuration Management + +- **Remote Configuration**: Edit Fail2Ban jail and filter configurations remotely +- **Jail Management**: Enable/disable jails across multiple servers +- **Filter Testing**: Debug and validate filters using `fail2ban-regex` before enabeling +- **Template Management**: Standardize configurations across server groups (planned) + + +### πŸ“§ Alerting + +**Security Notifications** + +- **Multi-Language Email Alerts**: Localized notifications in currently 5 supported languages +- **Country-Based Filtering**: Alert only on threats from specific geographic regions to reduce alert fatigue +- **SMTP Integration**: Support M365 Mail-Servers with STARTTLS +- **Emai Templates**: Currentls features a Modern and classic email design (more to come) +- **Alert Aggregation**: This feature is planned for the SIEM-modul + +### πŸ” Enterprise Security + +**Hardened for Production Environments** + +- **SELinux Support**: Full compatibility with SELinux-enabled systems and pre-created custom policies +- **Container Security**: Secure containerized deployment with proper best-practisies +- **Least Privilege**: Only minimal permissions are used using FACLs and special sudo-rules +- **Audit Logging**: Comprehensive logging for compliance and forensics also in the future planned to ingest into a Elastic SIEM +- **Encrypted Communications Only**: Secure data transmission for all remote operations will be enforced + +### 🌐 Internationalization + +- **Multi-Language UI**: English, German (DE/CH), French, Italian, Spanish +- **Localized Content**: All user-facing content translated +- **RTL Support Ready**: Architecture supports right-to-left languages +- **Easy Extension**: Simple JSON-based translation system + +### πŸ“± Modern User Experience + +- **Responsive Design**: Full functionality also on mobile devices +- **Progressive Web App**: Works also in a no-internet / offline and restricted environment with local CSS/JS builds only +- **Fast Performance**: Go-based backend with minimal resource footprint + +--- + +## πŸ“Έ Screenshots + +### Dashboard Overview +The central command center for monitoring all Fail2Ban instances and security events. + +![Dashboard](./screenshots/0_Dashboard.jpg) + +### Server Management +Add, configure, and manage multiple Fail2Ban servers from the "Manage Servers" modal. + +![Server Management](./screenshots/0.1_Dashboard_Manage_Servers.jpg) + +### IP Search and Management +Quickly locate and review / manage banned IPs across all jails and servers. + +![IP Search](./screenshots/1_Dashboard_search.jpg) + +### Unban Operations +One-click unban action with confirmation dialog. + +![Unban IP](./screenshots/2_Dashboard_unban_IP.jpg) + +### Configuration Editor +Edit Fail2Ban jail and filter configurations (with syntax highlighting - planned) and validation. + +![Configuration Editor](./screenshots/3_Dashboard_edit_filter.jpg) + +### Service Management +Reload or restart Fail2Ban services when needed, with integrated change detection. + +![Service Reload](./screenshots/4_Dashboard_Reload.jpg) + +### Filter Debugging +Test and validate Fail2Ban filters using `fail2ban-regex`. + +![Filter Debug](./screenshots/5_Filter_Debug.jpg) + +### Settings and Configuration +Comprehensive settings management for alerts, advanced banning, and system preferences. + +![Settings](./screenshots/6_Settings.jpg) + +![Settings Advanced](./screenshots/7_Settings_Bottom.jpg) + +--- + +## πŸ—οΈ Architecture + +### System Components ``` -registry.swissmakers.ch/infra/fail2ban-ui:latest + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Fail2Ban UI Web Interface β”‚ + β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ + β”‚ β”‚ Dashboard β”‚ β”‚ Management β”‚ β”‚ Settings β”‚ β”‚ + β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Go Backend API Server β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Fail2Ban UI (Backend) β”‚--->β”‚ Send Alerts via Mail β”‚ β”‚ +β”‚ β”‚ - Gin handlers + REST API β”‚ β”‚ (planned: Elastic) β”‚ β”‚ +β”‚ β”‚ - Vanilla JS + Tailwind UI β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ ->β”‚ - SQLite storage β”‚ β”‚ +β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ β”‚ Connector Manager and β”‚-------β”‚ Integrations β”‚ β”‚ +β”‚ β”‚ β”‚ handlers / actions β”‚ β”‚ Mikrotik / pfSense β”‚ β”‚ +β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ +β””β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ + β”‚ β–Ό +β”Œβ”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ β”‚ Connection to remote Server β”‚ +β”‚ β”‚ ───────────────────────────── β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β–Ό β–Ό β–Ό β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ β”‚ Local β”‚ β”‚ SSH β”‚ β”‚ API β”‚ β”‚ +β”‚ β”‚ β”‚ Server β”‚ β”‚ Server β”‚ β”‚ Agent β”‚ β”‚ +β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ β”‚ Fail2Ban instances on Reverse Proxies β”‚ β”‚ +β”‚ β”‚ β”‚ or remote / local Webserver β”‚ β”‚ +β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ β”‚ Report Alerts back to β”‚ β”‚ +β”‚ <----------β”‚ Fail2Ban-UI REST with β”‚ β”‚ +β”‚ β”‚ custom action β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` + +### Technology Stack + +- **Backend**: Go 1.21+ (Golang) +- **Frontend**: Vanilla JavaScript, Tailwind CSS +- **Database**: SQLite (embedded) +- **Container Runtime**: Podman/Docker compatible +- **Service Management**: systemd +- **Security**: SELinux compatible + +--- + +## πŸš€ Quick Start + +### Prerequisites + +- **Operating System**: Linux (RHEL 8+, Ubuntu 20.04+, Debian 11+, or containerized) +- **Fail2Ban**: At least version 0.10+ installed and configured +- **Go**: Version 1.21+ (only for source builds) +- **Node.js**: Version 16+ (for Tailwind CSS builds) +- **Permissions**: Root access to configure FACL and sudo-rules and Fail2Ban socket access + +### Installation Methods + +#### Method 1: Container Deployment (Recommended for Production) + +**Pull the official image:** ```bash -# Pull the latest pre-built image podman pull registry.swissmakers.ch/infra/fail2ban-ui:latest +``` -# Run the container +**Run the container:** +```bash podman run -d \ --name fail2ban-ui \ --network=host \ @@ -138,183 +262,421 @@ podman run -d \ registry.swissmakers.ch/infra/fail2ban-ui:latest ``` -> **πŸ“Œ Note:** The container can also be managed as a **systemd service**. +**πŸ“– [Complete Container Deployment Guide](./deployment/container/README.md)** + +#### Method 2: Systemd Service (Standalone) + +**Clone and build:** +```bash +git clone https://github.com/swissmakers/fail2ban-ui.git /opt/fail2ban-ui +cd /opt/fail2ban-ui + +# Build Tailwind CSS (optional, for offline use) +./build-tailwind.sh + +# Build Go application +go build -o fail2ban-ui ./cmd/server/main.go +``` + +**πŸ“– [Complete Systemd Setup Guide](./deployment/systemd/README.md)** + +### First Launch + +1. **Access the Web Interface** + - Navigate to `http://localhost:8080` (or your configured port) + - Default port: `8080` (configurable in settings) + +2. **Add Your First Server** + - **Local Server**: Enable the local connector if Fail2Ban runs on the same host + - **Remote Server**: Add via SSH or API agent connection + +3. **Configure Settings** + - Set up email alerts (optional) + - Configure language preferences + - Adjust security settings --- -## **πŸš€ Getting Started** +## πŸ“š Documentation -### **First Launch** -1. After starting Fail2Ban-UI, access the web interface at `http://localhost:8080` (or your configured port) -2. You'll be prompted to **add your first Fail2ban server** or **enable the local connector** -3. For local connections: Enable the local connector if Fail2ban is running on the same host -4. For remote connections: Add a new server via SSH or API agent +### Deployment Guides -### **Adding a Server** +- **[Container Deployment](./deployment/container/README.md)**: Complete guide for containerized deployments +- **[Systemd Service Setup](./deployment/systemd/README.md)**: Standalone installation and service configuration +- **[SELinux Configuration](./deployment/container/SELinux/)**: Security policies for SELinux-enabled systems -#### **Local Server** -- The local connector is available by default but **disabled** initially -- Enable it in the server management settings if Fail2ban runs on the same host -- Requires root access or passwordless sudo to the Fail2ban socket +### Configuration -#### **SSH Server** -1. Go to **Settings** β†’ **Manage Servers** -2. Click **Add Server** -3. Select **SSH** as connection type -4. Fill in: - - **Name**: A descriptive name for this server - - **Host**: IP address or hostname - - **Port**: SSH port (default: 22) - - **SSH User**: Username for SSH connection - - **SSH Key**: Select an SSH key from your `~/.ssh/` directory (or `/config/.ssh/` when running in a container) -5. Click **Test Connection** to verify before saving -6. The UI will automatically deploy the custom action for ban notifications +#### Adding a Local Server -**Requirements for SSH connections:** +The local connector allows managing Fail2Ban on the same host where Fail2Ban UI runs. + +**Requirements:** +- Fail2Ban installed and running +- Special FACL Rules and passwordless sudo to `/usr/bin/fail2ban-client` +- Socket access: `/var/run/fail2ban/fail2ban.sock` + +**Enable in UI:** +1. Navigate to **Settings** β†’ **Manage Servers** +2. Enable **Local Connector** +3. Test connection to verify access + +#### Adding an SSH Server + +Connect to remote Fail2Ban instances via SSH for centralized management. + +**Prerequisites:** - SSH key-based authentication (passwordless login) - Network connectivity from UI host to remote server -- The SSH user must have: - - Sudo access to run `systemctl restart fail2ban` and `/usr/bin/fail2ban-client` (configured via sudoers) - - File system ACLs on `/etc/fail2ban` for read/write access to configuration files (no sudo needed for file operations) +- Service account with appropriate permissions + +**Recommended Service Account Setup:** -**Recommended SSH setup (using service account with ACLs):** ```bash -# Create service account +# Create dedicated service account sudo useradd -r -s /bin/bash sa_fail2ban -# Configure sudoers for fail2ban-client and systemctl restart +# Configure sudoers for Fail2Ban operations sudo visudo -f /etc/sudoers.d/fail2ban-ui -# Add: +``` + +Add the following sudoers configuration: +``` sa_fail2ban ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client * sa_fail2ban ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart fail2ban +``` -# Set ACLs for /etc/fail2ban directory +**Set file system ACLs:** +```bash +# Grant read/write access to Fail2Ban configuration directory sudo setfacl -Rm u:sa_fail2ban:rwX /etc/fail2ban sudo setfacl -dRm u:sa_fail2ban:rwX /etc/fail2ban ``` -This setup provides fine-grained permissions without requiring full passwordless sudo access. +**Add Server in UI:** +1. Navigate to **Settings** β†’ **Manage Servers** +2. Click **Add Server** +3. Select **SSH** connection type +4. Configure: + - **Name**: Descriptive server identifier + - **Host**: IP address or hostname + - **Port**: SSH port (default: 22) + - **SSH User**: Service account username + - **SSH Key**: Select from `~/.ssh/` directory +5. Click **Test Connection** to verify +6. Save configuration -#### **API Agent Server** (Future) -- API agent support is planned for future releases -- Will allow connecting to remote Fail2ban instances via a lightweight API service +**Security Benefits:** +- Least privilege access model +- No full sudo access required +- Fine-grained file system permissions +- Audit trail via sudo logs --- -## **πŸ”’ Security Considerations** +### πŸ”’ Security Notes -- Fail2Ban-UI requires **root privileges** or **passwordless sudo** to interact with Fail2Ban sockets (for local connections). -- For SSH connections, use a **dedicated service account** with: - - **Limited sudo access** (only for `fail2ban-client` and `systemctl restart fail2ban`) - - **File system ACLs** on `/etc/fail2ban` for configuration file access (more secure than full sudo) -- **Restrict access** using **firewall rules** or a **reverse proxy** with authentication. -- Ensure that Fail2Ban logs/configs **aren't exposed publicly**. -- For SSH connections, use **SSH key-based authentication** and restrict SSH access. -- Store SSH keys securely and use strong passphrases. -- The SQLite database contains sensitive information - ensure proper file permissions. +#### Network Security Best Practices + +- **Reverse Proxy**: Use nginx or Apache as reverse proxy with SSL/TLS termination to secure the Fail2ban-UI +- **VPN Access**: Require VPN connection for access to Fail2Banu-UI only +- **IP Whitelisting**: Restrict access to specific IPs / ranges e.g. internal IT + +#### Authentication and Authorization + +- **SSH Key Management**: Use strong SSH keys like 4096-bit RSA or even better Ed25519. When using RSA no smaler bit size please. +- **Service Accounts**: Use dedicated service accounts, not personal accounts +- **Sudoers Configuration**: Minimal sudo permissions, no passwordless full sudo + +#### Data Protection + +- **Database Permissions**: Restrict SQLite database file permissions (600) +- **Log Files**: Secure log file access and don't forget to setup a rotation +- **Backup Encryption**: It's always a good idea, to encrypt backups of configuration and database files + +#### SELinux Configuration + +For SELinux-enabled systems, apply the required policies: -For **SELinux users**, apply the **Fail2Ban-UI security policies**: ```bash -# Basic rule to allow fail2ban access the fail2ban-ui API +# Basic rule to allow Fail2Ban to access the UI API semodule -i fail2ban-curl-allow.pp -# Also needed for a secure container deployment + +# Container deployment policies semodule -i fail2ban-container-ui.pp semodule -i fail2ban-container-client.pp ``` ---- - -## **πŸ› οΈ Troubleshooting** - -### **UI not accessible?** -- Ensure **port 8080** (or your configured port) is open: - ```bash - sudo firewall-cmd --add-port=8080/tcp --permanent - sudo firewall-cmd --reload - ``` -- Check logs: - ```bash - journalctl -u fail2ban-ui.service -f - ``` - -### **No servers configured?** -- On first launch, you need to either: - - Enable the local connector (if Fail2ban runs locally) - - Add a remote server via SSH or API agent -- Go to **Settings** β†’ **Manage Servers** to configure your first server - -### **SSH connection issues?** -- Verify SSH key authentication works manually: - ```bash - ssh -i ~/.ssh/your_key user@remote-host - ``` -- Ensure the SSH user has proper permissions: - - Check sudo access for `fail2ban-client` and `systemctl restart fail2ban`: - ```bash - sudo -l -U sa_fail2ban - ``` - - Verify ACLs on `/etc/fail2ban`: - ```bash - getfacl /etc/fail2ban - ``` -- Check debug mode in settings for detailed error messages -- Verify the SSH user has access to `/var/run/fail2ban/fail2ban.sock` - -### **Local connector not working?** -- Ensure Fail2ban is running: `sudo systemctl status fail2ban` -- Check socket permissions: `ls -la /var/run/fail2ban/fail2ban.sock` -- Verify the UI has access (runs as root or has sudo permissions) - -### **Database issues?** -- The SQLite database is stored in the application directory -- Check file permissions if you see database errors -- Backup the database before major updates +**πŸ“– [SELinux Policies Documentation](./deployment/container/SELinux/)** --- -## **πŸ“Š Database** +### πŸ—„οΈ Database Notes -Fail2Ban-UI uses an embedded **SQLite database** (`fail2ban-ui.db`) to store: -- **Server configurations**: All configured Fail2ban servers (local, SSH, API agent) -- **Application settings**: UI preferences, SMTP settings, etc. -- **Ban events**: Historical ban records with IP, jail, hostname, country, and log lines +#### SQLite Database Schema -The database is automatically created on first launch and migrated from legacy JSON settings if present. +Fail2Ban UI uses an embedded SQLite database (`fail2ban-ui.db`) for persistent storage: + +**Tables:** +- **`servers`**: Server configurations (local, SSH, API agent) +- **`app_settings`**: Application preferences and settings of Fail2Ban-UI +- **`ban_events`**: Historical ban records with full context +- **`permanent_blocks`**: Permanent block records for integrations + +**Data Retention:** +- Ban events are stored indefinitely (configurable) +- Automatic database migrations on version updates +- Backup recommended before major updates --- -## **🌐 Internationalization** +### 🌍 Internationalization Notes -Fail2Ban-UI supports multiple languages: -- English (en) -- German (de, de_ch) -- French (fr) -- Italian (it) -- Spanish (es) +#### Currently Supported Languages -Change the language in **Settings** β†’ **Language**. New languages can be added by creating translation files in `internal/locales/`. +- **English** (en) - Default +- **German** (de, de_CH) - Standard and Swiss variants +- **French** (fr) +- **Italian** (it) +- **Spanish** (es) + +#### Adding New Languages + +1. Create translation file: `internal/locales/{language_code}.json` +2. Copy structure from `internal/locales/en.json` +3. Translate all key-value pairs +4. Test in UI: **Settings** β†’ **Language** β†’ Select new language + +**Translation File Structure:** +```json +{ + "page.title": "Fail2ban UI Dashboard", + "nav.dashboard": "Dashboard", + "nav.settings": "Settings", + ... +} +``` --- -## **🀝 Contributing** -We welcome **pull requests** and **feature suggestions**! +### πŸ“Š API Reference -1. **Fork** this repository -2. **Create** a new branch: +Fail2Ban UI provides a RESTful API for programmatic access: + +#### Endpoints + +**Server Management:** +- `GET /api/servers` - List all configured servers +- `POST /api/servers` - Add or update server +- `DELETE /api/servers/:id` - Remove server +- `POST /api/servers/:id/test` - Test server connection + +**Jail Management:** +- `GET /api/summary` - Get summary of all jails +- `POST /api/jails/:jail/unban/:ip` - Unban IP address +- `GET /api/jails/manage` - List jail management status +- `POST /api/jails/manage` - Update jail enabled states + +**Configuration:** +- `GET /api/jails/:jail/config` - Get jail/filter configuration +- `POST /api/jails/:jail/config` - Update jail/filter configuration + +**Events and Analytics:** +- `GET /api/events/bans` - List ban events +- `GET /api/events/bans/stats` - Get ban statistics +- `GET /api/events/bans/insights` - Get ban insights and analytics + +**Settings:** +- `GET /api/settings` - Get application settings +- `POST /api/settings` - Update application settings +- `POST /api/settings/test-email` - Test email configuration + +**Filter Debugging:** +- `GET /api/filters` - List available filters +- `POST /api/filters/test` - Test filter against log lines + +**Service Control:** +- `POST /api/fail2ban/restart` - Restart Fail2Ban service + +**Notifications:** +- `POST /api/ban` - Receive ban notification from Fail2Ban + +--- + +## πŸ› οΈ Troubleshooting + +### Common Issues + +#### UI Not Accessible + +**Symptoms:** Cannot access web interface + +**Solution / Check:** +```bash +# Check if service is running +systemctl status fail2ban-ui + +# Check firewall rules +sudo firewall-cmd --list-ports +sudo firewall-cmd --add-port=8080/tcp --permanent +sudo firewall-cmd --reload + +# Check logs +journalctl -u fail2ban-ui.service -f +``` + +#### No Servers Configured + +**Symptoms:** Empty dashboard, no servers visible + +**Solution / Check:** +1. Navigate to **Settings** β†’ **Manage Servers** +2. Enable **Local Connector** (if Fail2Ban runs locally) +3. Add remote server via SSH or API agent +4. Verify server connection status + +#### SSH Connection Issues + +**Symptoms:** Cannot connect to remote server + +**Solution / Check:** +```bash +# Test SSH connection manually +ssh -i ~/.ssh/your_key user@remote-host + +# Verify SSH user permissions +sudo -l -U sa_fail2ban + +# Check ACLs on /etc/fail2ban +getfacl /etc/fail2ban + +# Enable debug mode in UI settings for detailed error messages +``` + +#### Local Connector Not Working + +**Symptoms:** Local server shows as disconnected + +**Solution / Check:** +```bash +# Verify Fail2Ban is running +sudo systemctl status fail2ban + +# Check socket permissions +ls -la /var/run/fail2ban/fail2ban.sock + +# Verify UI has access (runs as root or has sudo permissions) +sudo fail2ban-client status +``` + +#### Database Errors + +**Symptoms:** Database-related errors in logs + +**Solution / Check:** +```bash +# Check database file permissions +ls -la /opt/fail2ban-ui/fail2ban-ui.db + +# Verify database integrity +sqlite3 /opt/fail2ban-ui/fail2ban-ui.db "PRAGMA integrity_check;" + +# Backup and recreate if corrupted +cp fail2ban-ui.db fail2ban-ui.db.backup +``` + +--- + +## 🀝 Contributing + +We welcome contributions from the community! Whether it's bug fixes, feature enhancements, or documentation improvements, your contributions help make Fail2Ban UI better for everyone. + +### How to Contribute + +1. **Fork the Repository** ```bash - git checkout -b feature/my-feature + git clone https://github.com/swissmakers/fail2ban-ui.git + cd fail2ban-ui ``` -3. **Commit** your changes: - ```bash - git commit -m "Add new feature" - ``` -4. **Push** to the branch: - ```bash - git push origin feature/my-feature - ``` -5. **Open** a Pull Request +2. **Create a Feature Branch** + ```bash + git checkout -b feature/your-feature-name + ``` -## **πŸ“œ License** -Fail2Ban-UI is licensed under **GNU GENERAL PUBLIC LICENSE, Version 3**. -See [`LICENSE`](./LICENSE) for details. +3. **Make Your Changes** + - Follow Go coding standards + - Add tests for new features + - Update documentation as needed + +4. **Commit Your Changes** + ```bash + git commit -m "Add: Description of your feature" + ``` + +5. **Push and Create Pull Request** + ```bash + git push origin feature/your-feature-name + ``` + +### Contribution Guidelines + +- **Code Style**: Follow Go standard formatting (`gofmt`) +- **Testing**: Test your changes thoroughly +- **Documentation**: Update README and inline documentation +- **Commit Messages**: Use clear, descriptive commit messages +- **Pull Requests**: Provide detailed description of changes + +--- + +## πŸ“œ License + +Fail2Ban UI is licensed under the **GNU General Public License v3.0 (GPL-3.0)**. + +This means: +- βœ… **Free to use** in commercial and non-commercial projects +- βœ… **Free to modify** and distribute +- βœ… **Source code available** for inspection and auditing +- ⚠️ **Copyleft**: Modifications must be released under the same license + +**Full License Text:** [LICENSE](./LICENSE) + +--- + +## 🏒 Enterprise Support + +### Professional Services + +**Swissmakers GmbH** offers professional services for Fail2Ban UI: + +- **Enterprise Deployment**: Custom deployment and configuration +- **Training and Support**: On-site or remote training sessions +- **Custom Development**: Feature development and integrations +- **Security Audits**: Security assessment and hardening + +**Contact:** [https://swissmakers.ch](https://swissmakers.ch) + +### Community Support + +- **GitHub Issues**: [Report bugs and request features](https://github.com/swissmakers/fail2ban-ui/issues) +- **Documentation**: Comprehensive guides and API reference +- **Community**: Join discussions and share experiences + +--- + +## πŸ™ Acknowledgments + +Fail2Ban UI is built on the foundation of the excellent [Fail2Ban](https://www.fail2ban.org/) project and the open-source community. + +**Special Thanks:** +- Fail2Ban developers and contributors +- Go community and ecosystem +- All contributors and users of Fail2Ban UI + +--- + +
+ +**Fail2Ban UI** β€” *Enterprise-Grade Intrusion Detection System Management* + +
\ No newline at end of file diff --git a/go.mod b/go.mod index ae19709..a51c213 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/gin-gonic/gin v1.10.0 github.com/go-playground/validator/v10 v10.26.0 github.com/oschwald/maxminddb-golang v1.13.1 + golang.org/x/crypto v0.33.0 modernc.org/sqlite v1.33.1 ) @@ -33,7 +34,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.13.0 // indirect - golang.org/x/crypto v0.33.0 // indirect golang.org/x/net v0.34.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect diff --git a/go.sum b/go.sum index 31b9cf1..c766f63 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= diff --git a/internal/locales/de.json b/internal/locales/de.json index 192749d..f1ec653 100644 --- a/internal/locales/de.json +++ b/internal/locales/de.json @@ -126,7 +126,7 @@ "settings.advanced.title": "Erweiterte Aktionen fΓΌr WiederholungstΓ€ter", "settings.advanced.description": "Synchronisiere wiederholte Angreifer automatisch mit einer externen Firewall oder Blockliste.", "settings.advanced.refresh_log": "Protokoll aktualisieren", - "settings.advanced.test_button": "Integration testen", + "settings.advanced.test_button": "Manuell sperren / Test", "settings.advanced.enable": "Automatische permanente Sperre aktivieren", "settings.advanced.threshold": "Schwelle vor permanenter Sperre", "settings.advanced.threshold_hint": "Sobald eine IP diesen Wert erreicht, wird sie an die Integration ΓΌbergeben.", @@ -156,7 +156,7 @@ "settings.advanced.log_updated": "Aktualisiert", "settings.advanced.log_actions": "Aktionen", "settings.advanced.unblock_btn": "Entfernen", - "settings.advanced.test_title": "Integration testen", + "settings.advanced.test_title": "Manuell sperren / Test", "settings.advanced.test_ip": "IP-Adresse", "settings.advanced.test_server": "Optionaler Server", "settings.advanced.test_server_none": "Globale Integration verwenden", diff --git a/internal/locales/de_ch.json b/internal/locales/de_ch.json index 9f15926..f91870f 100644 --- a/internal/locales/de_ch.json +++ b/internal/locales/de_ch.json @@ -126,7 +126,7 @@ "settings.advanced.title": "Erwieterti Aktione fΓΌr WiederholungstΓ€ter", "settings.advanced.description": "Synchronisier d wiederholigstΓ€ter automatisch mit dr externe Firewall resp. Sperrlischte.", "settings.advanced.refresh_log": "Log aktualisiere", - "settings.advanced.test_button": "Integration teste", + "settings.advanced.test_button": "Manuell sperre / Test", "settings.advanced.enable": "Automatischi permanenti Sperrig aktiviere", "settings.advanced.threshold": "Schweue fΓΌr die permanenti Sperrig", "settings.advanced.threshold_hint": "Sobald e IP die Zahu erreitcht, wird sie via Integration gsperrt.", @@ -156,7 +156,7 @@ "settings.advanced.log_updated": "Aktualisiert", "settings.advanced.log_actions": "Aktione", "settings.advanced.unblock_btn": "Entferne", - "settings.advanced.test_title": "Integration teste", + "settings.advanced.test_title": "Manuell sperre / Test", "settings.advanced.test_ip": "IP-AdrΓ€sse", "settings.advanced.test_server": "Optionale Server", "settings.advanced.test_server_none": "Globali Integration bruuchΓ€", diff --git a/internal/locales/en.json b/internal/locales/en.json index 3668c9f..628ee12 100644 --- a/internal/locales/en.json +++ b/internal/locales/en.json @@ -126,7 +126,7 @@ "settings.advanced.title": "Advanced Actions for Recurring Offenders", "settings.advanced.description": "Automatically synchronize recurring offenders to an external firewall or blocklist.", "settings.advanced.refresh_log": "Refresh Log", - "settings.advanced.test_button": "Test Integration", + "settings.advanced.test_button": "Manually Block / Test", "settings.advanced.enable": "Enable automatic permanent blocking", "settings.advanced.threshold": "Threshold before permanent block", "settings.advanced.threshold_hint": "Once an IP reaches this number of bans it will be forwarded to the integration.", @@ -156,7 +156,7 @@ "settings.advanced.log_updated": "Updated", "settings.advanced.log_actions": "Actions", "settings.advanced.unblock_btn": "Remove", - "settings.advanced.test_title": "Test Advanced Integration", + "settings.advanced.test_title": "Manually Block / Test", "settings.advanced.test_ip": "IP address", "settings.advanced.test_server": "Optional server", "settings.advanced.test_server_none": "Use global integration settings", diff --git a/internal/locales/es.json b/internal/locales/es.json index f692a3b..70a5845 100644 --- a/internal/locales/es.json +++ b/internal/locales/es.json @@ -126,7 +126,7 @@ "settings.advanced.title": "Acciones avanzadas para reincidentes", "settings.advanced.description": "AΓ±ade automΓ‘ticamente IPs reincidentes a un firewall o lista de bloqueo externa.", "settings.advanced.refresh_log": "Actualizar registro", - "settings.advanced.test_button": "Probar integraciΓ³n", + "settings.advanced.test_button": "Bloquear manualmente / Test", "settings.advanced.enable": "Habilitar bloqueo permanente automΓ‘tico", "settings.advanced.threshold": "Umbral antes del bloqueo permanente", "settings.advanced.threshold_hint": "Cuando una IP alcanza este nΓΊmero de bloqueos se enviarΓ‘ a la integraciΓ³n.", @@ -156,7 +156,7 @@ "settings.advanced.log_updated": "Actualizado", "settings.advanced.log_actions": "Acciones", "settings.advanced.unblock_btn": "Eliminar", - "settings.advanced.test_title": "Probar integraciΓ³n avanzada", + "settings.advanced.test_title": "Bloquear manualmente / Test", "settings.advanced.test_ip": "DirecciΓ³n IP", "settings.advanced.test_server": "Servidor opcional", "settings.advanced.test_server_none": "Usar integraciΓ³n global", diff --git a/internal/locales/fr.json b/internal/locales/fr.json index 4db54f1..088bd55 100644 --- a/internal/locales/fr.json +++ b/internal/locales/fr.json @@ -126,7 +126,7 @@ "settings.advanced.title": "Actions avancΓ©es pour rΓ©cidivistes", "settings.advanced.description": "Ajoutez automatiquement les rΓ©cidivistes Γ  un pare-feu ou une liste de blocage externe.", "settings.advanced.refresh_log": "Actualiser le journal", - "settings.advanced.test_button": "Tester l’intΓ©gration", + "settings.advanced.test_button": "Bloquer manuellement / Test", "settings.advanced.enable": "Activer le blocage permanent automatique", "settings.advanced.threshold": "Seuil avant blocage permanent", "settings.advanced.threshold_hint": "Une IP atteignant ce nombre de bans sera envoyΓ©e Γ  l’intΓ©gration.", @@ -156,7 +156,7 @@ "settings.advanced.log_updated": "Mis Γ  jour", "settings.advanced.log_actions": "Actions", "settings.advanced.unblock_btn": "Retirer", - "settings.advanced.test_title": "Tester l’intΓ©gration avancΓ©e", + "settings.advanced.test_title": "Bloquer manuellement / Test", "settings.advanced.test_ip": "Adresse IP", "settings.advanced.test_server": "Serveur optionnel", "settings.advanced.test_server_none": "Utiliser l’intΓ©gration globale", diff --git a/pkg/web/advanced_actions.go b/pkg/web/advanced_actions.go index b34dda0..90a4aa3 100644 --- a/pkg/web/advanced_actions.go +++ b/pkg/web/advanced_actions.go @@ -40,12 +40,12 @@ func evaluateAdvancedActions(ctx context.Context, settings config.AppSettings, s "reason": "automatic_threshold", "count": count, "threshold": cfg.Threshold, - }); err != nil { + }, false); err != nil { log.Printf("⚠️ Failed to permanently block %s: %v", ip, err) } } -func runAdvancedIntegrationAction(ctx context.Context, action, ip string, settings config.AppSettings, server config.Fail2banServer, details map[string]any) error { +func runAdvancedIntegrationAction(ctx context.Context, action, ip string, settings config.AppSettings, server config.Fail2banServer, details map[string]any, skipLoggingIfAlreadyBlocked bool) error { cfg := settings.AdvancedActions if cfg.Integration == "" { return fmt.Errorf("no integration configured") @@ -85,26 +85,29 @@ func runAdvancedIntegrationAction(ctx context.Context, action, ip string, settin }[action] message := fmt.Sprintf("%s via %s", strings.Title(action), cfg.Integration) - if err != nil { + if err != nil && !skipLoggingIfAlreadyBlocked { status = "error" message = err.Error() } - if details == nil { - details = map[string]any{} - } - details["action"] = action - detailsBytes, _ := json.Marshal(details) - rec := storage.PermanentBlockRecord{ - IP: ip, - Integration: cfg.Integration, - Status: status, - Message: message, - ServerID: server.ID, - Details: string(detailsBytes), - } - if err2 := storage.UpsertPermanentBlock(ctx, rec); err2 != nil { - log.Printf("⚠️ Failed to record permanent block entry: %v", err2) + // If IP is already blocked, don't update the database entry - leave existing entry as is + if !skipLoggingIfAlreadyBlocked { + if details == nil { + details = map[string]any{} + } + details["action"] = action + detailsBytes, _ := json.Marshal(details) + rec := storage.PermanentBlockRecord{ + IP: ip, + Integration: cfg.Integration, + Status: status, + Message: message, + ServerID: server.ID, + Details: string(detailsBytes), + } + if err2 := storage.UpsertPermanentBlock(ctx, rec); err2 != nil { + log.Printf("⚠️ Failed to record permanent block entry: %v", err2) + } } return err diff --git a/pkg/web/handlers.go b/pkg/web/handlers.go index 8966936..d6eb8cb 100644 --- a/pkg/web/handlers.go +++ b/pkg/web/handlers.go @@ -787,6 +787,16 @@ func AdvancedActionsTestHandler(c *gin.Context) { } } + // Check if IP is already blocked before attempting action (for block action only) + skipLoggingIfAlreadyBlocked := false + if action == "block" && settings.AdvancedActions.Integration != "" { + active, checkErr := storage.IsPermanentBlockActive(c.Request.Context(), req.IP, settings.AdvancedActions.Integration) + if checkErr == nil && active { + // IP is already blocked, we'll check the error message after the call + skipLoggingIfAlreadyBlocked = true + } + } + err := runAdvancedIntegrationAction( c.Request.Context(), action, @@ -794,8 +804,20 @@ func AdvancedActionsTestHandler(c *gin.Context) { settings, server, map[string]any{"manual": true}, + skipLoggingIfAlreadyBlocked, ) if err != nil { + // Check if error indicates IP is already blocked - show as info instead of error + if skipLoggingIfAlreadyBlocked { + errMsg := strings.ToLower(err.Error()) + if strings.Contains(errMsg, "already have such entry") || + strings.Contains(errMsg, "already exists") || + strings.Contains(errMsg, "duplicate") { + // IP is already blocked, return info message with original error + c.JSON(http.StatusOK, gin.H{"message": err.Error(), "info": true}) + return + } + } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } diff --git a/pkg/web/templates/index.html b/pkg/web/templates/index.html index d2d7de7..e4eb1fd 100644 --- a/pkg/web/templates/index.html +++ b/pkg/web/templates/index.html @@ -366,7 +366,7 @@
- +
@@ -975,7 +975,7 @@
-

Test Advanced Integration

+

Manually Block / Test

@@ -993,7 +993,6 @@
-
@@ -3468,7 +3467,7 @@ ${escapeHtml(block.serverId || '')} ${block.updatedAt ? new Date(block.updatedAt).toLocaleString() : ''} - + `; }).join(''); @@ -3537,7 +3536,9 @@ if (data.error) { showToast('Advanced action failed: ' + data.error, 'error'); } else { - showToast(data.message || 'Action completed', 'success'); + // Check if this is an info message (e.g., IP already blocked) + const toastType = data.info ? 'info' : 'success'; + showToast(data.message || 'Action completed', toastType); loadPermanentBlockLog(); } }) @@ -3548,7 +3549,11 @@ }); } - function advancedUnblockIP(ip) { + function advancedUnblockIP(ip, event) { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } if (!ip) return; fetch('/api/advanced-actions/test', { method: 'POST',