[mod] limiter: trusted proxies (#4911)

Replaces `x_for` functionality with `trusted_proxies`. This allows defining
which IP / ranges to trust extracting the client IP address from X-Forwarded-For
and X-Real-IP headers.

We don't know if the proxy chain will give us the proper client
address (REMOTE_ADDR in the WSGI environment), so we rely on reading the headers
of the proxy before SearXNG (if there is one, in that case it must be added to
trusted_proxies) hoping it has done the proper checks. In case a proxy in the
chain does not check the client address correctly, integrity is compromised and
this should be fixed by whoever manages the proxy, not us.

Closes:

- https://github.com/searxng/searxng/issues/4940
- https://github.com/searxng/searxng/issues/4939
- https://github.com/searxng/searxng/issues/4907
- https://github.com/searxng/searxng/issues/3632
- https://github.com/searxng/searxng/issues/3191
- https://github.com/searxng/searxng/issues/1237

Related:

- https://github.com/searxng/searxng-docker/issues/386
- https://github.com/inetol-infrastructure/searxng-container/issues/81
This commit is contained in:
Ivan Gabaldon 2025-08-09 23:03:30 +02:00 committed by GitHub
parent 341d718c7f
commit ce8929cabe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 453 additions and 184 deletions

View file

@ -93,13 +93,14 @@ Implementation
"""
from __future__ import annotations
from ipaddress import ip_address
import sys
from pathlib import Path
from ipaddress import ip_address
import flask
import werkzeug
import searx.compat
from searx import (
logger,
valkeydb,
@ -116,7 +117,6 @@ from searx.botdetection import (
ip_limit,
ip_lists,
get_network,
get_real_ip,
dump_request,
)
@ -124,25 +124,24 @@ from searx.botdetection import (
# coherency, the logger is "limiter"
logger = logger.getChild('limiter')
CFG: config.Config = None # type: ignore
CFG: config.Config | None = None # type: ignore
_INSTALLED = False
LIMITER_CFG_SCHEMA = Path(__file__).parent / "limiter.toml"
"""Base configuration (schema) of the botdetection."""
CFG_DEPRECATED = {
# "dummy.old.foo": "config 'dummy.old.foo' exists only for tests. Don't use it in your real project config."
}
def get_cfg() -> config.Config:
"""Returns SearXNG's global limiter configuration."""
global CFG # pylint: disable=global-statement
if CFG is None:
from . import settings_loader # pylint: disable=import-outside-toplevel
cfg_file = (settings_loader.get_user_cfg_folder() or Path("/etc/searxng")) / "limiter.toml"
CFG = config.Config.from_toml(LIMITER_CFG_SCHEMA, cfg_file, CFG_DEPRECATED)
CFG = config.Config.from_toml(LIMITER_CFG_SCHEMA, cfg_file, searx.compat.LIMITER_CFG_DEPRECATED)
searx.compat.limiter_fix_cfg(CFG, cfg_file)
return CFG
@ -150,7 +149,7 @@ def filter_request(request: SXNG_Request) -> werkzeug.Response | None:
# pylint: disable=too-many-return-statements
cfg = get_cfg()
real_ip = ip_address(get_real_ip(request))
real_ip = ip_address(request.remote_addr)
network = get_network(real_ip, cfg)
if request.path == '/healthz':