Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for Wireguard in PIA #1836

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

FireMasterK
Copy link

Closes #612
Untested, as I don't have a PIA account

Warning: I don't really write a lot of Go code, so the structure may be bad!

@garlik82
Copy link

garlik82 commented Sep 3, 2023

I'm willing to test this. Already built the image but what should I put in my compose file?

@FireMasterK
Copy link
Author

I'm willing to test this. Already built the image but what should I put in my compose file?

You should be able to set your account credentials in the openvpn user credentials, and set VPN_TYPE=wireguard. You need not configure any wireguard keys, as ephemeral keys are automatically registered and used in the API.

@garlik82
Copy link

garlik82 commented Sep 3, 2023

I'm willing to test this. Already built the image but what should I put in my compose file?

You should be able to set your account credentials in the openvpn user credentials, and set VPN_TYPE=wireguard. You need not configure any wireguard keys, as ephemeral keys are automatically registered and used in the API.

ERROR VPN settings: provider settings: VPN provider name is not valid for Wireguard: value is not one of the possible choices: private internet access must be one of airvpn, custom, ivpn, mullvad, nordvpn, surfshark or windscrib.

This is what I get.

If I put custom instead of PIA I get the error saying no endpoint set

@FireMasterK
Copy link
Author

ERROR VPN settings: provider settings: VPN provider name is not valid for Wireguard: value is not one of the possible choices: private internet access must be one of airvpn, custom, ivpn, mullvad, nordvpn, surfshark or windscrib.

This is what I get.

If I put custom instead of PIA I get the error saying no endpoint set

I've fixed this in the latest commit, please build and try it again.

@garlik82
Copy link

garlik82 commented Sep 4, 2023

ERROR VPN settings: provider settings: VPN provider name is not valid for Wireguard: value is not one of the possible choices: private internet access must be one of airvpn, custom, ivpn, mullvad, nordvpn, surfshark or windscrib.
This is what I get.
If I put custom instead of PIA I get the error saying no endpoint set

I've fixed this in the latest commit, please build and try it again.

Now I get this:

ERROR [vpn] finding a VPN server: filtering servers: no server found: for VPN wireguard; protocol udp; region dk copenhagen; encryption preset strong


@FireMasterK
Copy link
Author

I've fixed this in the latest commit, please build and try it again.

Now I get this:

ERROR [vpn] finding a VPN server: filtering servers: no server found: for VPN wireguard; protocol udp; region dk copenhagen; encryption preset strong

Hi, please try again, this should now be fixed!

@garlik82
Copy link

garlik82 commented Sep 4, 2023

How do I know the right thing to put in SERVER_REGIONS= ?

@FireMasterK
Copy link
Author

How do I know the right thing to put in SERVER_REGIONS= ?

Anything that works with OpenVPN should most likely work with Wireguard too.

@garlik82
Copy link

garlik82 commented Sep 4, 2023

How do I know the right thing to put in SERVER_REGIONS= ?

Anything that works with OpenVPN should most likely work with Wireguard too.

It doesn't. Must likely it's searching on servers.json for a wireguard server but PIA only has ovpn server in the built in server list.

https://raw.githubusercontent.com/qdm12/gluetun/master/internal/storage/servers.json (builtin server list)
https://serverlist.piaservers.net/vpninfo/servers/v6 (pia server list)

@FireMasterK
Copy link
Author

It doesn't. Must likely it's searching on servers.json for a wireguard server but PIA only has ovpn server in the built in server list.

https://raw.githubusercontent.com/qdm12/gluetun/master/internal/storage/servers.json (builtin server list) https://serverlist.piaservers.net/vpninfo/servers/v6 (pia server list)

Could you give it a go again? I managed to reproduce those errors without an account, and each of them should be fixed now.

You may need to disable the firewall with FIREWALL=off (not sure how to work around it currently)

@garlik82
Copy link

garlik82 commented Sep 5, 2023

almost there.

ERROR [vpn] getting Wireguard connection: HTTP status code is not OK: https://www.privateinternetaccess.com/gtoken/generateToken: 403 403 Forbidden: response received: <title>403 Forbidden</title>

403 Forbidden


cloudflare </html

Copy link
Owner

@qdm12 qdm12 left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution!

Unfortunately this requires connectivity before the VPN is up, and that's a deal breaker for Gluetun. I'm slowly working towards a solution but this is far from done. The PR won't be useless though, it will be used at some point in the future 👍 !

result := replaceInString(testCase.s, testCase.substitutions)
result := ReplaceInString(testCase.s, testCase.substitutions)
Copy link
Owner

Choose a reason for hiding this comment

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

please keep this unexported (lowercase first letter), there is no need to export it

Comment on lines +11 to +38
// replaceInErr is used to remove sensitive information from errors.
func ReplaceInErr(err error, substitutions map[string]string) error {
s := ReplaceInString(err.Error(), substitutions)
return errors.New(s) //nolint:goerr113
}

