feat(pia): native WireGuard support with port forwarding#3393
Draft
neilcorp2kx wants to merge 3 commits into
Draft
feat(pia): native WireGuard support with port forwarding#3393neilcorp2kx wants to merge 3 commits into
neilcorp2kx wants to merge 3 commits into
Conversation
Add a mechanism to open narrowly-scoped, temporary outbound firewall allowances (destination IP + protocol + port + firewall mark, on the physical interface) that are needed before the VPN tunnel is up, then reliably torn down afterwards. - temporary.go: central registry of temporary allowances with retain-on-failure cleanup, idempotent deletion under a bounded non-cancelable context, and a sweep on firewall shutdown and before re-adding on reconnect. Attempted interfaces are tracked before the iptables append so a rule applied during a cancellation race is never left untracked. - iptables: emit and parse the '-m mark --mark <value>' match so the temporary ACCEPT rules are scoped to the bootstrap socket mark and can be deleted symmetrically. Reject malformed mark match forms instead of silently accepting a zero mark or panicking on a bad value. Covered by unit tests for rollback, deletion-failure retry, idempotence, mark scoping, reconnect de-duplication and mark parsing edge cases.
Private Internet Access previously only supported OpenVPN in gluetun because PIA registers a fresh WireGuard key per connection via its own API, which does not fit a static WireGuard config. This adds a provider-driven dynamic WireGuard path for PIA. At connection time, for VPN_SERVICE_PROVIDER=private internet access + VPN_TYPE=wireguard, gluetun now: - fetches PIA's live server list and selects a WireGuard server for the chosen region/name/hostname (honouring port-forwarding-only), - obtains an auth token, generates an ephemeral Curve25519 key pair, and registers the public key with the selected server (addKey) over TLS pinned to the server CN using the bundled PIA CA, - builds the WireGuard connection from the response (endpoint, peer key, interface address, DNS), re-registering on every reconnect, - performs the pre-tunnel token/server-list/addKey calls through scoped, temporary, mark-tagged firewall allowances so the killswitch is never opened to arbitrary destinations, resolving via a bootstrap dialer pinned to the physical default route. Port forwarding now works natively over WireGuard for PIA (gateway taken from the addKey server_vip). The earlier custom-provider workaround env VPN_PORT_FORWARDING_SERVER_NAME remains supported. Invalid, unspecified or non-IPv4 server_ip/server_vip/peer_ip values are rejected with errors rather than panicking, and the persisted port-forward token file is written and kept 0600. Config is just credentials + region, e.g.: VPN_SERVICE_PROVIDER=private internet access VPN_TYPE=wireguard SERVER_REGIONS=CA Vancouver VPN_PORT_FORWARDING=on VPN_PORT_FORWARDING_PROVIDER=private internet access Closes passteque#3070.
The VPN startup healthcheck performed a single 6s TCP+TLS check. When a tunnel's DNS server takes a few seconds to become ready after connect (e.g. providers that do pre-tunnel work), that single attempt could fail and trigger an endless VPN restart loop. Retry the short (6s) parallel TCP+TLS attempts within a 60s total budget with a 2s backoff, returning on the first success and preserving the existing aggregated error on budget exhaustion. Periodic checks are unchanged. This removes the need for any manual startup-grace tuning and is a general robustness improvement for all providers.
Member
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This adds native WireGuard support for Private Internet Access (PIA), including port forwarding, plus a small healthcheck robustness improvement that the native path depends on.
Today PIA is OpenVPN-only in gluetun, and PIA + WireGuard + port forwarding is a long-standing gap (#3070, #2147): the custom-provider workaround panics with
server name cannot be empty, and PIA cannot be selected as a WireGuard provider at all. The underlying reason is that PIA does not hand out static WireGuard configs — it registers a fresh WireGuard key per connection through its own API, which does not fit gluetun's normally-static WireGuard model.This PR implements that dynamic registration inside the PIA provider so the whole flow is just credentials + region:
No externally-generated WireGuard config, no static keys, no helper scripts. Server selection and key rotation happen automatically on every (re)connect.
Closes #3070.
What's in it
Three focused commits:
feat(firewall)— scoped temporary bootstrap connection allowances. A registry of narrowly-scoped (destination IP + protocol + port + firewall mark, physical interface) temporary outbound allowances needed before the tunnel is up, with retain-on-failure cleanup, idempotent deletion under a bounded non-cancelable context, and a sweep on shutdown / before reconnect. iptables gains-m mark --markemit+parse so the rules are mark-scoped and delete symmetrically.feat(pia)— native PIA WireGuard + port forwarding. At connect time the provider fetches PIA's live server list, selects a WireGuard server for the requested region/name/hostname, obtains a token, generates an ephemeral Curve25519 key pair, registers the public key (addKey) over TLS pinned to the server CN via the bundled PIA CA, and builds the WireGuard connection from the response (re-registering each reconnect). Port forwarding works natively (gateway from theaddKeyserver_vip).feat(healthcheck)— retry the startup TCP+TLS check within a 60s budget (2s backoff) instead of a single 6s attempt, so a tunnel whose DNS takes a few seconds to become ready doesn't trigger a restart loop. General improvement, not PIA-specific.Design / safety notes
addKeycalls are the only new outbound traffic before the tunnel exists. They go through the temporary, mark-tagged, exact-destination allowances above, resolved by a bootstrap dialer pinned to the physical default route, and are torn down before the tunnel comes up. Discovery/registration failure fails closed (the tunnel does not start). There is no fail-open to arbitrary destinations.0600.server_ip/server_vip/peer_ip) is rejected with errors rather than panicking.Testing
addKeyrequest/response mapping), healthcheck retry, and settings validation.Known / deferred (pre-existing, not introduced here)
OpenVPN.User/Passwordfor credentials, which are serialized by the authenticated/v1/vpn/settingscontrol endpoint like all other settings — a pre-existing exposure, called out here because the native path now depends on those fields. A redacted control DTO would be a separate change.addKeyclient trusts the bundled PIA CA additively on top of the system pool rather than pinning PIA-only. TLS name verification is enabled and there is noInsecureSkipVerify.A note on how this was developed
This was built and hardened with heavy, deliberate use of AI tooling — a combination of OpenAI Codex and Anthropic Claude — for implementation, iterative debugging against a live PIA endpoint, and multiple adversarial code-review passes focused on killswitch/leak safety, with all findings driven to resolution and re-verified. Flagging this transparently; every change has been reviewed and validated by a human, and I'm happy to walk through any part of the design or rationale.
Opening as a draft intentionally — soaking on a live setup for a bit before marking it ready. Feedback on the approach (especially the bootstrap-firewall handling and whether a redacted control DTO should be bundled) very welcome.