Fetch CF ranges concurrently and prevent writes

Use tokio::join to fetch IPv4 and IPv6 Cloudflare ranges in parallel.
When range fetch fails, avoid performing updates that could write
Cloudflare addresses by clearing detected/filtered IP lists and emitting
warnings. Add unit tests to validate parsing and boundary checks for the
current Cloudflare ranges. Bump crate version to 2.0.6.
Fetch Cloudflare ranges concurrently; avoid writes

Skip updates (clear detected IPs) if Cloudflare ranges can't be
retrieved to avoid writing Cloudflare anycast addresses.
Default REJECT_CLOUDFLARE_IPS=true, update README, add comprehensive
CF-range tests, and bump crate version
Fetch CF ranges concurrently and avoid updates

Enable rejecting Cloudflare IPs by default and skip any updates
if the published ranges cannot be fetched to avoid writing Cloudflare
anycast addresses. Fetch IPv4 and IPv6 ranges concurrently, add
parsing/matching tests, and update README and version.
This commit is contained in:
Timothy Miller
2026-03-19 18:56:11 -04:00
parent f8d5b5cb7e
commit 83dd454c42
6 changed files with 158 additions and 19 deletions

View File

@@ -28,7 +28,7 @@ Configure everything with environment variables. Supports notifications, heartbe
- 🎨 **Pretty output with emoji** — Configurable emoji and verbosity levels
- 🔒 **Zero-log IP detection** — Uses Cloudflare's [cdn-cgi/trace](https://www.cloudflare.com/cdn-cgi/trace) by default
- 🏠 **CGNAT-aware local detection** — Filters out shared address space (100.64.0.0/10) and private ranges
- 🚫 **Cloudflare IP rejection**Optionally reject Cloudflare anycast IPs to prevent incorrect DNS updates
- 🚫 **Cloudflare IP rejection**Automatically rejects Cloudflare anycast IPs to prevent incorrect DNS updates
- 🤏 **Tiny static binary** — ~1.9 MB Docker image built from scratch, zero runtime dependencies
## 🚀 Quick Start
@@ -92,11 +92,13 @@ Available providers:
| Variable | Default | Description |
|----------|---------|-------------|
| `REJECT_CLOUDFLARE_IPS` | `false` | Reject detected IPs that fall within Cloudflare's IP ranges |
| `REJECT_CLOUDFLARE_IPS` | `true` | Reject detected IPs that fall within Cloudflare's IP ranges |
Some IP detection providers occasionally return a Cloudflare anycast IP instead of your real public IP. When this happens, your DNS record gets updated to point at Cloudflare infrastructure rather than your actual address.
Setting `REJECT_CLOUDFLARE_IPS=true` prevents this. Each update cycle fetches [Cloudflare's published IP ranges](https://www.cloudflare.com/ips/) and skips any detected IP that falls within them. A warning is logged for every rejected IP.
By default, each update cycle fetches [Cloudflare's published IP ranges](https://www.cloudflare.com/ips/) and skips any detected IP that falls within them. A warning is logged for every rejected IP. If the ranges cannot be fetched, the update is skipped entirely to prevent writing a Cloudflare IP.
To disable this protection, set `REJECT_CLOUDFLARE_IPS=false`.
## ⏱️ Scheduling
@@ -221,7 +223,7 @@ Heartbeats are sent after each update cycle. On failure, a fail signal is sent.
| `MANAGED_WAF_LIST_ITEMS_COMMENT_REGEX` | — | 🎯 Managed WAF items regex |
| `DETECTION_TIMEOUT` | `5s` | ⏳ IP detection timeout |
| `UPDATE_TIMEOUT` | `30s` | ⏳ API request timeout |
| `REJECT_CLOUDFLARE_IPS` | `false` | 🚫 Reject Cloudflare anycast IPs |
| `REJECT_CLOUDFLARE_IPS` | `true` | 🚫 Reject Cloudflare anycast IPs |
| `EMOJI` | `true` | 🎨 Enable emoji output |
| `QUIET` | `false` | 🤫 Suppress info output |
| `HEALTHCHECKS` | — | 💓 Healthchecks.io URL |
@@ -373,17 +375,17 @@ Some ISP provided modems only allow port forwarding over IPv4 or IPv6. Disable t
### 🚫 Cloudflare IP Rejection (Legacy Mode)
The `REJECT_CLOUDFLARE_IPS` environment variable is supported in legacy config mode. Set it alongside your `config.json`:
Cloudflare IP rejection is enabled by default in legacy mode too. To disable it, set `REJECT_CLOUDFLARE_IPS=false` alongside your `config.json`:
```bash
REJECT_CLOUDFLARE_IPS=true cloudflare-ddns
REJECT_CLOUDFLARE_IPS=false cloudflare-ddns
```
Or in Docker Compose:
```yml
environment:
- REJECT_CLOUDFLARE_IPS=true
- REJECT_CLOUDFLARE_IPS=false
volumes:
- ./config.json:/config.json
```