Skip to content

Only force the bind address when it is unset or loopback - #275

Open
ivan-pinatti wants to merge 1 commit into
linuxserver:masterfrom
ivan-pinatti:respect-configured-bind-host
Open

Only force the bind address when it is unset or loopback#275
ivan-pinatti wants to merge 1 commit into
linuxserver:masterfrom
ivan-pinatti:respect-configured-bind-host

Conversation

@ivan-pinatti

Copy link
Copy Markdown

Description:

root/etc/s6-overlay/s6-rc.d/svc-sabnzbd/run always passes --server "$FAMILY" to SABnzbd, which overrides whatever host is stored in sabnzbd.ini and is then written straight back into it. This makes that override conditional. Before the exec, the script reads the host key out of the [misc] section of /config/sabnzbd.ini. If that value is empty or loopback (127.0.0.1, ::1, localhost), $FAMILY is imposed exactly as before. If it is anything else, that value is passed back through --server, so the flag becomes a no-op and the configured host survives the restart.

The check runs once in each branch rather than once above the if, because only the root branch mutates $FAMILY for the IPv6 fallback, and that mutation happens after where a shared check would sit. A single check ahead of the if would let the fallback clobber a configured host on a machine without IPv6, which is the bug this is meant to fix.

Reading the ini tracks the current section, because the NNTP server entries under [servers] carry their own unindented host = ... line and matching the wrong one would hand a news server's hostname to --server.

Benefits of this PR and context:

closes #274

This is a narrower version of something raised several times before, most recently in #240, and closed as intended behavior. I am not asking to revisit that decision. Forcing the bind address away from loopback still happens exactly as it does today, for exactly the reason given there, so nobody locks themselves out of the web UI by binding to 127.0.0.1. This only stops overriding a host that is already something other than unset or loopback, which cannot be the lockout case by definition.

The setup it unblocks is running sabnzbd with network_mode: container:<other container>, sharing another container's network namespace, which is the usual way to put a download client behind a VPN container for a kill switch. There is no bridge network to put a reverse proxy on and no port to map in that mode, because the network stack belongs to the other container, so the host has to be set directly and needs to survive a restart.

How Has This Been Tested?

shellcheck -s bash against the modified script, clean, and bash -n for syntax. The extraction was checked against four ini shapes: [misc] before [servers], [servers] before [misc], a loopback value, and no host key at all. The reordered case is the one that matters, since a plain grep '^host' returns the news server there.

I have not run it inside a built image yet. Happy to do that and report back before merge.

Source / References:

#240, #116, and the earlier reports of the same behaviour in #2, #35 and #42.

The service script passes --server unconditionally, which overrides host in
sabnzbd.ini and is persisted back into it, so a configured host cannot survive
a container start.

A configured host is now passed back through the same flag, making it a no-op,
unless the value is unset or loopback, in which case FAMILY is imposed exactly
as before. That keeps the lockout protection the flag exists for: a loopback
bind is unreachable from outside the container either way.

The check runs once per branch rather than once above the if, because only the
root branch mutates FAMILY for the IPv6 fallback, and a single check ahead of
it would let that fallback clobber a configured host on a host without IPv6.

Section tracking when reading the ini, because the NNTP server entries under
[servers] carry their own unindented host line.

closes linuxserver#274

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for opening this pull request! Be sure to follow the pull request template!

@ivan-pinatti

Copy link
Copy Markdown
Author

Heads up on the red permission_check, since it is not something this branch can fix and I would rather flag it than leave it looking like a problem with the contribution.

The job fails before it reaches any of my files:

Refusing to check out fork pull request code from a 'pull_request_target' workflow.

linuxserver/github-workflows/.github/workflows/init-svc-executable-permissions.yml@v1 runs actions/checkout@v7.0.1 with ref: ${{ github.event.pull_request.head.sha }}, and 7.0.1 added a guard that refuses a fork checkout in a pull_request_target context unless allow-unsafe-pr-checkout: true is set. So this should be failing on every fork pull request across the org right now, not only here.

For what it is worth the job itself looks harmless in substance, since it only runs find over the tree looking for run, finish and check files missing the executable bit, and it never executes anything it checked out. The guard cannot know that, which is why it trips.

Three ways out, in the order I would pick them:

  1. Run this particular check on pull_request rather than pull_request_target. It needs no secrets and no write token, so the fork restrictions cost it nothing.
  2. Read the modes from the API instead of checking out, for example GET /repos/{owner}/{repo}/git/trees/{sha}?recursive=1, which returns 100755 or 100644 per entry. That keeps pull_request_target and never puts fork code on the runner at all.
  3. Set allow-unsafe-pr-checkout: true, which is defensible given nothing is executed, but it is the weakest of the three.

The file this PR touches is already 100755, so the check passes once it can run. Happy to open an issue on linuxserver/github-workflows if that is the more useful place for this.

