dpl/sync-cf-ips.py

46 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""Synchronize only the new white proxy vhosts' Cloudflare trust include."""
import ipaddress
import os
import pathlib
import subprocess
target = pathlib.Path('/etc/nginx/white-proxy-cloudflare-realip.conf')
ranges = []
for version in (4, 6):
p = subprocess.run(['curl', '-4', '--fail', '--silent', '--show-error', '--max-time', '30',
f'https://www.cloudflare.com/ips-v{version}'],
check=True, capture_output=True, text=True)
nets = [ipaddress.ip_network(x.strip(), strict=True) for x in p.stdout.splitlines() if x.strip()]
if not ((5 if version == 4 else 2) <= len(nets) <= 100):
raise RuntimeError('Unexpected Cloudflare range count')
if any(n.version != version or n.prefixlen < (8 if version == 4 else 16) for n in nets):
raise RuntimeError('Invalid or overbroad Cloudflare trust range')
ranges.extend(str(n) for n in nets)
if len(ranges) != len(set(ranges)):
raise RuntimeError('Duplicate Cloudflare ranges')
data = ('# Managed new white proxy vhosts only; Cloudflare official ranges.\n' +
''.join(f'set_real_ip_from {n};\n' for n in ranges) +
'real_ip_header CF-Connecting-IP;\nreal_ip_recursive off;\n').encode()
old = target.read_bytes() if target.exists() else None
if old == data:
print('White proxy Cloudflare trust unchanged')
raise SystemExit(0)
temporary = target.with_suffix('.conf.new')
temporary.write_bytes(data)
temporary.chmod(0o644)
os.replace(temporary, target)
try:
subprocess.run(['nginx', '-t'], check=True, capture_output=True)
subprocess.run(['systemctl', 'reload', 'nginx'], check=True)
except Exception:
if old is None:
target.unlink()
else:
temporary.write_bytes(old)
os.replace(temporary, target)
subprocess.run(['nginx', '-t'], check=True, capture_output=True)
subprocess.run(['systemctl', 'reload', 'nginx'], check=True)
raise
print(f'White proxy Cloudflare trust synchronized: {len(ranges)} ranges')