diff --git a/Dockerfile b/Dockerfile index 120f0e7d..049b59d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -113,4 +113,6 @@ RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables i deluser tinyproxy && \ deluser unbound && \ mkdir /gluetun +# TODO remove once SAN is added to PIA servers certificates, see https://github.com/pia-foss/manual-connections/issues/10 +ENV GODEBUG=x509ignoreCN=0 COPY --from=builder /tmp/gobuild/entrypoint /entrypoint diff --git a/README.md b/README.md index e03010ee..0112087a 100644 --- a/README.md +++ b/README.md @@ -352,11 +352,11 @@ There are various ways to achieve this, depending on your use case. ## Private Internet Access port forwarding -Note that [not all regions support port forwarding](https://www.privateinternetaccess.com/helpdesk/kb/articles/how-do-i-enable-port-forwarding-on-my-vpn). - -When `PORT_FORWARDING=on`, a port will be forwarded on the VPN server side and written to the file specified by `PORT_FORWARDING_STATUS_FILE=/forwarded_port`. +When `PORT_FORWARDING=on`, a port will be forwarded on the VPN server side and written to the file specified by `PORT_FORWARDING_STATUS_FILE=/tmp/gluetun/forwarded_port`. It can be useful to mount this file as a volume to read it from other containers, for example to configure a torrenting client. +For `VPNSP=private internet access` (default), you will keep the same forwarded port for 60 days as long as you bind mount the `/gluetun` directory. + You can also use the HTTP control server (see below) to get the port forwarded. ## HTTP control server diff --git a/internal/provider/piav4.go b/internal/provider/piav4.go index 5866ab28..f364ccc3 100644 --- a/internal/provider/piav4.go +++ b/internal/provider/piav4.go @@ -24,9 +24,11 @@ import ( ) type piaV4 struct { - servers []models.PIAServer - timeNow timeNowFunc - randSource rand.Source + servers []models.PIAServer + timeNow timeNowFunc + randSource rand.Source + activeServer models.PIAServer + activeProtocol models.NetworkProtocol } func newPrivateInternetAccessV4(servers []models.PIAServer, timeNow timeNowFunc) *piaV4 { @@ -79,7 +81,29 @@ func (p *piaV4) GetOpenVPNConnection(selection models.ServerSelection) (connecti } } - return pickRandomConnection(connections, p.randSource), nil + connection = pickRandomConnection(connections, p.randSource) + + // Reverse lookup server from picked connection + found := false + for _, server := range servers { + IPs := server.OpenvpnUDP.IPs + if selection.Protocol == constants.TCP { + IPs = server.OpenvpnTCP.IPs + } + for _, IP := range IPs { + if connection.IP.Equal(IP) { + p.activeServer = server + found = true + break + } + } + if found { + break + } + } + p.activeProtocol = selection.Protocol + + return connection, nil } func (p *piaV4) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { @@ -90,11 +114,19 @@ func (p *piaV4) BuildConf(connection models.OpenVPNConnection, verbosity, uid, g func (p *piaV4) PortForward(ctx context.Context, client *http.Client, fileManager files.FileManager, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator, syncState func(port uint16) (pfFilepath models.Filepath)) { + if !p.activeServer.PortForward { + pfLogger.Error("The server %s does not support port forwarding", p.activeServer.Region) + return + } if gateway == nil { pfLogger.Error("aborting because: VPN gateway IP address was not found") return } - client, err := newPIAv4HTTPClient() + commonName := p.activeServer.OpenvpnUDP.CN + if p.activeProtocol == constants.TCP { + commonName = p.activeServer.OpenvpnTCP.CN + } + client, err := newPIAv4HTTPClient(commonName) if err != nil { pfLogger.Error("aborting because: %s", err) return @@ -225,7 +257,7 @@ func filterPIAServers(servers []models.PIAServer, region string) (filtered []mod return nil } -func newPIAv4HTTPClient() (client *http.Client, err error) { +func newPIAv4HTTPClient(serverName string) (client *http.Client, err error) { certificateBytes, err := base64.StdEncoding.DecodeString(constants.PIACertificateStrong) if err != nil { return nil, fmt.Errorf("cannot decode PIA root certificate: %w", err) @@ -237,10 +269,10 @@ func newPIAv4HTTPClient() (client *http.Client, err error) { rootCAs := x509.NewCertPool() rootCAs.AddCert(certificate) TLSClientConfig := &tls.Config{ - RootCAs: rootCAs, - MinVersion: tls.VersionTLS12, - InsecureSkipVerify: true, //nolint:gosec - } // TODO fix and remove InsecureSkipVerify + RootCAs: rootCAs, + MinVersion: tls.VersionTLS12, + ServerName: serverName, + } transport := http.Transport{ TLSClientConfig: TLSClientConfig, Proxy: http.ProxyFromEnvironment, @@ -267,9 +299,6 @@ func refreshPIAPortForwardData(client *http.Client, gateway net.IP, fileManager } data.Port, data.Signature, data.Expiration, err = fetchPIAPortForwardData(client, gateway, data.Token) if err != nil { - if strings.HasSuffix(err.Error(), "connection refused") { - return data, fmt.Errorf("cannot obtain port forwarding data: connection was refused, are you sure the region you are using supports port forwarding ;)") - } return data, fmt.Errorf("cannot obtain port forwarding data: %w", err) } if err := writePIAPortForwardData(fileManager, data); err != nil {