Trust X-Forwarded-For only from ArvanCloud edges

nginx on the server also answers on its own IP. A direct request with a fresh
X-Forwarded-For each time got a new throttle identity every time and skipped
every per-IP limit (OTP request/login, admin login): reproduced on the server
2026-10-13, 65 of 65 health requests with rotating XFF got 200.

TrustedProxyMiddleware reads X-Real-IP (set by nginx from the TCP peer) and,
when the peer is not in TRUSTED_PROXY_NETWORKS (default: ArvanCloud's
published list), replaces XFF with the peer address. Logs a WARNING once an
hour per untrusted peer that sent a proxied XFF, so a new Arvan range shows up.

Also ignore the parallel test DB clones (.test-db_N.sqlite3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 18:45:27 +03:30
parent 78c3669119
commit e3bddb3757
4 changed files with 139 additions and 1 deletions

View File

@@ -10,6 +10,7 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
import ipaddress
import os
import re
from pathlib import Path
@@ -265,7 +266,25 @@ INSTALLED_APPS = [
'rag',
]
# شبکه‌هایی که X-Forwarded-Forشان پذیرفته می‌شود (config.middleware.TrustedProxyMiddleware).
# پیش‌فرض: فهرستِ edgeهای ArvanCloud از https://www.arvancloud.ir/fa/ips.txt (۲۰۲۶-۱۰-۱۳). اگر آروان رنجِ
# تازه بگیرد و این‌جا نباشد، کاربرانِ پشتِ آن edge یک IP دیده می‌شوند و لاگ WARNINGِ
# «outside TRUSTED_PROXY_NETWORKS» می‌دهد؛ آن وقت فهرست را از همان آدرس تازه کنید (یا در .env بگذارید).
# مقدارِ نامعتبر بالا آمدن را می‌شکند، نه این‌که بی‌صدا نادیده گرفته شود.
_ARVAN_EDGE_NETWORKS = (
'185.143.232.0/22,188.229.116.16/30,94.101.182.0/27,2.144.3.128/28,37.32.16.0/27,37.32.17.0/27,'
'37.32.18.0/27,37.32.19.0/27,185.215.232.0/22,178.131.120.48/28,94.101.183.0/28,78.157.36.112/28,'
'95.38.61.80/28,193.24.119.0/29'
)
TRUSTED_PROXY_NETWORKS = [
ipaddress.ip_network(cidr.strip())
for cidr in os.environ.get('TRUSTED_PROXY_NETWORKS', _ARVAN_EDGE_NETWORKS).split(',')
if cidr.strip()
]
MIDDLEWARE = [
# پیش از هر چیزی که IPِ کاربر را می‌خواند (سقفِ ورودِ ادمین، throttleهای DRF).
'config.middleware.TrustedProxyMiddleware',
'django.middleware.security.SecurityMiddleware',
# ⚠️ CorsMiddleware باید تا حدِ ممکن بالا و حتماً قبل از CommonMiddleware
# باشد، وگرنه پاسخ‌هایی که زودتر برمی‌گردند (ریدایرکتِ APPEND_SLASH، خطای