Skip to content

feat: add Wireguard support for Private Internet Access provider - #3438

Draft
abihf wants to merge 1 commit into
passteque:masterfrom
abihf:pia-wireguard
Draft

feat: add Wireguard support for Private Internet Access provider#3438
abihf wants to merge 1 commit into
passteque:masterfrom
abihf:pia-wireguard

Conversation

@abihf

@abihf abihf commented Aug 18, 2026

Copy link
Copy Markdown

Type

Please tick which one the following applies to your pull request:

  • it is AI generated 🤖 and I did review it 👨👩
  • it is humanly written like the good old days 👨‍🎨👩‍🎨
  • it is AI generated 🤖 I did not review it 💤

I review the implementation but not the tests

Description

  • Implement Wireguard configuration logic in the Private Internet Access provider.
  • Create functions to handle token fetching and Wireguard key addition.
  • Add tests for Wireguard configuration and related functionalities.
  • Update interfaces to include WireguardConfiger for better integration.
  • Enhance existing VPN setup to support Wireguard configurations.

Issue (optional)

Gluetun wiki associated pull request (optional)

- Implement Wireguard configuration logic in the Private Internet Access provider.
- Create functions to handle token fetching and Wireguard key addition.
- Add tests for Wireguard configuration and related functionalities.
- Update interfaces to include WireguardConfiger for better integration.
- Enhance existing VPN setup to support Wireguard configurations.
@qdm12

qdm12 commented Aug 18, 2026

Copy link
Copy Markdown
Member

there is already a pr for this which needs finishing

@Harland70

Copy link
Copy Markdown

Tested this branch at c8712ac against PIA (CA Toronto, port-forward-only). The WireGuard
flow itself works — but the auth step fails 100% of the time as shipped, and I think the fix
is in the updater rather than in wireguard.go alone.

The auth token is requested from the wrong class of PIA server

wireguard.go builds the token URL from connection.IP:

apiURL := url.URL{
    Scheme: "https",
    Host:   net.JoinHostPort(serverIP.String(), "443"),
    Path:   "/authv3/generateToken",
}

connection.IP is the WireGuard server. PIA serves /authv3/generateToken only from its
meta servers, which are a separate IP list within the same region. Checked against the
live server list for ca_toronto:

meta 66.56.80.129   -> HTTP 403   (TLS handshake OK; 403 is just the unauthenticated response)
meta 191.96.36.124  -> HTTP 403
wg   66.56.80.161   -> curl exit 35 (SSL connect error)
wg   191.96.36.133  -> curl exit 35

That SSL failure is exactly what gluetun reports:

ERROR [vpn] configuring wireguard: fetching auth token: authv3 token:
Get "https://66.56.80.224:443/authv3/generateToken": EOF

The v2 fallback can't cover for it

getToken falls back to www.privateinternetaccess.com/api/client/v2/token, but by then the
firewall is up and DNS isn't yet reachable through the tunnel:

client v2 token: Post "https://www.privateinternetaccess.com/api/client/v2/token":
dial tcp: lookup www.privateinternetaccess.com on 192.168.1.254:53:
write udp ...: operation not permitted

So both paths fail and the tunnel never establishes.

Why I think the fix belongs in the updater

updater/servers.go ingests region.Servers.WG, but nothing ingests region.Servers.Meta
grep -rn 'Meta' internal/provider/privateinternetaccess/ (minus tests) comes back empty. There
is no meta IP anywhere in the model for getToken to use. So it probably needs:

  1. the updater to ingest region.Servers.Meta
  2. the server record to carry a meta IP alongside the WG IP
  3. getToken to use it (and the fw.AcceptOutput(... 443 ...) rule to target it)

One wrinkle worth designing around: the TLS ServerName is the WG server's CN and PIA's certs
are per-server, so the meta IP has to be the one sharing that CN. PIA's serverlist also returns a
rotating subset per request, so a single fetch won't reliably contain both halves of a pair.

The rest of the PR works

Patching getToken to use a matching meta IP (toronto417: wg 191.96.36.103, meta
191.96.36.94) gets the full flow working first try:

INFO [wireguard] Using available kernelspace implementation
INFO [wireguard] Connecting to 191.96.36.109:1337
INFO [port forwarding] Port forwarded data expires in 62 days
INFO [port forwarding] port forwarded is 39526

Healthy, correct public IP, port forwarding live. Token -> addKey on 1337 -> handshake -> PF all
behave. SNI handling in httpclient.go is already correct.

Other checks

  • Dockerfile / go.mod / go.sum are untouched vs the merge-base
  • go test ./... -race: no data races; the only failures (internal/netlink, internal/wireguard)
    fail identically on the merge-base and look like they need real kernel netlink access
  • Measured against an OpenVPN container from the same image and region, with no other load:
    ~1.45 CPU-seconds per 100 MB on WireGuard vs ~7.10 on OpenVPN (~4.9x). I'd treat the raw
    throughput numbers as unreliable — endpoint quality varied far too much between runs to
    attribute any of it to the protocol.

Happy to test a revision if you push one.

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.

3 participants