Root cause of the 429s: the ip_limit rate limiter caps JSON (format!=html) requests at API_MAX=4 per IP per hour, and that constant is hardcoded in Python (searx/botdetection/ip_limit.py), not settable in limiter.toml. The only bypass is pass_ip (filter_request returns before ip_limit runs). Rootless podman strips the client IP on published-port hits, so the container sees a slirp/bridge source (10.88.x/10.89.x), never the real client - hence even a LAN-IP curl 429'd on /search. Expand pass_ip to loopback + the host's private podman/slirp ranges (plus the existing LAN) so local subagents present a trusted source and skip the rate limit entirely. All these ranges are reachable only from the host or LAN, never the internet.
67 lines
2.4 KiB
TOML
67 lines
2.4 KiB
TOML
# SearXNG limiter / bot-detection config.
|
|
# Deployed to /opt/searxng/config/limiter.toml on Dankclaw (mounted at
|
|
# /etc/searxng/limiter.toml). Loaded when server.limiter: true in settings.yml.
|
|
#
|
|
# The "trusted group" lives HERE, not in settings.yml.
|
|
# Docs: https://docs.searxng.org/admin/searx.limiter.html
|
|
|
|
[botdetection]
|
|
|
|
# Number of leading bits compared to group addresses into a (client) network.
|
|
ipv4_prefix = 32
|
|
ipv6_prefix = 48
|
|
|
|
# If the request IP is in trusted_proxies, the real client IP is read from the
|
|
# X-Forwarded-For / X-Real-IP headers (set by the Caddy reverse proxy).
|
|
# SearXNG must be reached through Caddy (search.dankclaw.local): a direct hit on
|
|
# the published port has no X-Forwarded-For header, so the bot-detection cannot
|
|
# resolve a client and rate-limits it. Caddy connects from the podman bridge, so
|
|
# the bridge subnet must be trusted here. NOTE: rootless podman assigns the bridge
|
|
# CIDR dynamically; pin the network (podman network create with a fixed --subnet)
|
|
# so these entries stay stable.
|
|
trusted_proxies = [
|
|
'127.0.0.0/8',
|
|
'::1',
|
|
'192.168.0.0/24',
|
|
'10.88.0.0/24',
|
|
'10.89.1.0/24',
|
|
'10.89.2.0/24',
|
|
]
|
|
|
|
[botdetection.ip_limit]
|
|
|
|
# Link-local addresses (fe80::/10, 169.254.0.0/16) are not monitored.
|
|
filter_link_local = false
|
|
|
|
# link_token: per-session URL bypass token. Keep off.
|
|
link_token = false
|
|
|
|
[botdetection.ip_lists]
|
|
|
|
# ip_lists outranks every other method: an IP in pass_ip gets unrestricted
|
|
# access and is not checked by user-agent, rate limiting, or other bot
|
|
# detection. This is the ONLY bypass for the rate limiter (ip_limit): its JSON
|
|
# cap (API_MAX = 4 requests/IP/hour) is hardcoded in Python and cannot be
|
|
# raised from this file.
|
|
#
|
|
# Rootless podman strips the client IP on published-port hits: a request to the
|
|
# published port reaches the container with a slirp/bridge source (10.88.x /
|
|
# 10.89.x), never the real client IP. Only Caddy's X-Forwarded-For restores it.
|
|
# So a local subagent that hits the port directly must present a pass_ip source
|
|
# or it hits the 4/hour JSON cap. We trust loopback, the homelab LAN, and the
|
|
# host's own private podman/slirp ranges; all are reachable only from the host
|
|
# or LAN, never the internet.
|
|
pass_ip = [
|
|
'127.0.0.1/32',
|
|
'::1',
|
|
'192.168.0.0/24',
|
|
'10.88.0.0/16',
|
|
'10.89.0.0/16',
|
|
]
|
|
|
|
# Blocklist (none).
|
|
block_ip = []
|
|
|
|
# Keep the SearXNG org passlist (e.g. check.searx.space) enabled.
|
|
pass_searxng_org = true
|