Files
2026-09-12 18:15:20 +08:00

71 lines
3.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""utils/ip.py 可信代理白名单:仅直连对端命中白名单才信任 X-Forwarded-For。"""
import types
from utils import ip as ip_mod
from utils.ip import get_client_ip
def _make_request(client_host, xff=None):
req = types.SimpleNamespace()
req.client = types.SimpleNamespace(host=client_host)
req.headers = {'X-Forwarded-For': xff} if xff else {}
return req
def test_proxy_count_zero_ignores_xff(monkeypatch):
# TRUSTED_PROXY_COUNT=0:即使带 XFF 头也不信任,直接返回 TCP 层 IP
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 0)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'127.0.0.1'}))
assert get_client_ip(_make_request('203.0.113.9', xff='1.2.3.4')) == '203.0.113.9'
def test_proxy_count_positive_but_no_whitelist_is_fail_closed(monkeypatch):
# 配了 TRUSTED_PROXY_COUNT 但未配 TRUSTED_PROXY_IPS → 不信任 XFF(fail-closed)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset())
assert get_client_ip(_make_request('203.0.113.9', xff='1.2.3.4')) == '203.0.113.9'
def test_peer_not_in_whitelist_rejects_xff(monkeypatch):
# 直连对端不是可信代理(应用被直接暴露,如 Docker bridge 映射端口)
# → 客户端伪造的 XFF 被忽略,返回真实对端 IP
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.1'}))
assert get_client_ip(_make_request('203.0.113.9', xff='1.2.3.4')) == '203.0.113.9'
def test_peer_in_whitelist_trusts_xff(monkeypatch):
# 单层代理:XFF 仅含客户端 IP(由可信代理追加),跳过最右侧 1 个代理条目即客户端
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.1'}))
assert get_client_ip(_make_request('10.0.0.1', xff='1.2.3.4')) == '1.2.3.4'
def test_peer_in_whitelist_spoofed_left_entry_ignored(monkeypatch):
# 客户端伪造条目只会出现在 XFF 左侧(由可信代理追加的在其右侧),伪造 IP 被忽略
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.1'}))
assert get_client_ip(_make_request('10.0.0.1', xff='6.6.6.6, 1.2.3.4')) == '1.2.3.4'
def test_peer_in_whitelist_fewer_entries_than_proxies(monkeypatch):
# 代理条目少于层数(部分代理未追加 XFF)→ 回退到最右侧条目,避免拿到代理 IP
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 2)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.2'}))
assert get_client_ip(_make_request('10.0.0.2', xff='1.2.3.4')) == '1.2.3.4'
def test_peer_in_whitelist_multiple_proxy_layers(monkeypatch):
# 两层代理:XFF=[客户端, 外层代理],最内层可信代理是直连对端(不在 XFF 内),
# 跳过最右侧 2 个代理条目取真实客户端
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 2)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.2'}))
assert get_client_ip(_make_request('10.0.0.2', xff='1.2.3.4, 10.0.0.1')) == '1.2.3.4'
def test_no_client_falls_back_to_localhost(monkeypatch):
req = types.SimpleNamespace(client=None, headers={})
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.1'}))
assert get_client_ip(req) == '127.0.0.1'