Skip to content

Give each site its own VPN exit - #28

Merged
combor merged 6 commits into
mainfrom
site-proxies
Oct 1, 2026
Merged

combor merged 6 commits into
mainfrom
site-proxies

Conversation

@combor

@combor combor commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Sites limit their streams to their own country, and a process-wide HTTPS_PROXY can exit in only one. This lets every site be reached through its own proxy, packages a systemd unit that runs a VPN exit per country, and adds a guide.

What changes

  • A proxy setting per site: -tvp-proxy / MAGNETOWID_TVP_PROXY and -bbc-proxy / MAGNETOWID_BBC_PROXY take an HTTP proxy's URL or direct. Unset, a site follows the environment's HTTPS_PROXY and NO_PROXY as before.
  • A site with a setting gets its own transport. Its streams carry it, so playlists, segments, subtitles and search-time probes travel the same way as the site's API, and for the streams ffmpeg fetches itself, ffmpeg is given that proxy as http_proxy. A site without a setting keeps the previous path unchanged: the default clients, and ffmpeg inheriting the environment and applying its rules per host.
  • magnetowid-vpn@.service in the deb, rpm and AUR packages: magnetowid-vpn@pl runs a Gluetun container from /etc/magnetowid/vpn-pl.env and publishes its HTTP proxy on a local port. Nothing runs until an instance is enabled, and Docker stays optional. An exit whose file names no provider fails once with status 78 instead of restarting forever.
  • vpn.env.example, installed as /usr/share/magnetowid/vpn.env.example, and compose.vpn.yaml for Compose installs.
  • docs/vpn.md: one exit for one site, then a second, for the Linux service and for Docker Compose.

Notes for review

  • The unit's own variables are prefixed (MAGNETOWID_VPN_PORT, MAGNETOWID_VPN_IMAGE) because Gluetun reads the same file and takes names such as PROXY_PORT for its own settings.
  • The provider check sits in the unit's main command: RestartPreventExitStatus does not apply to ExecStartPre.
  • Gluetun exits with 1 when stopped while its proxy has open connections, so the unit counts 1 as a stop and uses Restart=always; otherwise every systemctl stop leaves the exit failed.
  • magnetowid.env is untouched, so upgrades produce no config-file prompt.
  • The API key and the proxy settings are no longer flag defaults: usage, printed when a required setting is missing, lists defaults, and showed the API key (before this change too) and any password in a proxy URL.
  • Known limit, documented: Gluetun answers CONNECT with Transfer-Encoding: chunked, which ffmpeg does not accept, so the live and encrypted streams ffmpeg fetches itself fail through a Gluetun exit instead of bypassing it.

Testing

  • make test, including a test that runs the binary's usage with secrets in its environment.
  • make package-smoke on Debian, Fedora and Arch, against a fake docker that exits with 1 on stop as Gluetun does: the unit and example install; an exit with a blank provider ends failed with status 78 and no restarts; with a provider and a non-default port it runs docker with the expected arguments; it stops without failing, restarts when it exits by itself, survives a package upgrade, and is stopped and disabled on removal.
  • With a real Gluetun and VPN, following docs/vpn.md:
    • Linux service, from the Arch package: an exit started from the example file passed the country check; with a site's setting naming it, the packaged magnetowid.service found and downloaded a release from that site while the other site kept working on its own connection. With the two settings swapped, both sites' releases came back unavailable. A second exit on another port ran beside the first.
    • Docker Compose, with an image built from this branch: the country check passed and the site was found through http://vpn-pl:8888, with both files selected through COMPOSE_FILE in .env.
    • With the final unit settings: stopping an exit leaves it inactive, not failed, and an exit whose container stops by itself is started again.
  • Reviewed with Codex against main: two findings (ffmpeg losing the environment's per-host proxy rules for a site without a setting, and proxy passwords in the usage text), both fixed; the second review was clean.

combor added 4 commits October 1, 2026 17:11
Sites stream to their own country only, and one process-wide HTTPS_PROXY
can exit in a single country. -tvp-proxy and -bbc-proxy
(MAGNETOWID_TVP_PROXY, MAGNETOWID_BBC_PROXY) take an HTTP proxy's URL or
"direct"; unset, a site follows the environment as before.

Each site gets its own client, and its streams carry that client's
transport, so playlists, segments, subtitles and probes go the same way
as the site's API. ffmpeg, for the streams it fetches itself, is given
the proxy that transport would use instead of inheriting http_proxy.
magnetowid-vpn@<name> runs a Gluetun container from
/etc/magnetowid/vpn-<name>.env and serves its HTTP proxy on a local
port, one instance per country. Nothing starts until an instance is
enabled, and Docker stays optional.

An exit whose file names no provider fails once with status 78 instead
of restarting forever; the check sits in the main command because
RestartPreventExitStatus ignores ExecStartPre. Removing the package
stops and disables the instances.

The package smoke test starts an exit against a fake docker on Debian,
Fedora and Arch.
docs/vpn.md walks through one exit for one site and then a second, for
the Linux service and for Docker Compose, with compose.vpn.yaml as the
Compose example.
Gluetun exits with 1 when it is stopped while its proxy has open
connections, so every systemctl stop left the exit failed. Count that
status as a stop and restart on any exit instead; an unfinished settings
file still ends the unit with 78 and no retries.

The smoke test's fake docker now exits with 1 on stop, as Gluetun does.
@combor
combor marked this pull request as ready for review October 1, 2026 16:54
combor added 2 commits October 1, 2026 18:46
A site with no proxy setting got a transport that follows the
environment, and the downloader then settled ffmpeg's proxy from the
master playlist's URL alone, dropping no_proxy. A rendition or segment on
another host could go direct where the environment asked for the proxy.

Such a site now has no transport of its own, as before per-site
settings: its streams use the default clients and ffmpeg inherits the
environment and applies its rules to each host.
Flags took their defaults from the environment, and usage, printed when
a required setting is missing, lists every default: the API key and any
password in a proxy URL ended up in the service's log.

Those flags now default to empty and read the environment after
parsing. A flag still wins over the environment.
@combor
combor merged commit 3de66e9 into main Oct 1, 2026
11 of 14 checks passed
@combor
combor deleted the site-proxies branch October 1, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant