feat(protonvpn): Wireguard support (#2390)

This commit is contained in:
Quentin McGaw
2024-08-03 16:10:35 +02:00
committed by GitHub
parent dea4080a7b
commit ac9446e296
11 changed files with 9584 additions and 111 deletions

View File

@@ -60,7 +60,7 @@ Lightweight swiss-knife-like VPN client to multiple VPN service providers
- Supports: **AirVPN**, **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers - Supports: **AirVPN**, **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
- Supports OpenVPN for all providers listed - Supports OpenVPN for all providers listed
- Supports Wireguard both kernelspace and userspace - Supports Wireguard both kernelspace and userspace
- For **AirVPN**, **FastestVPN**, **Ivpn**, **Mullvad**, **NordVPN**, **Perfect privacy**, **Surfshark** and **Windscribe** - For **AirVPN**, **FastestVPN**, **Ivpn**, **Mullvad**, **NordVPN**, **Perfect privacy**, **ProtonVPN**, **Surfshark** and **Windscribe**
- For **ProtonVPN**, **PureVPN**, **Torguard**, **VPN Unlimited** and **WeVPN** using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md) - For **ProtonVPN**, **PureVPN**, **Torguard**, **VPN Unlimited** and **WeVPN** using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md)
- For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md) - For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md)
- More in progress, see [#134](https://github.com/qdm12/gluetun/issues/134) - More in progress, see [#134](https://github.com/qdm12/gluetun/issues/134)

View File

@@ -39,6 +39,7 @@ func (p *Provider) validate(vpnType string, storage Storage) (err error) {
providers.Ivpn, providers.Ivpn,
providers.Mullvad, providers.Mullvad,
providers.Nordvpn, providers.Nordvpn,
providers.Protonvpn,
providers.Surfshark, providers.Surfshark,
providers.Windscribe, providers.Windscribe,
} }

View File

