feat(protonvpn): Wireguard support (#2390)
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) (
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user