Feature: FastestVPN support (#383)

This commit is contained in:
Quentin McGaw
2021-03-05 23:12:19 -05:00
committed by GitHub
parent 9509b855f1
commit 9f4077d35d
21 changed files with 587 additions and 18 deletions

3
.github/labels.yml vendored
View File

@@ -21,6 +21,9 @@
- name: ":cloud: HideMyAss"
color: "cfe8d4"
description: ""
- name: ":cloud: FastestVPN"
color: "cfe8d4"
description: ""
- name: ":cloud: Mullvad"
color: "cfe8d4"
description: ""

View File

@@ -1,6 +1,6 @@
# Gluetun VPN client
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost,
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
HideMyAss, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
PureVPN, Surfshark, TorGuard, VyprVPN and Windscribe VPN servers
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
@@ -39,7 +39,7 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
## Features
- Based on Alpine 3.12 for a small Docker image of 52MB
- Supports: **Cyberghost**, **HideMyAss**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **Vyprvpn**, **Windscribe**, servers
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **Vyprvpn**, **Windscribe**, servers
- Supports Openvpn only for now
- DNS over TLS baked in with service provider(s) of your choice
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours

View File

@@ -23,6 +23,7 @@ func (c *cli) Update(ctx context.Context, args []string, os os.OS) error {
flagSet.BoolVar(&options.Stdout, "stdout", false, "Write results to console to modify the program (for maintainers)")
flagSet.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use")
flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost servers")
flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers")
flagSet.BoolVar(&options.HideMyAss, "hidemyass", false, "Update HideMyAss servers")
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers")

View File

@@ -0,0 +1,43 @@
package configuration
import (
"github.com/qdm12/gluetun/internal/constants"
)
func (settings *Provider) fastestvpnLines() (lines []string) {
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
return lines
}
func (settings *Provider) readFastestvpn(r reader) (err error) {
settings.Name = constants.Fastestvpn
settings.ServerSelection.Protocol, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
return err
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.FastestvpnHostnameChoices())
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices())
if err != nil {
return err
}
return nil
}

View File

