Files
hassio-addons/sabnzbd/rootfs/etc/nginx/servers/ingress.conf
Alexandre 556eca95d3 fix(sabnzbd): stop forwarding X-Forwarded-For, which ingress 403s on (#3023)
Reported from a remote session: ingress answered
`403 External internet access denied - https://sabnzbd.org/access-denied`.

Root cause is `check_access()` in `sabnzbd/interface.py`:

    # Never check the XFF header unless access would have been granted
    # based on the remote IP alone!
    if is_allowed and cfg.verify_xff_header() and (xff_ips := ...):
        is_allowed = all(is_local_addr(ip) or is_loopback_addr(ip)
                         for ip in xff_ips)

nginx's own address is loopback, so the first test passes, and then every
address in X-Forwarded-For has to be local too. Supervisor puts the browser's
address in that header, so anyone reaching Home Assistant from outside the LAN
is refused. `verify_xff_header` defaults to on (`cfg.py:531`), so this is not a
configuration a user opted into.

Reproduced against the running add-on, `GET /config/general/`:

    no X-Forwarded-For                    200
    X-Forwarded-For: 81.164.12.7          403  External internet access denied
    X-Forwarded-For: 81.164.12.7, 172.30.32.2  403
    X-Forwarded-For: 192.168.1.44         200

which is why it worked on the LAN and not from outside. Verified the fix the
same way, running the shipped nginx.conf and ingress.conf in front of the live
add-on with both variants side by side: the current config 403s on a public
address, the fixed one answers 200 for all three chains, and redirects, static
roots and the API are unaffected. That instance has an empty `url_base`, so the
pass-through routing is now confirmed for both `url_base` values.

The header was forwarded because SABnzbd reads it — the wrong test, since what
it does with it is reject. Clearing it leaves SABnzbd looking at nginx's
loopback address, which is what it saw before the header was added; ingress is
gated by Home Assistant authentication before reaching this proxy either way.

The evidence.md entry records the methodology error, per the skill's own
feed-the-skill rule.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 16:15:13 +02:00

43 lines
1.8 KiB
Plaintext

server {
listen %%interface%%:%%port%% default_server;
include /etc/nginx/includes/server_params.conf;
client_max_body_size 0;
location / {
proxy_pass http://127.0.0.1:8080;
# SABnzbd refuses any request whose Host is not an IP literal
# ("Access denied - Hostname verification failed"), so send the
# upstream socket rather than the browser's host.
proxy_set_header Host $proxy_host;
# X-Forwarded-For must NOT be forwarded. verify_xff_header defaults to
# on, and check_access() then requires every address in the header to
# be local, so Supervisor's copy of the browser's public address makes
# SABnzbd answer 403 "External internet access denied" for anyone
# reaching Home Assistant from outside the LAN. Clearing it leaves
# SABnzbd looking at nginx's own loopback address. Access is gated by
# Home Assistant authentication before it ever reaches this proxy.
proxy_set_header X-Forwarded-For "";
# The interface itself only emits relative links, so no body
# rewriting is needed. Redirects are the exception: Raiser() emits
# a path under url_base, so prefix them with the ingress entry.
# absolute_redirect must stay off, or nginx expands the rewritten
# Location into http://<browser host>:<ingress port>/..., a port the
# browser cannot reach.
proxy_redirect / %%ingress_entry%%/;
absolute_redirect off;
# The login cookie is hardcoded to Path=/, which on the ingress
# origin would send it to every other add-on's ingress path too.
proxy_cookie_path / %%ingress_entry%%/;
proxy_http_version 1.1;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}