Add the 10.0.0.0/8 RFC-1918 block to _TRUSTED_PROXY_NETS so reverse proxies in cloud VPCs and Kubernetes clusters are recognized. Without this, X-Forwarded-For was silently ignored for any proxy on a 10.x.x.x address, logging all requests as originating from the proxy IP. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
55 lines
1.8 KiB
Python
55 lines
1.8 KiB
Python
"""Shared dependency utilities — request parsing helpers used across all API routers."""
|
|
from __future__ import annotations
|
|
|
|
import ipaddress
|
|
import uuid
|
|
from typing import Optional
|
|
|
|
from fastapi import HTTPException, Request
|
|
|
|
_TRUSTED_PROXY_NETS = [
|
|
ipaddress.ip_network("10.0.0.0/8"),
|
|
ipaddress.ip_network("127.0.0.0/8"),
|
|
ipaddress.ip_network("172.16.0.0/12"),
|
|
ipaddress.ip_network("192.168.0.0/16"),
|
|
ipaddress.ip_network("::1/128"),
|
|
]
|
|
|
|
|
|
def _is_trusted_proxy(host: str) -> bool:
|
|
try:
|
|
addr = ipaddress.ip_address(host)
|
|
return any(addr in net for net in _TRUSTED_PROXY_NETS)
|
|
except ValueError:
|
|
return False
|
|
|
|
|
|
def get_client_ip(request: Request) -> Optional[str]:
|
|
"""Extract best-effort client IP from request for audit logging (D-11 — trusted-proxy CIDR check).
|
|
|
|
Only honours X-Forwarded-For when the direct peer is a known trusted proxy
|
|
(RFC-1918 / loopback). Untrusted direct peers always return their own address,
|
|
preventing XFF spoofing by external callers.
|
|
|
|
TRUST BOUNDARY: this value is used for forensic audit logging only —
|
|
not for authentication or access control decisions.
|
|
"""
|
|
direct_peer = request.client.host if request.client else None
|
|
if direct_peer and _is_trusted_proxy(direct_peer):
|
|
xff = request.headers.get("X-Forwarded-For")
|
|
if xff:
|
|
return xff.split(",")[0].strip()
|
|
return direct_peer
|
|
|
|
|
|
def parse_uuid(value: str, detail: str = "Not found") -> uuid.UUID:
|
|
"""Parse *value* as a UUID, raising HTTP 404 with *detail* on failure.
|
|
|
|
Use at API boundaries to convert path/body string IDs to UUID objects.
|
|
Returns the parsed UUID so callers can use it directly without a try/except.
|
|
"""
|
|
try:
|
|
return uuid.UUID(value)
|
|
except ValueError:
|
|
raise HTTPException(status_code=404, detail=detail)
|