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:
parent
56fc86d77d
commit
b4944ac544
8 changed files with 187 additions and 11 deletions
|
|
@ -1,3 +1,7 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 6
|
||||||
|
---
|
||||||
|
|
||||||
# Cleanup Plugin
|
# Cleanup Plugin
|
||||||
|
|
||||||
**Type:** Global Plugin
|
**Type:** Global Plugin
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 5
|
||||||
|
---
|
||||||
|
|
||||||
# Cloudflare Plugin
|
# Cloudflare Plugin
|
||||||
|
|
||||||
**Type:** Domain Plugin
|
**Type:** Domain Plugin
|
||||||
|
|
@ -5,7 +9,7 @@
|
||||||
|
|
||||||
## Overview
|
## 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
|
## Why Use It
|
||||||
|
|
||||||
|
|
@ -13,14 +17,15 @@ Cloudflare replaces the visitor's IP with its own. This plugin restores the orig
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
| Option | Description | Default |
|
| Option | Description | Default |
|
||||||
|----------------|----------------------------|-----------------------------------|
|
|-------------------|------------------------------------------|-----------------------------------|
|
||||||
| `enabled` | Enable/disable plugin | `true` |
|
| `enabled` | Enable/disable plugin | `true` |
|
||||||
| `ip_list_path` | Path to Cloudflare IP list | `/etc/haproxy/cloudflare_ips.lst` |
|
| `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
|
## Configuration Examples
|
||||||
|
|
||||||
### Docker/Docker Compose (Basic)
|
### Docker/Docker Compose (Basic - Uses Built-in IPs)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
|
|
@ -28,13 +33,17 @@ services:
|
||||||
labels:
|
labels:
|
||||||
easyhaproxy.http.host: example.com
|
easyhaproxy.http.host: example.com
|
||||||
easyhaproxy.http.plugins: cloudflare
|
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
|
```yaml
|
||||||
labels:
|
labels:
|
||||||
easyhaproxy.http.plugins: cloudflare
|
easyhaproxy.http.plugins: cloudflare
|
||||||
|
easyhaproxy.http.plugin.cloudflare.use_builtin_ips: false
|
||||||
easyhaproxy.http.plugin.cloudflare.ip_list_path: /custom/path/cf_ips.lst
|
easyhaproxy.http.plugin.cloudflare.ip_list_path: /custom/path/cf_ips.lst
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -68,7 +77,7 @@ plugins:
|
||||||
config:
|
config:
|
||||||
cloudflare:
|
cloudflare:
|
||||||
enabled: true
|
enabled: true
|
||||||
ip_list_path: /etc/haproxy/cloudflare_ips.lst
|
use_builtin_ips: true # Uses built-in Cloudflare IPs (default)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Generated HAProxy Configuration
|
## 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
|
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
|
## 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
|
- 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
|
## Related Documentation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 4
|
||||||
|
---
|
||||||
|
|
||||||
# Deny Pages Plugin
|
# Deny Pages Plugin
|
||||||
|
|
||||||
**Type:** Domain Plugin
|
**Type:** Domain Plugin
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
---
|
||||||
|
|
||||||
# FastCGI Plugin
|
# FastCGI Plugin
|
||||||
|
|
||||||
**Type:** Domain Plugin
|
**Type:** Domain Plugin
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
# IP Whitelist Plugin
|
# IP Whitelist Plugin
|
||||||
|
|
||||||
**Type:** Domain Plugin
|
**Type:** Domain Plugin
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
---
|
||||||
|
|
||||||
# JWT Validator Plugin
|
# JWT Validator Plugin
|
||||||
|
|
||||||
**Type:** Domain Plugin
|
**Type:** Domain Plugin
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,19 @@ Cloudflare Plugin for EasyHAProxy
|
||||||
This plugin restores the original visitor IP address from Cloudflare's
|
This plugin restores the original visitor IP address from Cloudflare's
|
||||||
CF-Connecting-IP header when requests come through Cloudflare's CDN.
|
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:
|
Configuration:
|
||||||
- ip_list_path: Path to file containing Cloudflare IP ranges (default: /etc/haproxy/cloudflare_ips.lst)
|
- 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:
|
Example YAML config:
|
||||||
plugins:
|
plugins:
|
||||||
cloudflare:
|
cloudflare:
|
||||||
enabled: true
|
enabled: true
|
||||||
ip_list_path: /etc/haproxy/cloudflare_ips.lst
|
ip_list_path: /etc/haproxy/cloudflare_ips.lst
|
||||||
|
use_builtin_ips: true
|
||||||
|
|
||||||
Example Container Label:
|
Example Container Label:
|
||||||
easyhaproxy.http.plugins: "cloudflare"
|
easyhaproxy.http.plugins: "cloudflare"
|
||||||
|
|
@ -29,14 +34,45 @@ import sys
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from plugins import PluginInterface, PluginType, PluginContext, PluginResult
|
from plugins import PluginInterface, PluginType, PluginContext, PluginResult
|
||||||
|
from functions import loggerEasyHaproxy
|
||||||
|
|
||||||
|
|
||||||
class CloudflarePlugin(PluginInterface):
|
class CloudflarePlugin(PluginInterface):
|
||||||
"""Plugin to restore original visitor IP from Cloudflare"""
|
"""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):
|
def __init__(self):
|
||||||
self.ip_list_path = "/etc/haproxy/cloudflare_ips.lst"
|
self.ip_list_path = "/etc/haproxy/cloudflare_ips.lst"
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
|
self.use_builtin_ips = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
@ -54,6 +90,7 @@ class CloudflarePlugin(PluginInterface):
|
||||||
config: Dictionary with configuration options
|
config: Dictionary with configuration options
|
||||||
- ip_list_path: Path to Cloudflare IP list file
|
- ip_list_path: Path to Cloudflare IP list file
|
||||||
- enabled: Whether plugin is enabled
|
- enabled: Whether plugin is enabled
|
||||||
|
- use_builtin_ips: Use built-in Cloudflare IP ranges (default: true)
|
||||||
"""
|
"""
|
||||||
if "ip_list_path" in config:
|
if "ip_list_path" in config:
|
||||||
self.ip_list_path = config["ip_list_path"]
|
self.ip_list_path = config["ip_list_path"]
|
||||||
|
|
@ -61,6 +98,9 @@ class CloudflarePlugin(PluginInterface):
|
||||||
if "enabled" in config:
|
if "enabled" in config:
|
||||||
self.enabled = str(config["enabled"]).lower() in ["true", "1", "yes"]
|
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:
|
def process(self, context: PluginContext) -> PluginResult:
|
||||||
"""
|
"""
|
||||||
Generate HAProxy config to restore original IP from Cloudflare
|
Generate HAProxy config to restore original IP from Cloudflare
|
||||||
|
|
@ -74,6 +114,23 @@ class CloudflarePlugin(PluginInterface):
|
||||||
if not self.enabled:
|
if not self.enabled:
|
||||||
return PluginResult()
|
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
|
# Generate HAProxy config snippet
|
||||||
haproxy_config = f"""# Cloudflare - Restore original visitor IP
|
haproxy_config = f"""# Cloudflare - Restore original visitor IP
|
||||||
acl from_cloudflare src -f {self.ip_list_path}
|
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,
|
modified_easymapping=None,
|
||||||
metadata={
|
metadata={
|
||||||
"domain": context.domain,
|
"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
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ class TestCloudflarePlugin:
|
||||||
plugin = CloudflarePlugin()
|
plugin = CloudflarePlugin()
|
||||||
assert plugin.name == "cloudflare"
|
assert plugin.name == "cloudflare"
|
||||||
assert plugin.enabled is True
|
assert plugin.enabled is True
|
||||||
|
assert plugin.use_builtin_ips is True
|
||||||
assert plugin.ip_list_path == "/etc/haproxy/cloudflare_ips.lst"
|
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):
|
def test_cloudflare_plugin_configuration(self):
|
||||||
"""Test plugin configuration"""
|
"""Test plugin configuration"""
|
||||||
|
|
@ -123,6 +125,75 @@ class TestCloudflarePlugin:
|
||||||
assert "acl from_cloudflare src -f /etc/haproxy/cloudflare_ips.lst" in haproxy_config
|
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
|
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:
|
class TestCleanupPlugin:
|
||||||
"""Test cases for CleanupPlugin (GLOBAL plugin)"""
|
"""Test cases for CleanupPlugin (GLOBAL plugin)"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue