add advanced ban actions

This commit is contained in:
2025-11-30 13:26:09 +01:00
parent 493b79537d
commit 65b56b3461
11 changed files with 661 additions and 267 deletions

830
README.md
View File

@@ -1,132 +1,256 @@
# **Fail2Ban UI** # Fail2Ban UI
🚀 **Fail2Ban-UI** is a Swiss-made **web-based management interface** for [Fail2Ban](https://www.fail2ban.org/). <div align="center">
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** *Swiss-made open-source solution for centralized Fail2Ban management across distributed infrastructure*
- 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
**Real-time Dashboard** [Features](#-features) • [Quick Start](#-quick-start) • [Documentation](#-documentation) • [Screenshots](#-screenshots) • [Security Notes](#-security-notes)
- 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
**Ban & Unban Management** </div>
- **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**: **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.
**1⃣ Running from local source**
**2⃣ Running as a container**
### **🔹 Method 1: Running from Local Source** ### Why we Need Fail2Ban UI
To install and run directly on the system:
📌 **[Follow the basic systemd setup guide](./deployment/systemd/README.md)**
```bash 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:
git clone https://github.com/swissmakers/fail2ban-ui.git /opt/fail2ban-ui
cd /opt/fail2ban-ui
# Build Tailwind CSS for production (optional but recommended) - **Centralized Management**: Control multible Fail2Ban instances from a single interface
# This requires Node.js and npm to be installed - **Real-Time Visibility**: Monitor ban / security events across your entire proxy / webserver infrastructure (also planned to ingest all via connector to SIEM)
./build-tailwind.sh - **Operational Efficiency**: Reduce administrative overhead with automated workflows e.g. auto-banning for recurring IPs, directly on the firewall.
# Build Go application **Developed by [Swissmakers GmbH](https://swissmakers.ch)** — Trusted by enterprises swisswide for mission-critical security infrastructure.
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
--- ---
### **🔹 Method 2: Running as a Container** ## 🚀 Current Features
For an easy containerized deployment:
📌 **[Follow the basic container deployment guide](./deployment/container/README.md)**
You can either build the image locally or pull the **official, automatically built image** from the Swissmakers registry. ### 🏢 Multi-Server Management
Every merge into the `main` branch triggers a new build that is published at:
**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 ```bash
# Pull the latest pre-built image
podman pull registry.swissmakers.ch/infra/fail2ban-ui:latest podman pull registry.swissmakers.ch/infra/fail2ban-ui:latest
```
# Run the container **Run the container:**
```bash
podman run -d \ podman run -d \
--name fail2ban-ui \ --name fail2ban-ui \
--network=host \ --network=host \
@@ -138,183 +262,421 @@ podman run -d \
registry.swissmakers.ch/infra/fail2ban-ui:latest 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** ### Deployment Guides
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
### **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** ### Configuration
- 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
#### **SSH Server** #### Adding a Local 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
**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) - SSH key-based authentication (passwordless login)
- Network connectivity from UI host to remote server - Network connectivity from UI host to remote server
- The SSH user must have: - Service account with appropriate permissions
- 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) **Recommended Service Account Setup:**
**Recommended SSH setup (using service account with ACLs):**
```bash ```bash
# Create service account # Create dedicated service account
sudo useradd -r -s /bin/bash sa_fail2ban 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 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/fail2ban-client *
sa_fail2ban ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart fail2ban 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 -Rm u:sa_fail2ban:rwX /etc/fail2ban
sudo setfacl -dRm 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) **Security Benefits:**
- API agent support is planned for future releases - Least privilege access model
- Will allow connecting to remote Fail2ban instances via a lightweight API service - 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). #### Network Security Best Practices
- For SSH connections, use a **dedicated service account** with:
- **Limited sudo access** (only for `fail2ban-client` and `systemctl restart fail2ban`) - **Reverse Proxy**: Use nginx or Apache as reverse proxy with SSL/TLS termination to secure the Fail2ban-UI
- **File system ACLs** on `/etc/fail2ban` for configuration file access (more secure than full sudo) - **VPN Access**: Require VPN connection for access to Fail2Banu-UI only
- **Restrict access** using **firewall rules** or a **reverse proxy** with authentication. - **IP Whitelisting**: Restrict access to specific IPs / ranges e.g. internal IT
- Ensure that Fail2Ban logs/configs **aren't exposed publicly**.
- For SSH connections, use **SSH key-based authentication** and restrict SSH access. #### Authentication and Authorization
- Store SSH keys securely and use strong passphrases.
- The SQLite database contains sensitive information - ensure proper file permissions. - **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 ```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 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-ui.pp
semodule -i fail2ban-container-client.pp semodule -i fail2ban-container-client.pp
``` ```
--- **📖 [SELinux Policies Documentation](./deployment/container/SELinux/)**
## **🛠️ 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
--- ---
## **📊 Database** ### 🗄️ Database Notes
Fail2Ban-UI uses an embedded **SQLite database** (`fail2ban-ui.db`) to store: #### SQLite Database Schema
- **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
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: #### Currently Supported Languages
- English (en)
- German (de, de_ch)
- French (fr)
- Italian (it)
- Spanish (es)
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** ### 📊 API Reference
We welcome **pull requests** and **feature suggestions**!
1. **Fork** this repository Fail2Ban UI provides a RESTful API for programmatic access:
2. **Create** a new branch:
```bash
git checkout -b feature/my-feature
```
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
#### Endpoints
## **📜 License** **Server Management:**
Fail2Ban-UI is licensed under **GNU GENERAL PUBLIC LICENSE, Version 3**. - `GET /api/servers` - List all configured servers
See [`LICENSE`](./LICENSE) for details. - `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 clone https://github.com/swissmakers/fail2ban-ui.git
cd fail2ban-ui
```
2. **Create a Feature Branch**
```bash
git checkout -b feature/your-feature-name
```
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
---
<div align="center">
**Fail2Ban UI***Enterprise-Grade Intrusion Detection System Management*
</div>

2
go.mod
View File

@@ -6,6 +6,7 @@ require (
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/go-playground/validator/v10 v10.26.0 github.com/go-playground/validator/v10 v10.26.0
github.com/oschwald/maxminddb-golang v1.13.1 github.com/oschwald/maxminddb-golang v1.13.1
golang.org/x/crypto v0.33.0
modernc.org/sqlite v1.33.1 modernc.org/sqlite v1.33.1
) )
@@ -33,7 +34,6 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.13.0 // 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/net v0.34.0 // indirect
golang.org/x/sys v0.30.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect golang.org/x/text v0.22.0 // indirect

2
go.sum
View File

@@ -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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 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/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 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= 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= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=

View File

@@ -126,7 +126,7 @@
"settings.advanced.title": "Erweiterte Aktionen für Wiederholungstäter", "settings.advanced.title": "Erweiterte Aktionen für Wiederholungstäter",
"settings.advanced.description": "Synchronisiere wiederholte Angreifer automatisch mit einer externen Firewall oder Blockliste.", "settings.advanced.description": "Synchronisiere wiederholte Angreifer automatisch mit einer externen Firewall oder Blockliste.",
"settings.advanced.refresh_log": "Protokoll aktualisieren", "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.enable": "Automatische permanente Sperre aktivieren",
"settings.advanced.threshold": "Schwelle vor permanenter Sperre", "settings.advanced.threshold": "Schwelle vor permanenter Sperre",
"settings.advanced.threshold_hint": "Sobald eine IP diesen Wert erreicht, wird sie an die Integration übergeben.", "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_updated": "Aktualisiert",
"settings.advanced.log_actions": "Aktionen", "settings.advanced.log_actions": "Aktionen",
"settings.advanced.unblock_btn": "Entfernen", "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_ip": "IP-Adresse",
"settings.advanced.test_server": "Optionaler Server", "settings.advanced.test_server": "Optionaler Server",
"settings.advanced.test_server_none": "Globale Integration verwenden", "settings.advanced.test_server_none": "Globale Integration verwenden",

View File

@@ -126,7 +126,7 @@
"settings.advanced.title": "Erwieterti Aktione für Wiederholungstäter", "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.description": "Synchronisier d wiederholigstäter automatisch mit dr externe Firewall resp. Sperrlischte.",
"settings.advanced.refresh_log": "Log aktualisiere", "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.enable": "Automatischi permanenti Sperrig aktiviere",
"settings.advanced.threshold": "Schweue für die permanenti Sperrig", "settings.advanced.threshold": "Schweue für die permanenti Sperrig",
"settings.advanced.threshold_hint": "Sobald e IP die Zahu erreitcht, wird sie via Integration gsperrt.", "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_updated": "Aktualisiert",
"settings.advanced.log_actions": "Aktione", "settings.advanced.log_actions": "Aktione",
"settings.advanced.unblock_btn": "Entferne", "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_ip": "IP-Adrässe",
"settings.advanced.test_server": "Optionale Server", "settings.advanced.test_server": "Optionale Server",
"settings.advanced.test_server_none": "Globali Integration bruuchä", "settings.advanced.test_server_none": "Globali Integration bruuchä",

View File

@@ -126,7 +126,7 @@
"settings.advanced.title": "Advanced Actions for Recurring Offenders", "settings.advanced.title": "Advanced Actions for Recurring Offenders",
"settings.advanced.description": "Automatically synchronize recurring offenders to an external firewall or blocklist.", "settings.advanced.description": "Automatically synchronize recurring offenders to an external firewall or blocklist.",
"settings.advanced.refresh_log": "Refresh Log", "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.enable": "Enable automatic permanent blocking",
"settings.advanced.threshold": "Threshold before permanent block", "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.", "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_updated": "Updated",
"settings.advanced.log_actions": "Actions", "settings.advanced.log_actions": "Actions",
"settings.advanced.unblock_btn": "Remove", "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_ip": "IP address",
"settings.advanced.test_server": "Optional server", "settings.advanced.test_server": "Optional server",
"settings.advanced.test_server_none": "Use global integration settings", "settings.advanced.test_server_none": "Use global integration settings",

View File

@@ -126,7 +126,7 @@
"settings.advanced.title": "Acciones avanzadas para reincidentes", "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.description": "Añade automáticamente IPs reincidentes a un firewall o lista de bloqueo externa.",
"settings.advanced.refresh_log": "Actualizar registro", "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.enable": "Habilitar bloqueo permanente automático",
"settings.advanced.threshold": "Umbral antes del bloqueo permanente", "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.", "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_updated": "Actualizado",
"settings.advanced.log_actions": "Acciones", "settings.advanced.log_actions": "Acciones",
"settings.advanced.unblock_btn": "Eliminar", "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_ip": "Dirección IP",
"settings.advanced.test_server": "Servidor opcional", "settings.advanced.test_server": "Servidor opcional",
"settings.advanced.test_server_none": "Usar integración global", "settings.advanced.test_server_none": "Usar integración global",

View File

@@ -126,7 +126,7 @@
"settings.advanced.title": "Actions avancées pour récidivistes", "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.description": "Ajoutez automatiquement les récidivistes à un pare-feu ou une liste de blocage externe.",
"settings.advanced.refresh_log": "Actualiser le journal", "settings.advanced.refresh_log": "Actualiser le journal",
"settings.advanced.test_button": "Tester lintégration", "settings.advanced.test_button": "Bloquer manuellement / Test",
"settings.advanced.enable": "Activer le blocage permanent automatique", "settings.advanced.enable": "Activer le blocage permanent automatique",
"settings.advanced.threshold": "Seuil avant blocage permanent", "settings.advanced.threshold": "Seuil avant blocage permanent",
"settings.advanced.threshold_hint": "Une IP atteignant ce nombre de bans sera envoyée à lintégration.", "settings.advanced.threshold_hint": "Une IP atteignant ce nombre de bans sera envoyée à lintégration.",
@@ -156,7 +156,7 @@
"settings.advanced.log_updated": "Mis à jour", "settings.advanced.log_updated": "Mis à jour",
"settings.advanced.log_actions": "Actions", "settings.advanced.log_actions": "Actions",
"settings.advanced.unblock_btn": "Retirer", "settings.advanced.unblock_btn": "Retirer",
"settings.advanced.test_title": "Tester lintégration avancée", "settings.advanced.test_title": "Bloquer manuellement / Test",
"settings.advanced.test_ip": "Adresse IP", "settings.advanced.test_ip": "Adresse IP",
"settings.advanced.test_server": "Serveur optionnel", "settings.advanced.test_server": "Serveur optionnel",
"settings.advanced.test_server_none": "Utiliser lintégration globale", "settings.advanced.test_server_none": "Utiliser lintégration globale",

View File

@@ -40,12 +40,12 @@ func evaluateAdvancedActions(ctx context.Context, settings config.AppSettings, s
"reason": "automatic_threshold", "reason": "automatic_threshold",
"count": count, "count": count,
"threshold": cfg.Threshold, "threshold": cfg.Threshold,
}); err != nil { }, false); err != nil {
log.Printf("⚠️ Failed to permanently block %s: %v", ip, err) 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 cfg := settings.AdvancedActions
if cfg.Integration == "" { if cfg.Integration == "" {
return fmt.Errorf("no integration configured") return fmt.Errorf("no integration configured")
@@ -85,11 +85,13 @@ func runAdvancedIntegrationAction(ctx context.Context, action, ip string, settin
}[action] }[action]
message := fmt.Sprintf("%s via %s", strings.Title(action), cfg.Integration) message := fmt.Sprintf("%s via %s", strings.Title(action), cfg.Integration)
if err != nil { if err != nil && !skipLoggingIfAlreadyBlocked {
status = "error" status = "error"
message = err.Error() message = err.Error()
} }
// If IP is already blocked, don't update the database entry - leave existing entry as is
if !skipLoggingIfAlreadyBlocked {
if details == nil { if details == nil {
details = map[string]any{} details = map[string]any{}
} }
@@ -106,6 +108,7 @@ func runAdvancedIntegrationAction(ctx context.Context, action, ip string, settin
if err2 := storage.UpsertPermanentBlock(ctx, rec); err2 != nil { if err2 := storage.UpsertPermanentBlock(ctx, rec); err2 != nil {
log.Printf("⚠️ Failed to record permanent block entry: %v", err2) log.Printf("⚠️ Failed to record permanent block entry: %v", err2)
} }
}
return err return err
} }

View File

@@ -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( err := runAdvancedIntegrationAction(
c.Request.Context(), c.Request.Context(),
action, action,
@@ -794,8 +804,20 @@ func AdvancedActionsTestHandler(c *gin.Context) {
settings, settings,
server, server,
map[string]any{"manual": true}, map[string]any{"manual": true},
skipLoggingIfAlreadyBlocked,
) )
if err != nil { 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()}) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return return
} }

View File

@@ -366,7 +366,7 @@
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<button type="button" class="px-3 py-2 text-sm rounded border border-gray-300 text-gray-700 hover:bg-gray-50" onclick="refreshPermanentBlockLog()" data-i18n="settings.advanced.refresh_log">Refresh Log</button> <button type="button" class="px-3 py-2 text-sm rounded border border-gray-300 text-gray-700 hover:bg-gray-50" onclick="refreshPermanentBlockLog()" data-i18n="settings.advanced.refresh_log">Refresh Log</button>
<button type="button" class="px-3 py-2 text-sm rounded border border-blue-600 text-blue-600 hover:bg-blue-50" onclick="openAdvancedTestModal()" data-i18n="settings.advanced.test_button">Test Integration</button> <button type="button" class="px-3 py-2 text-sm rounded border border-blue-600 text-blue-600 hover:bg-blue-50" onclick="openAdvancedTestModal()" data-i18n="settings.advanced.test_button">Manually Block / Test</button>
</div> </div>
</div> </div>
@@ -975,7 +975,7 @@
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start"> <div class="sm:flex sm:items-start">
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full"> <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
<h3 class="text-lg leading-6 font-medium text-gray-900" data-i18n="settings.advanced.test_title">Test Advanced Integration</h3> <h3 class="text-lg leading-6 font-medium text-gray-900" data-i18n="settings.advanced.test_title">Manually Block / Test</h3>
<div class="mt-4 space-y-4"> <div class="mt-4 space-y-4">
<div> <div>
<label for="advancedTestIP" class="block text-sm font-medium text-gray-700" data-i18n="settings.advanced.test_ip">IP address</label> <label for="advancedTestIP" class="block text-sm font-medium text-gray-700" data-i18n="settings.advanced.test_ip">IP address</label>
@@ -993,7 +993,6 @@
</div> </div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse gap-3"> <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse gap-3">
<button type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm" onclick="submitAdvancedTest('block')" data-i18n="settings.advanced.test_block">Block IP</button> <button type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm" onclick="submitAdvancedTest('block')" data-i18n="settings.advanced.test_block">Block IP</button>
<button type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" onclick="submitAdvancedTest('unblock')" data-i18n="settings.advanced.test_unblock">Remove IP</button>
<button type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" onclick="closeModal('advancedTestModal')" data-i18n="modal.close">Close</button> <button type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" onclick="closeModal('advancedTestModal')" data-i18n="modal.close">Close</button>
</div> </div>
</div> </div>
@@ -3468,7 +3467,7 @@
<td class="px-3 py-2 text-xs text-gray-500">${escapeHtml(block.serverId || '')}</td> <td class="px-3 py-2 text-xs text-gray-500">${escapeHtml(block.serverId || '')}</td>
<td class="px-3 py-2 text-xs text-gray-500">${block.updatedAt ? new Date(block.updatedAt).toLocaleString() : ''}</td> <td class="px-3 py-2 text-xs text-gray-500">${block.updatedAt ? new Date(block.updatedAt).toLocaleString() : ''}</td>
<td class="px-3 py-2 text-right"> <td class="px-3 py-2 text-right">
<button class="text-sm text-blue-600 hover:text-blue-800" onclick="advancedUnblockIP('${escapeHtml(block.ip)}')" data-i18n="settings.advanced.unblock_btn">Remove</button> <button type="button" class="text-sm text-blue-600 hover:text-blue-800" onclick="advancedUnblockIP('${escapeHtml(block.ip)}', event)" data-i18n="settings.advanced.unblock_btn">Remove</button>
</td> </td>
</tr>`; </tr>`;
}).join(''); }).join('');
@@ -3537,7 +3536,9 @@
if (data.error) { if (data.error) {
showToast('Advanced action failed: ' + data.error, 'error'); showToast('Advanced action failed: ' + data.error, 'error');
} else { } 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(); loadPermanentBlockLog();
} }
}) })
@@ -3548,7 +3549,11 @@
}); });
} }
function advancedUnblockIP(ip) { function advancedUnblockIP(ip, event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
if (!ip) return; if (!ip) return;
fetch('/api/advanced-actions/test', { fetch('/api/advanced-actions/test', {
method: 'POST', method: 'POST',