Add CIDR notation support for IP blocking
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 2m1s
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 2m1s
- Update map file format to include value (IP/CIDR 1) - Fix HAProxy template to use map_ip() for CIDR support - Update runtime map commands to include value - Document CIDR range blocking in API documentation - Support blocking entire network ranges (e.g., 192.168.1.0/24) This allows blocking compromised ISP ranges and other large-scale attacks.
This commit is contained in:
@@ -5,12 +5,23 @@ This document describes the IP blocking functionality added to HAProxy Manager,
|
|||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The IP blocking feature allows administrators to:
|
The IP blocking feature allows administrators to:
|
||||||
- Block specific IP addresses from accessing any sites managed by HAProxy
|
- Block specific IP addresses or CIDR ranges from accessing any sites managed by HAProxy
|
||||||
- Unblock previously blocked IP addresses
|
- Unblock previously blocked IP addresses or CIDR ranges
|
||||||
- View all currently blocked IP addresses
|
- View all currently blocked IP addresses and CIDR ranges
|
||||||
- Track who blocked an IP and when
|
- Track who blocked an IP/CIDR and when
|
||||||
|
|
||||||
When an IP is blocked, visitors from that IP address will receive a 403 Forbidden response.
|
When an IP is blocked (or falls within a blocked CIDR range), visitors from that IP address will receive a 403 Forbidden response.
|
||||||
|
|
||||||
|
### CIDR Range Support
|
||||||
|
|
||||||
|
The IP blocking system supports CIDR notation for blocking entire network ranges:
|
||||||
|
- **Single IP**: `192.168.1.100` (blocks only this IP)
|
||||||
|
- **CIDR Range**: `192.168.1.0/24` (blocks 256 IPs from 192.168.1.0 to 192.168.1.255)
|
||||||
|
- **Common CIDR Masks**:
|
||||||
|
- `/32` - Single IP (1 address)
|
||||||
|
- `/24` - Standard subnet (256 addresses)
|
||||||
|
- `/16` - Large network (65,536 addresses)
|
||||||
|
- `/8` - Very large network (16,777,216 addresses)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -78,7 +89,7 @@ Add an IP address to the blocked list.
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `ip_address` (required): The IP address to block (e.g., "192.168.1.100")
|
- `ip_address` (required): The IP address or CIDR range to block (e.g., "192.168.1.100" or "192.168.1.0/24")
|
||||||
- `reason` (optional): Reason for blocking (default: "No reason provided")
|
- `reason` (optional): Reason for blocking (default: "No reason provided")
|
||||||
- `blocked_by` (optional): Who/what initiated the block (default: "API")
|
- `blocked_by` (optional): Who/what initiated the block (default: "API")
|
||||||
|
|
||||||
@@ -96,7 +107,7 @@ Add an IP address to the blocked list.
|
|||||||
- `409 Conflict`: IP address is already blocked
|
- `409 Conflict`: IP address is already blocked
|
||||||
- `500 Internal Server Error`: Configuration generation failed
|
- `500 Internal Server Error`: Configuration generation failed
|
||||||
|
|
||||||
**Example Request:**
|
**Example Request (Single IP):**
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://localhost:8000/api/blocked-ips \
|
curl -X POST http://localhost:8000/api/blocked-ips \
|
||||||
-H "Authorization: Bearer your-api-key" \
|
-H "Authorization: Bearer your-api-key" \
|
||||||
@@ -108,9 +119,21 @@ curl -X POST http://localhost:8000/api/blocked-ips \
|
|||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Unblock an IP Address
|
**Example Request (CIDR Range):**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8000/api/blocked-ips \
|
||||||
|
-H "Authorization: Bearer your-api-key" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"ip_address": "192.168.1.0/24",
|
||||||
|
"reason": "DDoS attack from compromised ISP",
|
||||||
|
"blocked_by": "WHP Security Module"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
Remove an IP address from the blocked list.
|
### 3. Unblock an IP Address or CIDR Range
|
||||||
|
|
||||||
|
Remove an IP address or CIDR range from the blocked list.
|
||||||
|
|
||||||
**Endpoint:** `DELETE /api/blocked-ips`
|
**Endpoint:** `DELETE /api/blocked-ips`
|
||||||
|
|
||||||
@@ -122,7 +145,7 @@ Remove an IP address from the blocked list.
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `ip_address` (required): The IP address to unblock
|
- `ip_address` (required): The IP address or CIDR range to unblock (must match exactly as it was blocked)
|
||||||
|
|
||||||
**Response:**
|
**Response:**
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -1341,11 +1341,13 @@ def update_blocked_ips_map():
|
|||||||
cursor.execute('SELECT ip_address FROM blocked_ips ORDER BY ip_address')
|
cursor.execute('SELECT ip_address FROM blocked_ips ORDER BY ip_address')
|
||||||
blocked_ips = [row[0] for row in cursor.fetchall()]
|
blocked_ips = [row[0] for row in cursor.fetchall()]
|
||||||
|
|
||||||
# Write map file
|
# Write map file in HAProxy map format: <key> <value>
|
||||||
|
# For IP blocking, we use: <ip_or_cidr> 1
|
||||||
|
# This allows map_ip() to work with both single IPs and CIDR ranges
|
||||||
os.makedirs(os.path.dirname(BLOCKED_IPS_MAP_PATH), exist_ok=True)
|
os.makedirs(os.path.dirname(BLOCKED_IPS_MAP_PATH), exist_ok=True)
|
||||||
with open(BLOCKED_IPS_MAP_PATH, 'w') as f:
|
with open(BLOCKED_IPS_MAP_PATH, 'w') as f:
|
||||||
for ip in blocked_ips:
|
for ip in blocked_ips:
|
||||||
f.write(f"{ip}\n")
|
f.write(f"{ip} 1\n")
|
||||||
|
|
||||||
logger.info(f"Updated blocked IPs map file with {len(blocked_ips)} IPs")
|
logger.info(f"Updated blocked IPs map file with {len(blocked_ips)} IPs")
|
||||||
return True
|
return True
|
||||||
@@ -1362,7 +1364,9 @@ def add_ip_to_runtime_map(ip_address):
|
|||||||
socket_path = '/tmp/haproxy-cli'
|
socket_path = '/tmp/haproxy-cli'
|
||||||
|
|
||||||
# Add to runtime map (map file ID 0 for blocked IPs)
|
# Add to runtime map (map file ID 0 for blocked IPs)
|
||||||
cmd = f'echo "add map #0 {ip_address}" | socat stdio {socket_path}'
|
# Format: add map #<id> <key> <value>
|
||||||
|
# For IP blocking, value is always "1"
|
||||||
|
cmd = f'echo "add map #0 {ip_address} 1" | socat stdio {socket_path}'
|
||||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ frontend web
|
|||||||
http-request set-var(txn.real_ip) src if !has_cf_connecting_ip !has_x_real_ip !has_x_forwarded_for
|
http-request set-var(txn.real_ip) src if !has_cf_connecting_ip !has_x_real_ip !has_x_forwarded_for
|
||||||
|
|
||||||
# IP blocking using map file (manual blocks only)
|
# IP blocking using map file (manual blocks only)
|
||||||
# Map file: /etc/haproxy/blocked_ips.map
|
# Map file format: /etc/haproxy/blocked_ips.map contains "<ip_or_cidr> 1" per line
|
||||||
# Runtime updates: echo "add map #0 IP_ADDRESS" | socat stdio /var/run/haproxy.sock
|
# Runtime updates: echo "add map #0 IP_ADDRESS 1" | socat stdio /var/run/haproxy.sock
|
||||||
# Checks the real client IP (from headers if present, otherwise src)
|
# Checks the real client IP (from headers if present, otherwise src)
|
||||||
http-request set-path /blocked-ip if { var(txn.real_ip) -m ip -f /etc/haproxy/blocked_ips.map }
|
# map_ip() converter supports both single IPs and CIDR ranges (e.g., 192.168.1.0/24)
|
||||||
use_backend default-backend if { var(txn.real_ip) -m ip -f /etc/haproxy/blocked_ips.map }
|
acl is_blocked_ip var(txn.real_ip),map_ip(/etc/haproxy/blocked_ips.map,0) -m int gt 0
|
||||||
|
http-request set-path /blocked-ip if is_blocked_ip
|
||||||
|
use_backend default-backend if is_blocked_ip
|
||||||
|
|||||||
Reference in New Issue
Block a user