@@ -62,6 +62,7 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported bool) (err error)
providers.Ivpn, providers.Ivpn,
providers.Mullvad, providers.Mullvad,
providers.Nordvpn, providers.Nordvpn,
providers.Protonvpn,
providers.Surfshark, providers.Surfshark,
providers.Windscribe, providers.Windscribe,
) { ) {
@@ -173,10 +174,15 @@ func (w *Wireguard) overrideWith(other Wireguard) {
func (w *Wireguard) setDefaults(vpnProvider string) { func (w *Wireguard) setDefaults(vpnProvider string) {
w.PrivateKey = gosettings.DefaultPointer(w.PrivateKey, "") w.PrivateKey = gosettings.DefaultPointer(w.PrivateKey, "")
w.PreSharedKey = gosettings.DefaultPointer(w.PreSharedKey, "") w.PreSharedKey = gosettings.DefaultPointer(w.PreSharedKey, "")
if vpnProvider == providers.Nordvpn { switch vpnProvider {
case providers.Nordvpn:
defaultNordVPNAddress := netip.AddrFrom4([4]byte{10, 5, 0, 2}) defaultNordVPNAddress := netip.AddrFrom4([4]byte{10, 5, 0, 2})
defaultNordVPNPrefix := netip.PrefixFrom(defaultNordVPNAddress, defaultNordVPNAddress.BitLen()) defaultNordVPNPrefix := netip.PrefixFrom(defaultNordVPNAddress, defaultNordVPNAddress.BitLen())
w.Addresses = gosettings.DefaultSlice(w.Addresses, []netip.Prefix{defaultNordVPNPrefix}) w.Addresses = gosettings.DefaultSlice(w.Addresses, []netip.Prefix{defaultNordVPNPrefix})
case providers.Protonvpn:
defaultAddress := netip.AddrFrom4([4]byte{10, 2, 0, 2})
defaultPrefix := netip.PrefixFrom(defaultAddress, defaultAddress.BitLen())
w.Addresses = gosettings.DefaultSlice(w.Addresses, []netip.Prefix{defaultPrefix})
} }
defaultAllowedIPs := []netip.Prefix{ defaultAllowedIPs := []netip.Prefix{
netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.PrefixFrom(netip.IPv4Unspecified(), 0),

View File

@@ -39,8 +39,8 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
// Validate EndpointIP // Validate EndpointIP
switch vpnProvider { switch vpnProvider {
case providers.Airvpn, providers.Fastestvpn, providers.Ivpn, case providers.Airvpn, providers.Fastestvpn, providers.Ivpn,
providers.Mullvad, providers.Nordvpn, providers.Surfshark, providers.Mullvad, providers.Nordvpn, providers.Protonvpn,
providers.Windscribe: providers.Surfshark, providers.Windscribe:
// endpoint IP addresses are baked in // endpoint IP addresses are baked in
case providers.Custom: case providers.Custom:
if !w.EndpointIP.IsValid() || w.EndpointIP.IsUnspecified() { if !w.EndpointIP.IsValid() || w.EndpointIP.IsUnspecified() {
@@ -57,7 +57,8 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
return fmt.Errorf("%w", ErrWireguardEndpointPortNotSet) return fmt.Errorf("%w", ErrWireguardEndpointPortNotSet)
} }
// EndpointPort cannot be set // EndpointPort cannot be set
case providers.Fastestvpn, providers.Surfshark, providers.Nordvpn: case providers.Fastestvpn, providers.Nordvpn,
providers.Protonvpn, providers.Surfshark:
if *w.EndpointPort != 0 { if *w.EndpointPort != 0 {
return fmt.Errorf("%w", ErrWireguardEndpointPortSet) return fmt.Errorf("%w", ErrWireguardEndpointPortSet)
} }

View File

@@ -21,7 +21,7 @@ type Connection struct {
PubKey string `json:"pubkey"` PubKey string `json:"pubkey"`
// ServerName is used for PIA for port forwarding // ServerName is used for PIA for port forwarding
ServerName string `json:"server_name,omitempty"` ServerName string `json:"server_name,omitempty"`
// PortForward is used for PIA for port forwarding // PortForward is used for PIA and ProtonVPN for port forwarding
PortForward bool `json:"port_forward"` PortForward bool `json:"port_forward"`
} }

View File

@@ -135,7 +135,7 @@ func getMarkdownHeaders(vpnProvider string) (headers []string) {
case providers.Privatevpn: case providers.Privatevpn:
return []string{countryHeader, cityHeader, hostnameHeader} return []string{countryHeader, cityHeader, hostnameHeader}
case providers.Protonvpn: case providers.Protonvpn:
return []string{countryHeader, regionHeader, cityHeader, hostnameHeader, return []string{countryHeader, regionHeader, cityHeader, hostnameHeader, vpnHeader,
freeHeader, portForwardHeader, secureHeader, torHeader} freeHeader, portForwardHeader, secureHeader, torHeader}
case providers.Purevpn: case providers.Purevpn:
return []string{countryHeader, regionHeader, cityHeader, hostnameHeader, tcpHeader, udpHeader} return []string{countryHeader, regionHeader, cityHeader, hostnameHeader, tcpHeader, udpHeader}

View File

@@ -8,7 +8,7 @@ import (
func (p *Provider) GetConnection(selection settings.ServerSelection, ipv6Supported bool) ( func (p *Provider) GetConnection(selection settings.ServerSelection, ipv6Supported bool) (
connection models.Connection, err error) { connection models.Connection, err error) {
defaults := utils.NewConnectionDefaults(443, 1194, 0) //nolint:gomnd defaults := utils.NewConnectionDefaults(443, 1194, 51820) //nolint:gomnd
return utils.GetConnection(p.Name(), return utils.GetConnection(p.Name(),
p.storage, selection, defaults, ipv6Supported, p.randSource) p.storage, selection, defaults, ipv6Supported, p.randSource)
} }

View File

@@ -28,10 +28,11 @@ type logicalServer struct {
} }
type physicalServer struct { type physicalServer struct {
EntryIP netip.Addr `json:"EntryIP"` EntryIP netip.Addr `json:"EntryIP"`
ExitIP netip.Addr `json:"ExitIP"` ExitIP netip.Addr `json:"ExitIP"`
Domain string `json:"Domain"` Domain string `json:"Domain"`
Status uint8 `json:"Status"` Status uint8 `json:"Status"`
X25519PublicKey string `json:"X25519PublicKey"`
} }
func fetchAPI(ctx context.Context, client *http.Client) ( func fetchAPI(ctx context.Context, client *http.Client) (

View File

@@ -7,7 +7,7 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
) )
type ipToServer map[string]models.Server type ipToServers map[string][2]models.Server // first server is OpenVPN, second is Wireguard.
type features struct { type features struct {
secureCore bool secureCore bool
@@ -16,36 +16,50 @@ type features struct {
stream bool stream bool
} }
func (its ipToServer) add(country, region, city, name, hostname string, func (its ipToServers) add(country, region, city, name, hostname, wgPubKey string,
free bool, entryIP netip.Addr, features features) { free bool, entryIP netip.Addr, features features) {
key := entryIP.String() key := entryIP.String()
server, ok := its[key] servers, ok := its[key]
if ok { if ok {
return return
} }
server.VPN = vpn.OpenVPN baseServer := models.Server{
server.Country = country Country: country,
server.Region = region Region: region,
server.City = city City: city,
server.ServerName = name ServerName: name,
server.Hostname = hostname Hostname: hostname,
server.Free = free Free: free,
server.SecureCore = features.secureCore SecureCore: features.secureCore,
server.Tor = features.tor Tor: features.tor,
server.PortForward = features.p2p PortForward: features.p2p,
server.Stream = features.stream Stream: features.stream,
server.UDP = true IPs: []netip.Addr{entryIP},
server.TCP = true }
server.IPs = []netip.Addr{entryIP} openvpnServer := baseServer
its[key] = server openvpnServer.VPN = vpn.OpenVPN
openvpnServer.UDP = true
openvpnServer.TCP = true
servers[0] = openvpnServer
wireguardServer := baseServer
wireguardServer.VPN = vpn.Wireguard
wireguardServer.WgPubKey = wgPubKey
servers[1] = wireguardServer
its[key] = servers
} }
func (its ipToServer) toServersSlice() (servers []models.Server) { func (its ipToServers) toServersSlice() (serversSlice []models.Server) {
servers = make([]models.Server, 0, len(its)) const vpnProtocols = 2
for _, server := range its { serversSlice = make([]models.Server, 0, vpnProtocols*len(its))
servers = append(servers, server) for _, servers := range its {
serversSlice = append(serversSlice, servers[0], servers[1])
} }
return servers return serversSlice
}
func (its ipToServers) numberOfServers() int {
const serversPerIP = 2
return len(its) * serversPerIP
} }

View File

@@ -29,7 +29,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
common.ErrNotEnoughServers, count, minServers) common.ErrNotEnoughServers, count, minServers)
} }
ipToServer := make(ipToServer, count) ipToServer := make(ipToServers, count)
for _, logicalServer := range data.LogicalServers { for _, logicalServer := range data.LogicalServers {
region := getStringValue(logicalServer.Region) region := getStringValue(logicalServer.Region)
city := getStringValue(logicalServer.City) city := getStringValue(logicalServer.City)
@@ -65,6 +65,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
hostname := physicalServer.Domain hostname := physicalServer.Domain
entryIP := physicalServer.EntryIP entryIP := physicalServer.EntryIP
wgPubKey := physicalServer.X25519PublicKey
// Note: for multi-hop use the server name or hostname // Note: for multi-hop use the server name or hostname
// instead of the country // instead of the country
@@ -74,11 +75,11 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
u.warner.Warn(warning) u.warner.Warn(warning)
} }
ipToServer.add(country, region, city, name, hostname, free, entryIP, features) ipToServer.add(country, region, city, name, hostname, wgPubKey, free, entryIP, features)
} }
} }
if len(ipToServer) < minServers { if ipToServer.numberOfServers() < minServers {
return nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
common.ErrNotEnoughServers, len(ipToServer), minServers) common.ErrNotEnoughServers, len(ipToServer), minServers)
} }

File diff suppressed because it is too large Load Diff