// replaceInString is used to remove sensitive information.
func ReplaceInString(s string, substitutions map[string]string) string {
for old, new := range substitutions {
s = strings.ReplaceAll(s, old, new)
}
return s
}

func makeNOKStatusError(response *http.Response, substitutions map[string]string) (err error) {
url := response.Request.URL.String()
url = ReplaceInString(url, substitutions)

b, _ := io.ReadAll(response.Body)
shortenMessage := string(b)
shortenMessage = strings.ReplaceAll(shortenMessage, "\n", "")
shortenMessage = strings.ReplaceAll(shortenMessage, " ", " ")
shortenMessage = ReplaceInString(shortenMessage, substitutions)

return fmt.Errorf("%w: %s: %d %s: response received: %s",
ErrHTTPStatusCodeNotOK, url, response.StatusCode,
response.Status, shortenMessage)
}
Copy link
Owner

Choose a reason for hiding this comment

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

please move this back to original location; you can do a subsequent PR to move these around after for cleanup if necessary, but let's keep this PR focused on what it's supposed to do.


func (p *Provider) GetWireguardConnection(ctx context.Context, connection models.Connection, wireguardSettings settings.Wireguard, ipv6Supported bool) (settings wireguard.Settings, err error) {
// create http client to make requests to the server's API
client, err := newHTTPClient(connection.Hostname)
Copy link
Owner

Choose a reason for hiding this comment

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

handle the error

Suggested change
client, err := newHTTPClient(connection.Hostname)
client, err := newHTTPClient(connection.Hostname)
if err != nil {
return fmt.Errorf("creating http client: %w", err)
}

)

func (p *Provider) GetWireguardConnection(ctx context.Context, connection models.Connection, wireguardSettings settings.Wireguard, ipv6Supported bool) (settings wireguard.Settings, err error) {
// create http client to make requests to the server's API
Copy link
Owner

Choose a reason for hiding this comment

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

nit comment unneeded

Suggested change
// create http client to make requests to the server's API

// create http client to make requests to the server's API
client, err := newHTTPClient(connection.Hostname)

// generate a new private key
Copy link
Owner

Choose a reason for hiding this comment

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

nit comment unneeded

Suggested change
// generate a new private key

@@ -1 +1,2 @@
scratch.txt
.idea/
Copy link
Owner

Choose a reason for hiding this comment

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

remove before merging

@@ -0,0 +1,72 @@
package privateinternetaccess
Copy link
Owner

Choose a reason for hiding this comment

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

rename file to token.go, avoid utils in filename or directory, it's rather meaningless

Comment on lines +12 to +13
func fetchToken(ctx context.Context, client *http.Client,
tokenType string, authFilePath string) (token string, err error) {
Copy link
Owner

Choose a reason for hiding this comment

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

since this is used outside openvpn-only, we should inject the username and password and not use the openvpn auth file anymore (especially if it's for Wireguard, that makes it super strange)

case "gtoken":
path = "/gtoken/generateToken"
default:
return "", fmt.Errorf("token type %q is not supported", tokenType)
Copy link
Owner

Choose a reason for hiding this comment

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

you could even panic here, since that would be a programming error:

Suggested change
return "", fmt.Errorf("token type %q is not supported", tokenType)
panic(fmt.Sprintf("token type %q is not supported", tokenType))

publicKey := privateKey.PublicKey()

// fetch token from PIA's API
token, err := fetchToken(ctx, client, "gtoken", p.authFilePath)
Copy link
Owner

Choose a reason for hiding this comment

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

⚠️ This calls www.privateinternetaccess.com and that's a deal breaker for the killswitch and to avoid leaking data outside the tunnel (especially from other containers/devices connecting through gluetun). For example if the tunnel takes 5 seconds to setup, data will leak for 5 seconds; if the tunnel never manages to setup, it would leak for hours.
That's also why I did not implement this yet, but don't worry, your code will be used at some point.

I'm working on a few other moving parts, and the idea is that connecting to privateinternetaccess.com over https (and its dns resolution) would be the only one allowed outside the vpn, temporarily. But this is far from done, so expect this pull request to stay opened for a few months unfortunately.

Copy link

@bryanvaz bryanvaz Feb 22, 2024

Choose a reason for hiding this comment

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

Theoretically, if the agent were to connect via openvpn over a sideband, download the necessary wireguard configs over the ovpn tunnel, then bring up the primary wireguard tunnel using the downloaded wireguard credentials, would that address the leakage risk?

Obviously the scenario assumes all tunnelled traffic is only routed over wireguard, and thus is blocked till the wireguard tunnel is up. I would assume this should be the default behaviour anyways in the event of the PIA dropping the wireguard tunnel to rotate IPs.

@qdm12 qdm12 force-pushed the master branch 2 times, most recently from 7793495 to a194906 Compare September 28, 2023 07:08
@Nikamura
Copy link

any way I could help to get support added for PIA wireguard?

@qdm12 qdm12 force-pushed the master branch 2 times, most recently from 57a0645 to 059b128 Compare November 23, 2023 08:37
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.

Feature request: Wireguard support for PIA
5 participants