@@ -56,8 +56,8 @@ var (
func (settings *OpenVPN) read(r reader) (err error) {
vpnsp, err := r.env.Inside("VPNSP", []string{
"cyberghost", "hidemyass", "mullvad", "nordvpn", "privado",
"pia", "private internet access", "privatevpn",
"cyberghost", "fastestvpn", "hidemyass", "mullvad", "nordvpn",
"privado", "pia", "private internet access", "privatevpn",
"purevpn", "surfshark", "torguard", "vyprvpn", "windscribe"},
params.Default("private internet access"))
if err != nil {
@@ -115,6 +115,8 @@ func (settings *OpenVPN) read(r reader) (err error) {
switch settings.Provider.Name {
case constants.Cyberghost:
readProvider = settings.Provider.readCyberghost
case constants.Fastestvpn:
readProvider = settings.Provider.readFastestvpn
case constants.HideMyAss:
readProvider = settings.Provider.readHideMyAss
case constants.Mullvad:

View File

@@ -31,6 +31,8 @@ func (settings *Provider) lines() (lines []string) {
switch strings.ToLower(settings.Name) {
case "cyberghost":
providerLines = settings.cyberghostLines()
case "fastestvpn":
providerLines = settings.fastestvpnLines()
case "hidemyass":
providerLines = settings.hideMyAssLines()
case "mullvad":

View File

@@ -42,6 +42,22 @@ func Test_Provider_lines(t *testing.T) {
" |--Client certificate is set",
},
},
"fastestvpn": {
settings: Provider{
Name: constants.Fastestvpn,
ServerSelection: ServerSelection{
Protocol: constants.UDP,
Hostnames: []string{"a", "b"},
Countries: []string{"c", "d"},
},
},
lines: []string{
"|--Fastestvpn settings:",
" |--Network protocol: udp",
" |--Hostnames: a, b",
" |--Countries: c, d",
},
},
"hidemyass": {
settings: Provider{
Name: constants.HideMyAss,

View File

@@ -15,9 +15,9 @@ type ServerSelection struct {
// Cyberghost
Group string `json:"group"`
Countries []string `json:"countries"` // HideMyAss, Mullvad, PrivateVPN, PureVPN
Countries []string `json:"countries"` // Fastestvpn, HideMyAss, Mullvad, PrivateVPN, PureVPN
Cities []string `json:"cities"` // HideMyAss, Mullvad, PrivateVPN, PureVPN, Windscribe
Hostnames []string `json:"hostnames"` // HideMyAss, PrivateVPN, Windscribe, Privado
Hostnames []string `json:"hostnames"` // Fastestvpn, HideMyAss, PrivateVPN, Windscribe, Privado
// Mullvad
ISPs []string `json:"isps"`

View File

@@ -11,6 +11,7 @@ type Updater struct {
Period time.Duration `json:"period"`
DNSAddress string `json:"dns_address"`
Cyberghost bool `json:"cyberghost"`
Fastestvpn bool `json:"fastestvpn"`
HideMyAss bool `json:"hidemyass"`
Mullvad bool `json:"mullvad"`
Nordvpn bool `json:"nordvpn"`

View File

@@ -0,0 +1,117 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
FastestvpnCertificate = "MIIFQjCCAyqgAwIBAgIIUfxepT+rr8owDQYJKoZIhvcNAQEMBQAwPzELMAkGA1UEBhMCS1kxEzARBgNVBAoTCkZhc3Rlc3RWUE4xGzAZBgNVBAMTEkZhc3Rlc3RWUE4gUm9vdCBDQTAeFw0xNzA5MTYwMDAxNDZaFw0yNzA5MTQwMDAxNDZaMD8xCzAJBgNVBAYTAktZMRMwEQYDVQQKEwpGYXN0ZXN0VlBOMRswGQYDVQQDExJGYXN0ZXN0VlBOIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1Xj+WfPTozFynFqc+c3CVrggIllaXEl5bY5VgFynXkqCTM6lSrfC4pNjGXUbqWe6RnGJbM4/6kUn+lQDjFSQV1rzP2eDS8+r5+X2WXh4AoeNRUWhvSG+HiHD/B2EFK+Nd5BRSdUjpKWAtsCmT2bBt7nT0jN1OdeNrLJeyF8siAqv/oQzKznF9aIe/N01b2M8ZOFTzoXi2fZAckgGWui8NB/lzkVIJqSkAPRL8qiJLuRCPVOX1PFD8vV//R8/QumtfbcYBMo6vCk2HmWdrh5OQHPxb3KJtbtG+Z1j8x6HGEAe17djYepBiRMyCEQvYgfD6tvFylc4IquhqE9yaP60PJod5TxpWnRQ6HIGSeBm+S+rYSMalTZ8+pUqOOA+IQCYpfpx6EKIJL/VsW2C7cXdvudxDhXPI5lR/QidCb9Ohq3WkfxXaYwzrngdg2avmNqId9R4KESuM9GoHW0dszfyBCh5wYfeaffMElfDam3B92NUwyhZwtIiv623WVXY9PPz+EDjSJsIAu2Vi1vdJyA4nD4k9Lwmx/1zTc/UaYVLsiBqL2WdfvFTeoWoV+dNxQXSEPhB8gwi8x4O4lZW0cwVy/6fa8KMY8gZbcbSTr7U5bRERfW8l+jY+mYKQ/M/ccgpxaHiw1/+4LWfbJQ7VhJJrTyN0C36FQzY1URkSXg+53wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmVEL4x6xdCqiqu2OBLs27EA8xGYwDQYJKoZIhvcNAQEMBQADggIBABCpITvO1+R4T9v2+onHiFxU5JjtCZ0zkXqRCMp/Z0UIYbeo1p07pZCPAUjBfGPCkAaR++OiG9sysALdJf8Y6HQKcyuAcWUqQnaIhoZ2JcAP7EKq7uCqsMhcYZD/j3O/3RPtSW5UOx6ItDU+Ua0t9Edho9whNw0VQXmo1JjYoP3FzPjuKoDWTSO1q5eYlZfwcTcs55O2shNkFafPg/6cCm5j6v9nyHrM3sk4LjkrBPUXVx2m/aoz219t8O9Ha9/CdMKXsPO/8gTUzpgnzSgPnGnBmi5xr1nspVN8X4E2f3D+DKqBim3YgslD68NcuFQvJ0/BxZzWVbrr+QXoyzaiCgXuogpIDc2bB6oRXqFnHNz36d4QJmJdWdSaijiS/peQ6EOPgOZ1GuObLWlDCBZLNeQ+N6QaiJxVO4XUj/s22i1IRtwdz84TRHrbWiIpEymsqmb/Ep5r4xV5d6+791axclfOTH7tQrY/SbPtTJI4OEgNekI8YfadQifpelF82MsFFEZuaQn0lj+fvLGtE/zKh3OdLTxRc5TAgBB+0T81+JQosygNr2aFFG0hxar1eyw/gLeG8H+7Ie50pyPvXO4OgB6Key8rSExpilQXlvAT1qX0qS3/K1i/9QkSE9ftIPT6vtwLV2sVQzfyanI4IZgWC6ryhvNLsRn0NFnQclor0+aq"
FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
)
func FastestvpnCountriesChoices() (choices []string) {
servers := FastestvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return choices
}
func FastestvpnHostnameChoices() (choices []string) {
servers := FastestvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
return choices
}
// FastestvpnServers returns the list of all VPN servers for FastestVPN.
//nolint:lll
func FastestvpnServers() []models.FastestvpnServer {
return []models.FastestvpnServer{
{Country: "Australia", Hostname: "au-sd-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{139, 99, 149, 10}}},
{Country: "Australia", Hostname: "au-sd-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{139, 99, 149, 10}}},
{Country: "Australia", Hostname: "au2-sd-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{139, 99, 131, 126}}},
{Country: "Australia", Hostname: "au2-sd-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{139, 99, 131, 126}}},
{Country: "Austria", Hostname: "at.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{86, 107, 21, 146}}},
{Country: "Belgium", Hostname: "bel1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{217, 138, 211, 67}}},
{Country: "Belgium", Hostname: "bel2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{217, 138, 211, 68}}},
{Country: "Belgium", Hostname: "bel3.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{217, 138, 211, 69}}},
{Country: "Brazil", Hostname: "br-jp-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{45, 179, 88, 31}}},
{Country: "Brazil", Hostname: "br-jp-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{45, 179, 88, 31}}},
{Country: "Bulgaria", Hostname: "bg.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{37, 46, 114, 46}}},
{Country: "Canada", Hostname: "canada.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{158, 69, 26, 75}}},
{Country: "Czechia", Hostname: "cz-pr-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{185, 216, 35, 218}}},
{Country: "Czechia", Hostname: "cz-pr-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{185, 216, 35, 218}}},
{Country: "Denmark", Hostname: "dk.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{185, 245, 84, 70}}},
{Country: "Finland", Hostname: "fi-hs-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{194, 34, 132, 19}}},
{Country: "Finland", Hostname: "fi-hs-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{194, 34, 132, 19}}},
{Country: "France", Hostname: "fr-rb-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{37, 59, 172, 213}}},
{Country: "France", Hostname: "fr-rb-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{37, 59, 172, 213}}},
{Country: "Germany", Hostname: "de1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{83, 143, 245, 254}}},
{Country: "Hong.Kong", Hostname: "hk-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{64, 120, 88, 115}}},
{Country: "Hong.Kong", Hostname: "hk-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{64, 120, 88, 115}}},
{Country: "India", Hostname: "in50.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{103, 104, 74, 32}}},
{Country: "India-Stream", Hostname: "in-stream.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{103, 104, 74, 30}}},
{Country: "Italy", Hostname: "it.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{37, 120, 207, 90}}},
{Country: "Japan", Hostname: "jp-tk-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{202, 239, 38, 147}}},
{Country: "Japan", Hostname: "jp-tk-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{202, 239, 38, 147}}},
{Country: "Luxembourg", Hostname: "lux1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{94, 242, 195, 147}}},
{Country: "Netherlands", Hostname: "nl.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{213, 5, 64, 22}}},
{Country: "Netherlands", Hostname: "nl2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{89, 46, 223, 251}}},
{Country: "Netherlands", Hostname: "nl3.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{89, 46, 223, 252}}},
{Country: "Norway", Hostname: "nr-ol-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{185, 90, 61, 20}}},
{Country: "Norway", Hostname: "nr-ol-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{185, 90, 61, 20}}},
{Country: "Poland", Hostname: "pl2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{194, 15, 196, 117}}},
{Country: "Portugal", Hostname: "pt.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{185, 90, 57, 146}}},
{Country: "Romania", Hostname: "ro.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{91, 199, 50, 131}}},
{Country: "Russia", Hostname: "russia.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{95, 213, 193, 52}}},
{Country: "Serbia", Hostname: "rs.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{37, 46, 115, 246}}},
{Country: "Singapore", Hostname: "sg-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{209, 58, 174, 195}}},
{Country: "Singapore", Hostname: "sg-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{209, 58, 174, 195}}},
{Country: "South.Korea", Hostname: "kr-so-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{103, 249, 31, 36}}},
{Country: "South.Korea", Hostname: "kr-so-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{103, 249, 31, 36}}},
{Country: "Spain", Hostname: "es-bl-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{193, 148, 19, 155}}},
{Country: "Spain", Hostname: "es-bl-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{193, 148, 19, 155}}},
{Country: "Sweden", Hostname: "se-st-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{128, 127, 104, 200}}},
{Country: "Sweden", Hostname: "se-st-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{128, 127, 104, 201}}},
{Country: "Sweden", Hostname: "se2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{79, 142, 76, 142}}},
{Country: "Switzerland", Hostname: "ch-zr-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{82, 102, 24, 254}}},
{Country: "Switzerland", Hostname: "ch-zr-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{82, 102, 24, 254}}},
{Country: "Turkey", Hostname: "tr-iz-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{185, 123, 102, 57}}},
{Country: "Turkey", Hostname: "tr.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{185, 123, 102, 57}}},
{Country: "UAE-Dubai", Hostname: "ue-db-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{45, 9, 249, 110}}},
{Country: "UAE-Dubai", Hostname: "ue-db-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{45, 9, 249, 110}}},
{Country: "UK", Hostname: "uk.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{5, 226, 139, 143}}},
{Country: "UK", Hostname: "uk6.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{5, 226, 139, 148}}},
{Country: "UK-Stream", Hostname: "uk-stream.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{195, 206, 169, 171}}},
{Country: "US-Atlanta", Hostname: "us-at-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{23, 82, 10, 205}}},
{Country: "US-Atlanta", Hostname: "us-at-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{23, 82, 10, 205}}},
{Country: "US-Charlotte", Hostname: "us-cf-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{192, 154, 253, 6}}},
{Country: "US-Charlotte", Hostname: "us-cf-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{192, 154, 253, 6}}},
{Country: "US-Chicago", Hostname: "us-ch1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{174, 34, 154, 209}}},
{Country: "US-Chicago", Hostname: "us-ch2.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{174, 34, 154, 207}}},
{Country: "US-Dallas", Hostname: "us-dl-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{74, 63, 219, 202}}},
{Country: "US-Dallas", Hostname: "us-dl-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{74, 63, 219, 202}}},
{Country: "US-Denver", Hostname: "us-dv1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{173, 248, 157, 107}}},
{Country: "US-Los.Angeles", Hostname: "us-la-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{64, 31, 35, 222}}},
{Country: "US-Los.Angeles", Hostname: "us-la-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{64, 31, 35, 222}}},
{Country: "US-Miami", Hostname: "us-mi-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{162, 255, 138, 231}}},
{Country: "US-Miami", Hostname: "us-mi-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{162, 255, 138, 232}}},
{Country: "US-Netflix", Hostname: "netflix.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{37, 59, 172, 215}}},
{Country: "US-New.York", Hostname: "us-ny-ovtcp-01.jumptoserver.com", UDP: false, TCP: true, IPs: []net.IP{{38, 132, 102, 107}}},
{Country: "US-New.York", Hostname: "us-ny-ovudp-01.jumptoserver.com", UDP: true, TCP: false, IPs: []net.IP{{38, 132, 102, 107}}},
{Country: "US-Phoenix", Hostname: "us-ph1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{23, 83, 184, 71}}},
{Country: "US-Seattle", Hostname: "us-se1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{23, 82, 33, 99}}},
{Country: "US-St.Louis", Hostname: "us-st1.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{148, 72, 173, 28}}},
{Country: "US-St.Louis", Hostname: "us-st3.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{148, 72, 173, 30}}},
{Country: "US-St.Louis", Hostname: "us-st4.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{148, 72, 173, 31}}},
{Country: "US-St.Louis", Hostname: "us-st5.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{148, 72, 173, 32}}},
{Country: "US-Washington", Hostname: "us-wt.jumptoserver.com", UDP: true, TCP: true, IPs: []net.IP{{23, 82, 15, 90}}},
}
}

View File

@@ -11,6 +11,11 @@ func GetAllServers() (allServers models.AllServers) {
Timestamp: 1612031135, // latest takes precedence
Servers: CyberghostServers(),
},
Fastestvpn: models.FastestvpnServers{
Version: 1,
Timestamp: 1613323814,
Servers: FastestvpnServers(),
},
HideMyAss: models.HideMyAssServers{
Version: 1,
Timestamp: 1614562368,

View File

@@ -39,6 +39,11 @@ func Test_versions(t *testing.T) {
version: allServers.Cyberghost.Version,
digest: "fd6242bb",
},
"Fastestvpn": {
model: models.FastestvpnServer{},
version: allServers.Fastestvpn.Version,
digest: "8825919b",
},
"HideMyAss": {
model: models.HideMyAssServer{},
version: allServers.HideMyAss.Version,
@@ -135,6 +140,11 @@ func Test_timestamps(t *testing.T) {
timestamp: allServers.Cyberghost.Timestamp,
digest: "5d3a8cbf",
},
"Fastestvpn": {
servers: allServers.Fastestvpn.Version,
timestamp: allServers.Fastestvpn.Timestamp,
digest: "da65734a",
},
"HideMyAss": {
servers: allServers.HideMyAss.Servers,
timestamp: allServers.HideMyAss.Timestamp,

View File

@@ -3,6 +3,8 @@ package constants
const (
// Cyberghost is a VPN provider.
Cyberghost = "cyberghost"
// Fastestvpn is a VPN provider.
Fastestvpn = "fastestvpn"
// HideMyAss is a VPN provider.
HideMyAss = "hidemyass"
// Mullvad is a VPN provider.

View File

@@ -17,6 +17,19 @@ func (s *CyberghostServer) String() string {
return fmt.Sprintf("{Region: %q, Group: %q, IPs: %s}", s.Region, s.Group, goStringifyIPs(s.IPs))
}
type FastestvpnServer struct {
Hostname string `json:"hostname"`
TCP bool `json:"tcp"`
UDP bool `json:"udp"`
Country string `json:"country"`
IPs []net.IP `json:"ips"`
}
func (s *FastestvpnServer) String() string {
return fmt.Sprintf("{Country: %q, Hostname: %q, UDP: %t, TCP: %t, IPs: %s}",
s.Country, s.Hostname, s.UDP, s.TCP, goStringifyIPs(s.IPs))
}
type HideMyAssServer struct {
Country string `json:"country"`
Region string `json:"region"`
@@ -83,6 +96,18 @@ func (p *PIAServer) String() string {
p.Region, p.ServerName, p.TCP, p.UDP, p.PortForward, goStringifyIP(p.IP))
}
type PrivatevpnServer struct {
Country string `json:"country"`
City string `json:"city"`
Hostname string `json:"hostname"`
IPs []net.IP `json:"ip"`
}
func (s *PrivatevpnServer) String() string {
return fmt.Sprintf("{Country: %q, City: %q, Hostname: %q, IPs: %s}",
s.Country, s.City, s.Hostname, goStringifyIPs(s.IPs))
}
type PurevpnServer struct {
Country string `json:"country"`
Region string `json:"region"`
@@ -137,18 +162,6 @@ func (s *WindscribeServer) String() string {
s.Region, s.City, s.Hostname, goStringifyIP(s.IP))
}
type PrivatevpnServer struct {
Country string `json:"country"`
City string `json:"city"`
Hostname string `json:"hostname"`
IPs []net.IP `json:"ip"`
}
func (s *PrivatevpnServer) String() string {
return fmt.Sprintf("{Country: %q, City: %q, Hostname: %q, IPs: %s}",
s.Country, s.City, s.Hostname, goStringifyIPs(s.IPs))
}
func goStringifyIP(ip net.IP) string {
s := fmt.Sprintf("%#v", ip)
s = strings.TrimSuffix(strings.TrimPrefix(s, "net.IP{"), "}")

View File

@@ -3,6 +3,7 @@ package models
type AllServers struct {
Version uint16 `json:"version"`
Cyberghost CyberghostServers `json:"cyberghost"`
Fastestvpn FastestvpnServers `json:"fastestvpn"`
HideMyAss HideMyAssServers `json:"hidemyass"`
Mullvad MullvadServers `json:"mullvad"`
Nordvpn NordvpnServers `json:"nordvpn"`
@@ -18,6 +19,7 @@ type AllServers struct {
func (a *AllServers) Count() int {
return len(a.Cyberghost.Servers) +
len(a.Fastestvpn.Servers) +
len(a.HideMyAss.Servers) +
len(a.Mullvad.Servers) +
len(a.Nordvpn.Servers) +
@@ -36,6 +38,11 @@ type CyberghostServers struct {
Timestamp int64 `json:"timestamp"`
Servers []CyberghostServer `json:"servers"`
}
type FastestvpnServers struct {
Version uint16 `json:"version"`
Timestamp int64 `json:"timestamp"`
Servers []FastestvpnServer `json:"servers"`
}
type HideMyAssServers struct {
Version uint16 `json:"version"`
Timestamp int64 `json:"timestamp"`

View File

@@ -0,0 +1,170 @@
package provider
import (
"context"
"fmt"
"math/rand"
"net"
"net/http"
"strconv"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/os"
)
type fastestvpn struct {
servers []models.FastestvpnServer
randSource rand.Source
}
func newFastestvpn(servers []models.FastestvpnServer, timeNow timeNowFunc) *fastestvpn {
return &fastestvpn{
servers: servers,
randSource: rand.NewSource(timeNow().UnixNano()),
}
}
func (f *fastestvpn) filterServers(countries, hostnames []string, protocol string) (servers []models.FastestvpnServer) {
var tcp, udp bool
if protocol == "tcp" {
tcp = true
} else {
udp = true
}
for _, server := range f.servers {
switch {
case filterByPossibilities(server.Country, countries):
case filterByPossibilities(server.Hostname, hostnames):
case tcp && !server.TCP:
case udp && !server.UDP:
default:
servers = append(servers, server)
}
}
return servers
}
func (f *fastestvpn) notFoundErr(selection configuration.ServerSelection) error {
message := "no server found for protocol " + selection.Protocol
if len(selection.Hostnames) > 0 {
message += " + hostnames " + commaJoin(selection.Hostnames)
}
if len(selection.Countries) > 0 {
message += " + countries " + commaJoin(selection.Countries)
}
return fmt.Errorf(message)
}
func (f *fastestvpn) GetOpenVPNConnection(selection configuration.ServerSelection) (
connection models.OpenVPNConnection, err error) {
var port uint16 = 4443
if selection.TargetIP != nil {
return models.OpenVPNConnection{IP: selection.TargetIP, Port: port, Protocol: selection.Protocol}, nil
}
servers := f.filterServers(selection.Countries, selection.Hostnames, selection.Protocol)
if len(servers) == 0 {
return connection, f.notFoundErr(selection)
}
var connections []models.OpenVPNConnection
for _, server := range servers {
for _, IP := range server.IPs {
connection := models.OpenVPNConnection{
IP: IP,
Port: port,
Protocol: selection.Protocol,
}
connections = append(connections, connection)
}
}
return pickRandomConnection(connections, f.randSource), nil
}
func (f *fastestvpn) BuildConf(connection models.OpenVPNConnection,
username string, settings configuration.OpenVPN) (lines []string) {
if len(settings.Cipher) == 0 {
settings.Cipher = aes256cbc
}
if len(settings.Auth) == 0 {
settings.Auth = sha256
}
if settings.MSSFix == 0 {
settings.MSSFix = 1450
}
lines = []string{
"client",
"dev tun",
"nobind",
"persist-key",
"ping 15",
"ping-exit 60",
"ping-timer-rem",
"tls-exit",
// Fastestvpn specific
"ping-restart 0",
"tls-client",
"tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA", //nolint:lll
"comp-lzo",
"key-direction 1",
"tun-mtu 1500",
"tun-mtu-extra 32",
"mssfix " + strconv.Itoa(int(settings.MSSFix)), // defaults to 1450
// Added constant values
"auth-nocache",
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
`pull-filter ignore "ping-restart"`,
"auth-retry nointeract",
"suppress-timestamps",
// Modified variables
"verb " + strconv.Itoa(settings.Verbosity),
"auth-user-pass " + constants.OpenVPNAuthConf,
"proto " + connection.Protocol,
"remote " + connection.IP.String() + " " + strconv.Itoa(int(connection.Port)),
"cipher " + settings.Cipher,
"auth " + settings.Auth,
}
if !settings.Root {
lines = append(lines, "user "+username)
}
lines = append(lines, []string{
"<ca>",
"-----BEGIN CERTIFICATE-----",
constants.FastestvpnCertificate,
"-----END CERTIFICATE-----",
"</ca>",
}...)
lines = append(lines, []string{
"<tls-auth>",
"-----BEGIN OpenVPN Static key V1-----",
constants.FastestvpnOpenvpnStaticKeyV1,
"-----END OpenVPN Static key V1-----",
"</tls-auth>",
"",
}...)
return lines
}
func (f *fastestvpn) PortForward(ctx context.Context, client *http.Client,
openFile os.OpenFileFunc, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,
syncState func(port uint16) (pfFilepath string)) {
panic("port forwarding is not supported for fastestvpn")
}

View File

@@ -27,6 +27,8 @@ func New(provider string, allServers models.AllServers, timeNow timeNowFunc) Pro
switch provider {
case constants.Cyberghost:
return newCyberghost(allServers.Cyberghost.Servers, timeNow)
case constants.Fastestvpn:
return newFastestvpn(allServers.Fastestvpn.Servers, timeNow)
case constants.HideMyAss:
return newHideMyAss(allServers.HideMyAss.Servers, timeNow)
case constants.Mullvad:

View File

@@ -18,6 +18,7 @@ func (s *storage) mergeServers(hardcoded, persisted models.AllServers) models.Al
return models.AllServers{
Version: hardcoded.Version,
Cyberghost: s.mergeCyberghost(hardcoded.Cyberghost, persisted.Cyberghost),
Fastestvpn: s.mergeFastestvpn(hardcoded.Fastestvpn, persisted.Fastestvpn),
HideMyAss: s.mergeHideMyAss(hardcoded.HideMyAss, persisted.HideMyAss),
Mullvad: s.mergeMullvad(hardcoded.Mullvad, persisted.Mullvad),
Nordvpn: s.mergeNordVPN(hardcoded.Nordvpn, persisted.Nordvpn),
@@ -41,6 +42,22 @@ func (s *storage) mergeCyberghost(hardcoded, persisted models.CyberghostServers)
return persisted
}
func (s *storage) mergeFastestvpn(hardcoded, persisted models.FastestvpnServers) models.FastestvpnServers {
if persisted.Timestamp <= hardcoded.Timestamp {
return hardcoded
}
versionDiff := hardcoded.Version - persisted.Version
if versionDiff > 0 {
s.logger.Info(
"Fastestvpn servers from file discarded because they are %d versions behind",
versionDiff)
return hardcoded
}
s.logger.Info("Using Fastestvpn servers from file (%s more recent)",
getUnixTimeDifference(persisted.Timestamp, hardcoded.Timestamp))
return persisted
}
func (s *storage) mergeHideMyAss(hardcoded, persisted models.HideMyAssServers) models.HideMyAssServers {
if persisted.Timestamp <= hardcoded.Timestamp {
return hardcoded

View File

@@ -18,6 +18,7 @@ var (
func countServers(allServers models.AllServers) int {
return len(allServers.Cyberghost.Servers) +
len(allServers.Fastestvpn.Servers) +
len(allServers.HideMyAss.Servers) +
len(allServers.Mullvad.Servers) +
len(allServers.Nordvpn.Servers) +

View File

@@ -0,0 +1,147 @@
package updater
import (
"context"
"fmt"
"net/http"
"regexp"
"sort"
"strings"
"github.com/qdm12/gluetun/internal/models"
)
func (u *updater) updateFastestvpn(ctx context.Context) (err error) {
servers, warnings, err := findFastestvpnServersFromZip(ctx, u.client, u.lookupIP)
if u.options.CLI {
for _, warning := range warnings {
u.logger.Warn("FastestVPN: %s", warning)
}
}
if err != nil {
return fmt.Errorf("cannot update FastestVPN servers: %w", err)
}
if u.options.Stdout {
u.println(stringifyFastestVPNServers(servers))
}
u.servers.Fastestvpn.Timestamp = u.timeNow().Unix()
u.servers.Fastestvpn.Servers = servers
return nil
}
func findFastestvpnServersFromZip(ctx context.Context, client *http.Client, lookupIP lookupIPFunc) (
servers []models.FastestvpnServer, warnings []string, err error) {
const zipURL = "https://support.fastestvpn.com/download/openvpn-tcp-udp-config-files"
contents, err := fetchAndExtractFiles(ctx, client, zipURL)
if err != nil {
return nil, nil, err
}
trailNumberExp := regexp.MustCompile(`[0-9]+$`)
type Data struct {
TCP bool
UDP bool
Country string
}
hostToData := make(map[string]Data)
for fileName, content := range contents {
const (
tcpSuffix = "-TCP.ovpn"
udpSuffix = "-UDP.ovpn"
)
var tcp, udp bool
var suffix string
switch {
case strings.HasSuffix(fileName, tcpSuffix):
suffix = tcpSuffix
tcp = true
case strings.HasSuffix(fileName, udpSuffix):
suffix = udpSuffix
udp = true
default:
warning := `filename "` + fileName + `" does not have a protocol suffix`
warnings = append(warnings, warning)
continue
}
countryWithNumber := strings.TrimSuffix(fileName, suffix)
number := trailNumberExp.FindString(countryWithNumber)
country := countryWithNumber[:len(countryWithNumber)-len(number)]
host, warning, err := extractHostFromOVPN(content)
if len(warning) > 0 {
warnings = append(warnings, warning)
}
if err != nil {
// treat error as warning and go to next file
warnings = append(warnings, err.Error()+" in "+fileName)
continue
}
data := hostToData[host]
data.Country = country
if tcp {
data.TCP = true
}
if udp {
data.UDP = true
}
hostToData[host] = data
}
hosts := make([]string, len(hostToData))
i := 0
for host := range hostToData {
hosts[i] = host
i++
}
const repetition = 1
const timeBetween = 0
const failOnErr = true
hostToIPs, _, err := parallelResolve(ctx, lookupIP, hosts, repetition, timeBetween, failOnErr)
if err != nil {
return nil, warnings, err
}
for host, IPs := range hostToIPs {
if len(IPs) == 0 {
warning := fmt.Sprintf("no IP address found for host %q", host)
warnings = append(warnings, warning)
continue
}
data := hostToData[host]
server := models.FastestvpnServer{
Hostname: host,
TCP: data.TCP,
UDP: data.UDP,
Country: data.Country,
IPs: uniqueSortedIPs(IPs),
}
servers = append(servers, server)
}
sort.Slice(servers, func(i, j int) bool {
if servers[i].Country == servers[j].Country {
return servers[i].Hostname < servers[j].Hostname
}
return servers[i].Country < servers[j].Country
})
return servers, warnings, nil
}
func stringifyFastestVPNServers(servers []models.FastestvpnServer) (s string) {
s = "func FastestvpnServers() []models.FastestvpnServer {\n"
s += " return []models.FastestvpnServer{\n"
for _, server := range servers {
s += " " + server.String() + ",\n"
}
s += " }\n"
s += "}"
return s
}

View File

@@ -60,6 +60,16 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe
}
}
if u.options.Fastestvpn {
u.logger.Info("updating Fastestvpn servers...")
if err := u.updateFastestvpn(ctx); err != nil {
u.logger.Error(err)
}
if err := ctx.Err(); err != nil {
return allServers, err
}
}
if u.options.HideMyAss {
u.logger.Info("updating HideMyAss servers...")
if err := u.updateHideMyAss(ctx); err != nil {