"""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'