1
0
Fork 0

Add use_builtin_ips support and reorganize plugin documentation

- Enhanced Cloudflare Plugin with `use_builtin_ips` option to automatically use and update built-in IP ranges.
- Updated Cloudflare IP restoration logic, including metadata and HAProxy config generation.
- Reorganized plugin documentation with sidebar positions for improved accessibility.
- Extended Cloudflare documentation to detail built-in IP ranges, examples, and configurations.
- Added new test cases to validate `use_builtin_ips` functionality and file handling.
This commit is contained in:
Joao Gilberto Magalhaes 2025-12-01 18:00:52 -05:00
parent 56fc86d77d
commit b4944ac544
8 changed files with 187 additions and 11 deletions

View file

@ -1,3 +1,7 @@
---
sidebar_position: 6
---
# Cleanup Plugin
**Type:** Global Plugin

View file

@ -1,3 +1,7 @@
---
sidebar_position: 5
---
# Cloudflare Plugin
**Type:** Domain Plugin
@ -5,7 +9,7 @@
## Overview
The Cloudflare plugin restores the original visitor IP address when requests come through Cloudflare's CDN.
The Cloudflare plugin restores the original visitor IP address when requests come through Cloudflare's CDN. The plugin includes **built-in Cloudflare IP ranges** that are automatically written to the IP list file - no manual configuration required!
## Why Use It
@ -13,14 +17,15 @@ Cloudflare replaces the visitor's IP with its own. This plugin restores the orig
## Configuration Options
| Option | Description | Default |
|----------------|----------------------------|-----------------------------------|
| `enabled` | Enable/disable plugin | `true` |
| `ip_list_path` | Path to Cloudflare IP list | `/etc/haproxy/cloudflare_ips.lst` |
| Option | Description | Default |
|-------------------|------------------------------------------|-----------------------------------|
| `enabled` | Enable/disable plugin | `true` |
| `use_builtin_ips` | Use built-in Cloudflare IP ranges | `true` |
| `ip_list_path` | Path to Cloudflare IP list | `/etc/haproxy/cloudflare_ips.lst` |
## Configuration Examples
### Docker/Docker Compose (Basic)
### Docker/Docker Compose (Basic - Uses Built-in IPs)
```yaml
services:
@ -28,13 +33,17 @@ services:
labels:
easyhaproxy.http.host: example.com
easyhaproxy.http.plugins: cloudflare
# Built-in Cloudflare IPs are automatically used - no additional configuration needed!
```
### Docker/Docker Compose (Custom IP List Path)
### Docker/Docker Compose (Custom IP List)
If you want to use your own IP list file instead of the built-in ranges:
```yaml
labels:
easyhaproxy.http.plugins: cloudflare
easyhaproxy.http.plugin.cloudflare.use_builtin_ips: false
easyhaproxy.http.plugin.cloudflare.ip_list_path: /custom/path/cf_ips.lst
```
@ -68,7 +77,7 @@ plugins:
config:
cloudflare:
enabled: true
ip_list_path: /etc/haproxy/cloudflare_ips.lst
use_builtin_ips: true # Uses built-in Cloudflare IPs (default)
```
## Generated HAProxy Configuration
@ -79,11 +88,28 @@ acl from_cloudflare src -f /etc/haproxy/cloudflare_ips.lst
http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)] if from_cloudflare
```
## Built-in Cloudflare IP Ranges
The plugin includes the current Cloudflare IP ranges (22 ranges total):
**IPv4 Ranges (15):**
- 173.245.48.0/20, 103.21.244.0/22, 103.22.200.0/22, 103.31.4.0/22
- 141.101.64.0/18, 108.162.192.0/18, 190.93.240.0/20, 188.114.96.0/20
- 197.234.240.0/22, 198.41.128.0/17, 162.158.0.0/15, 104.16.0.0/13
- 104.24.0.0/14, 172.64.0.0/13, 131.0.72.0/22
**IPv6 Ranges (7):**
- 2400:cb00::/32, 2606:4700::/32, 2803:f800::/32, 2405:b500::/32
- 2405:8100::/32, 2a06:98c0::/29, 2c0f:f248::/32
These ranges are automatically written to `/etc/haproxy/cloudflare_ips.lst` during each discovery cycle.
## Important Notes
- **Required:** Download Cloudflare IP list from [Cloudflare documentation](https://support.cloudflare.com/hc/en-us/articles/200170786)
- **No manual configuration required** - Built-in Cloudflare IPs are included!
- The plugin runs once per domain during the discovery cycle
- Ensure the IP list file is mounted and accessible to HAProxy
- IP list file is automatically created and updated
- To update Cloudflare IPs in the future, simply update the plugin source code and rebuild
## Related Documentation

View file

@ -1,3 +1,7 @@
---
sidebar_position: 4
---
# Deny Pages Plugin
**Type:** Domain Plugin

View file

@ -1,3 +1,7 @@
---
sidebar_position: 1
---
# FastCGI Plugin
**Type:** Domain Plugin

View file

@ -1,3 +1,7 @@
---
sidebar_position: 3
---
# IP Whitelist Plugin
**Type:** Domain Plugin

View file

@ -1,3 +1,7 @@
---
sidebar_position: 2
---
# JWT Validator Plugin
**Type:** Domain Plugin

View file

@ -4,14 +4,19 @@ Cloudflare Plugin for EasyHAProxy
This plugin restores the original visitor IP address from Cloudflare's
CF-Connecting-IP header when requests come through Cloudflare's CDN.
The plugin includes built-in Cloudflare IP ranges that are automatically
updated and written to the IP list file.
Configuration:
- ip_list_path: Path to file containing Cloudflare IP ranges (default: /etc/haproxy/cloudflare_ips.lst)
- use_builtin_ips: Use built-in Cloudflare IP ranges (default: true)
Example YAML config:
plugins:
cloudflare:
enabled: true
ip_list_path: /etc/haproxy/cloudflare_ips.lst
use_builtin_ips: true
Example Container Label:
easyhaproxy.http.plugins: "cloudflare"
@ -29,14 +34,45 @@ import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from plugins import PluginInterface, PluginType, PluginContext, PluginResult
from functions import loggerEasyHaproxy
class CloudflarePlugin(PluginInterface):
"""Plugin to restore original visitor IP from Cloudflare"""
# Current Cloudflare IP ranges (IPv4 and IPv6)
# Source: https://www.cloudflare.com/ips/
CLOUDFLARE_IPS = [
# IPv4
"173.245.48.0/20",
"103.21.244.0/22",
"103.22.200.0/22",
"103.31.4.0/22",
"141.101.64.0/18",
"108.162.192.0/18",
"190.93.240.0/20",
"188.114.96.0/20",
"197.234.240.0/22",
"198.41.128.0/17",
"162.158.0.0/15",
"104.16.0.0/13",
"104.24.0.0/14",
"172.64.0.0/13",
"131.0.72.0/22",
# IPv6
"2400:cb00::/32",
"2606:4700::/32",
"2803:f800::/32",
"2405:b500::/32",
"2405:8100::/32",
"2a06:98c0::/29",
"2c0f:f248::/32",
]
def __init__(self):
self.ip_list_path = "/etc/haproxy/cloudflare_ips.lst"
self.enabled = True
self.use_builtin_ips = True
@property
def name(self) -> str:
@ -54,6 +90,7 @@ class CloudflarePlugin(PluginInterface):
config: Dictionary with configuration options
- ip_list_path: Path to Cloudflare IP list file
- enabled: Whether plugin is enabled
- use_builtin_ips: Use built-in Cloudflare IP ranges (default: true)
"""
if "ip_list_path" in config:
self.ip_list_path = config["ip_list_path"]
@ -61,6 +98,9 @@ class CloudflarePlugin(PluginInterface):
if "enabled" in config:
self.enabled = str(config["enabled"]).lower() in ["true", "1", "yes"]
if "use_builtin_ips" in config:
self.use_builtin_ips = str(config["use_builtin_ips"]).lower() in ["true", "1", "yes"]
def process(self, context: PluginContext) -> PluginResult:
"""
Generate HAProxy config to restore original IP from Cloudflare
@ -74,6 +114,23 @@ class CloudflarePlugin(PluginInterface):
if not self.enabled:
return PluginResult()
# Write built-in Cloudflare IPs to file if using built-in IPs
if self.use_builtin_ips:
try:
# Create directory if it doesn't exist
ip_list_dir = os.path.dirname(self.ip_list_path)
if ip_list_dir and not os.path.exists(ip_list_dir):
os.makedirs(ip_list_dir, exist_ok=True)
# Write Cloudflare IPs to file
with open(self.ip_list_path, 'w') as f:
for ip_range in self.CLOUDFLARE_IPS:
f.write(f"{ip_range}\n")
loggerEasyHaproxy.info(f"Cloudflare plugin: Written {len(self.CLOUDFLARE_IPS)} IP ranges to {self.ip_list_path}")
except Exception as e:
loggerEasyHaproxy.warning(f"Cloudflare plugin: Failed to write IP list to {self.ip_list_path}: {e}")
# Generate HAProxy config snippet
haproxy_config = f"""# Cloudflare - Restore original visitor IP
acl from_cloudflare src -f {self.ip_list_path}
@ -84,6 +141,8 @@ http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)] if from_clo
modified_easymapping=None,
metadata={
"domain": context.domain,
"ip_list_path": self.ip_list_path
"ip_list_path": self.ip_list_path,
"use_builtin_ips": self.use_builtin_ips,
"ip_count": len(self.CLOUDFLARE_IPS) if self.use_builtin_ips else None
}
)

View file

@ -42,7 +42,9 @@ class TestCloudflarePlugin:
plugin = CloudflarePlugin()
assert plugin.name == "cloudflare"
assert plugin.enabled is True
assert plugin.use_builtin_ips is True
assert plugin.ip_list_path == "/etc/haproxy/cloudflare_ips.lst"
assert len(plugin.CLOUDFLARE_IPS) == 22 # 15 IPv4 + 7 IPv6
def test_cloudflare_plugin_configuration(self):
"""Test plugin configuration"""
@ -123,6 +125,75 @@ class TestCloudflarePlugin:
assert "acl from_cloudflare src -f /etc/haproxy/cloudflare_ips.lst" in haproxy_config
assert "http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)]" in haproxy_config
def test_cloudflare_plugin_builtin_ips_enabled(self):
"""Test plugin uses built-in Cloudflare IPs and writes to file"""
# Use temp directory for testing
with tempfile.TemporaryDirectory() as tmpdir:
ip_list_path = os.path.join(tmpdir, "cloudflare_ips.lst")
plugin = CloudflarePlugin()
plugin.configure({
"use_builtin_ips": "true",
"ip_list_path": ip_list_path
})
context = PluginContext(
parsed_object={},
easymapping=[],
container_env={},
domain="example.com",
port="80",
host_config={}
)
result = plugin.process(context)
# Verify config is generated
assert result.haproxy_config is not None
assert f"acl from_cloudflare src -f {ip_list_path}" in result.haproxy_config
# Verify metadata
assert result.metadata["use_builtin_ips"] is True
assert result.metadata["ip_count"] == 22
# Verify file was written
assert os.path.exists(ip_list_path)
# Verify file contains correct number of IPs
with open(ip_list_path, 'r') as f:
lines = [line.strip() for line in f if line.strip()]
assert len(lines) == 22
# Verify some known Cloudflare IPs are in the file
assert "173.245.48.0/20" in lines
assert "2606:4700::/32" in lines
def test_cloudflare_plugin_builtin_ips_disabled(self):
"""Test plugin doesn't write to file when use_builtin_ips is disabled"""
plugin = CloudflarePlugin()
plugin.configure({
"use_builtin_ips": "false",
"ip_list_path": "/custom/cloudflare_ips.lst"
})
context = PluginContext(
parsed_object={},
easymapping=[],
container_env={},
domain="example.com",
port="80",
host_config={}
)
result = plugin.process(context)
# Verify config is generated with custom path
assert result.haproxy_config is not None
assert "acl from_cloudflare src -f /custom/cloudflare_ips.lst" in result.haproxy_config
# Verify metadata
assert result.metadata["use_builtin_ips"] is False
assert result.metadata["ip_count"] is None
class TestCleanupPlugin:
"""Test cases for CleanupPlugin (GLOBAL plugin)"""