pull Bot pushed a commit to abraxas678/docker-torrent-box-with-vpn that referenced this pull request Sep 3, 2026
…am (ivan-pinatti-labs#146)

* chore: drop the jdownloader-2 and lazylibrarian patches, fixed upstream

Two of the four `patches/` directories exist to work around bugs that
upstream has since fixed. Both fixes have reached the image, so the patches
are now shadowing files that no longer need shadowing.

**jdownloader-2 (ivan-pinatti-labs#107).** The image's secret loader only set a variable that
was currently unset, and the Dockerfile pre-declares
`WEB_AUTHENTICATION_USERNAME`/`PASSWORD` as empty strings, so it always found
them "set" and skipped the secret. `baseimage-gui` 4.13.0 fixed it by giving
`load_env_var()` a `force` argument that the secret path passes.

Nothing had to move to collect this: `JDOWNLOADER2_VERSION` is `v26.08.1`,
which is built on `baseimage-gui` 4.13.1, and the pin had already drifted onto
it. Verified by pulling that exact image and extracting `/init`:

    # Always apply secrets: they are user-provided and take precedence over
    # environment variables (including empty image ENV defaults).
    load_env_var "${env_var_name}" "$(head -n1 < "${fpath}")" force

**lazylibrarian (ivan-pinatti-labs#109).** `login()` and `logout()` prepended `HTTP_ROOT` to a
`from_page` that nginx had already prefixed, so the first redirect after
logging in 404'd. Upstream !1832 merged on 2026-08-08 and has reached the
image, carrying the same guard this repository was patching in.

This one did need a bump: `40a389ea-ls310` does not have the fix and
`f9f62f7a-ls342` does, confirmed by extracting `auth.py` from both. Worth
noting the image has moved a long way since the patch was written, gaining a
`get_remote_ip()` and a `PROXY_LOCAL` whitelist path the patch predates, so
the patch had started overwriting newer upstream code to deliver a fix
upstream already had.

**Renovate holds.** jdownloader-2 comes off the `enabled: false` list with its
patch. lazylibrarian does not, and the comment now says why: issue ivan-pinatti-labs#119, its
tag carrying no version number, so `loose` versioning ranks releases by
reading the leading hex of a commit fragment as a number. That was always
going to matter "on the day `patches/lazylibrarian/` is dropped", and this is
that day. It stays held and hand-bumped until ivan-pinatti-labs#119 settles the scheme.

`test_patched_services_were_found`'s floor drops from four to two, which is
what now mount a patch: sabnzbd and mylar. Both remain blocked upstream,
checked the same day: linuxserver/docker-sabnzbd#275 is still open and the
shipped `run` still forces `--server` unconditionally, and mylar3's fix is on
`stable` but its image builds from the `v0.9.0` release tag, so even the
newest `ls257` rebuild does not carry it.

* fix: name the jdownloader-2 secrets so the image's own loader finds them

Dropping `patches/jdownloader2/10-webauth.sh` would have broken web
authentication, and the review caught it.

The patch read `/run/secrets/jdownloader2_username` and `_password` directly,
so the compose secret names never mattered. The image's own loader is not so
relaxed. `/init`'s cont-secrets stage does:

    find /run/secrets -maxdepth 1 -type f -name "CONT_ENV_*"
    env_var_name="${fpath#/run/secrets/CONT_ENV_}"

It only sees files whose name starts with `CONT_ENV_`, and derives the
environment variable by stripping exactly that prefix. Secrets called
`jdownloader2_username` and `jdownloader2_password` are invisible to that
loop, so `WEB_AUTHENTICATION_USERNAME` and `WEB_AUTHENTICATION_PASSWORD`
would have stayed at the image's empty defaults and the web UI would have
come up with no credential: the exact failure the patch existed to prevent,
reintroduced by removing it.

They are now named `CONT_ENV_WEB_AUTHENTICATION_USERNAME` and
`CONT_ENV_WEB_AUTHENTICATION_PASSWORD`. The host paths are unchanged, so
`rotate-passwords.sh` and its test, which reference
`configs/jdownloader2/secrets/password.txt`, are unaffected. Renaming rather
than using the long-syntax `target:` because nothing else in this repository
uses that form and podman's support for it is inconsistent.

`test_rotate_jdownloader2_password` is the check that matters here: it rotates
the password and then performs a real login against the running container,
asserting HTTP 200. `WEB_AUTHENTICATION=1` comes from the tracked
`configs/jdownloader2/.env`, not from the image, whose own default is 0, so
that test is not vacuous in CI.

Also picks up three stale references the review flagged and the previous
commit missed: `renovate.json5` still described four held images and still
said jdownloader-2 opens no pull requests, and `DEPENDENCY_UPDATES.md` still
said each of four patches has an open issue.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

[FEAT] Only force the SABnzbd bind address when it is unset or loopback

2 participants