diff --git a/README.md b/README.md index c935faca..b6ae854d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Gluetun VPN client -*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access, -Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN, Privado and TorGuard VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy* +*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, +Mullvad, NordVPN, Privado, Private Internet Access, PureVPN, +Surfshark, TorGuard, VyprVPN and Windscribe VPN servers +using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy* **ANNOUNCEMENT**: *New Docker image name `qmcgaw/gluetun`* @@ -37,7 +39,7 @@ Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN, Privado an ## Features - Based on Alpine 3.12 for a small Docker image of 52MB -- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN**, **Privado** and **TorGuard** servers +- Supports: **Cyberghost**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **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 diff --git a/internal/configuration/openvpn.go b/internal/configuration/openvpn.go index e362982e..dde8ac26 100644 --- a/internal/configuration/openvpn.go +++ b/internal/configuration/openvpn.go @@ -56,8 +56,9 @@ var ( func (settings *OpenVPN) read(r reader) (err error) { vpnsp, err := r.env.Inside("VPNSP", []string{ - "pia", "private internet access", "mullvad", "windscribe", "surfshark", "torguard", - "cyberghost", "vyprvpn", "nordvpn", "purevpn", "privado"}, + "cyberghost", "mullvad", "nordvpn", "privado", + "pia", "private internet access", "purevpn", "surfshark", + "torguard", "vyprvpn", "windscribe"}, params.Default("private internet access")) if err != nil { return err @@ -112,26 +113,26 @@ func (settings *OpenVPN) read(r reader) (err error) { var readProvider func(r reader) error switch settings.Provider.Name { - case constants.PrivateInternetAccess: - readProvider = settings.Provider.readPrivateInternetAccess - case constants.Mullvad: - readProvider = settings.Provider.readMullvad - case constants.Windscribe: - readProvider = settings.Provider.readWindscribe - case constants.Surfshark: - readProvider = settings.Provider.readSurfshark case constants.Cyberghost: readProvider = settings.Provider.readCyberghost - case constants.Vyprvpn: - readProvider = settings.Provider.readVyprvpn + case constants.Mullvad: + readProvider = settings.Provider.readMullvad case constants.Nordvpn: readProvider = settings.Provider.readNordvpn - case constants.Purevpn: - readProvider = settings.Provider.readPurevpn case constants.Privado: readProvider = settings.Provider.readPrivado + case constants.PrivateInternetAccess: + readProvider = settings.Provider.readPrivateInternetAccess + case constants.Purevpn: + readProvider = settings.Provider.readPurevpn + case constants.Surfshark: + readProvider = settings.Provider.readSurfshark case constants.Torguard: readProvider = settings.Provider.readTorguard + case constants.Vyprvpn: + readProvider = settings.Provider.readVyprvpn + case constants.Windscribe: + readProvider = settings.Provider.readWindscribe default: return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name) } diff --git a/internal/configuration/updater.go b/internal/configuration/updater.go index f10ef653..163ce9be 100644 --- a/internal/configuration/updater.go +++ b/internal/configuration/updater.go @@ -45,6 +45,7 @@ func (settings *Updater) read(r reader) (err error) { settings.Cyberghost = true settings.Mullvad = true settings.Nordvpn = true + settings.Privado = true settings.PIA = true settings.Purevpn = true settings.Surfshark = true diff --git a/internal/constants/servers.go b/internal/constants/servers.go index 0a56e125..2452987f 100644 --- a/internal/constants/servers.go +++ b/internal/constants/servers.go @@ -21,6 +21,11 @@ func GetAllServers() (allServers models.AllServers) { Timestamp: 1611096594, Servers: NordvpnServers(), }, + Privado: models.PrivadoServers{ + Version: 2, + Timestamp: 1612031135, + Servers: PrivadoServers(), + }, Pia: models.PiaServers{ Version: 4, Timestamp: 1613480675, @@ -31,11 +36,6 @@ func GetAllServers() (allServers models.AllServers) { Timestamp: 1612031135, Servers: PurevpnServers(), }, - Privado: models.PrivadoServers{ - Version: 2, - Timestamp: 1612031135, - Servers: PrivadoServers(), - }, Surfshark: models.SurfsharkServers{ Version: 1, Timestamp: 1612031135, diff --git a/internal/constants/servers_test.go b/internal/constants/servers_test.go index 8de9cb9d..0c73e62e 100644 --- a/internal/constants/servers_test.go +++ b/internal/constants/servers_test.go @@ -49,16 +49,16 @@ func Test_versions(t *testing.T) { version: allServers.Nordvpn.Version, digest: "040de8d0", }, - "Private Internet Access": { - model: models.PIAServer{}, - version: allServers.Pia.Version, - digest: "3e6066ec", - }, "Privado": { model: models.PrivadoServer{}, version: allServers.Privado.Version, digest: "1d5aeb23", }, + "Private Internet Access": { + model: models.PIAServer{}, + version: allServers.Pia.Version, + digest: "3e6066ec", + }, "Purevpn": { model: models.PurevpnServer{}, version: allServers.Purevpn.Version, @@ -135,6 +135,11 @@ func Test_timestamps(t *testing.T) { timestamp: allServers.Nordvpn.Timestamp, digest: "2296312c", }, + "Privado": { + servers: allServers.Privado.Servers, + timestamp: allServers.Privado.Timestamp, + digest: "2ac55360", + }, "Private Internet Access": { servers: allServers.Pia.Servers, timestamp: allServers.Pia.Timestamp, @@ -145,11 +150,6 @@ func Test_timestamps(t *testing.T) { timestamp: allServers.Purevpn.Timestamp, digest: "cd19edf5", }, - "Privado": { - servers: allServers.Privado.Servers, - timestamp: allServers.Privado.Timestamp, - digest: "2ac55360", - }, "Surfshark": { servers: allServers.Surfshark.Servers, timestamp: allServers.Surfshark.Timestamp, diff --git a/internal/constants/vpn.go b/internal/constants/vpn.go index 916321f1..a03112a5 100644 --- a/internal/constants/vpn.go +++ b/internal/constants/vpn.go @@ -1,26 +1,26 @@ package constants const ( - // PrivateInternetAccess is a VPN provider. - PrivateInternetAccess = "private internet access" - // Mullvad is a VPN provider. - Mullvad = "mullvad" - // Windscribe is a VPN provider. - Windscribe = "windscribe" - // Surfshark is a VPN provider. - Surfshark = "surfshark" // Cyberghost is a VPN provider. Cyberghost = "cyberghost" - // Vyprvpn is a VPN provider. - Vyprvpn = "vyprvpn" + // Mullvad is a VPN provider. + Mullvad = "mullvad" // NordVPN is a VPN provider. Nordvpn = "nordvpn" - // PureVPN is a VPN provider. - Purevpn = "purevpn" // Privado is a VPN provider. Privado = "privado" + // PrivateInternetAccess is a VPN provider. + PrivateInternetAccess = "private internet access" + // PureVPN is a VPN provider. + Purevpn = "purevpn" + // Surfshark is a VPN provider. + Surfshark = "surfshark" // Torguard is a VPN provider. Torguard = "torguard" + // Vyprvpn is a VPN provider. + Vyprvpn = "vyprvpn" + // Windscribe is a VPN provider. + Windscribe = "windscribe" ) const ( diff --git a/internal/models/server.go b/internal/models/server.go index 21cafd78..129b54a5 100644 --- a/internal/models/server.go +++ b/internal/models/server.go @@ -7,18 +7,14 @@ import ( "strings" ) -type PIAServer struct { - Region string `json:"region"` - ServerName string `json:"server_name"` - TCP bool `json:"tcp"` - UDP bool `json:"udp"` - PortForward bool `json:"port_forward"` - IP net.IP `json:"ip"` +type CyberghostServer struct { + Region string `json:"region"` + Group string `json:"group"` + IPs []net.IP `json:"ips"` } -func (p *PIAServer) String() string { - return fmt.Sprintf("{Region: %q, ServerName: %q, TCP: %t, UDP: %t, PortForward: %t, IP: %s}", - p.Region, p.ServerName, p.TCP, p.UDP, p.PortForward, goStringifyIP(p.IP)) +func (s *CyberghostServer) String() string { + return fmt.Sprintf("{Region: %q, Group: %q, IPs: %s}", s.Region, s.Group, goStringifyIPs(s.IPs)) } type MullvadServer struct { @@ -35,16 +31,53 @@ func (s *MullvadServer) String() string { s.Country, s.City, s.ISP, s.Owned, goStringifyIPs(s.IPs), goStringifyIPs(s.IPsV6)) } -type WindscribeServer struct { - Region string `json:"region"` - City string `json:"city"` - Hostname string `json:"hostname"` - IP net.IP `json:"ip"` +type NordvpnServer struct { //nolint:maligned + Region string `json:"region"` + Number uint16 `json:"number"` + IP net.IP `json:"ip"` + TCP bool `json:"tcp"` + UDP bool `json:"udp"` } -func (s *WindscribeServer) String() string { - return fmt.Sprintf("{Region: %q, City: %q, Hostname: %q, IP: %s}", - s.Region, s.City, s.Hostname, goStringifyIP(s.IP)) +func (s *NordvpnServer) String() string { + return fmt.Sprintf("{Region: %q, Number: %d, TCP: %t, UDP: %t, IP: %s}", + s.Region, s.Number, s.TCP, s.UDP, goStringifyIP(s.IP)) +} + +type PrivadoServer struct { + IP net.IP `json:"ip"` + Hostname string `json:"hostname"` +} + +func (s *PrivadoServer) String() string { + return fmt.Sprintf("{Hostname: %q, IP: %s}", + s.Hostname, goStringifyIP(s.IP)) +} + +type PIAServer struct { + Region string `json:"region"` + ServerName string `json:"server_name"` + TCP bool `json:"tcp"` + UDP bool `json:"udp"` + PortForward bool `json:"port_forward"` + IP net.IP `json:"ip"` +} + +func (p *PIAServer) String() string { + return fmt.Sprintf("{Region: %q, ServerName: %q, TCP: %t, UDP: %t, PortForward: %t, IP: %s}", + p.Region, p.ServerName, p.TCP, p.UDP, p.PortForward, goStringifyIP(p.IP)) +} + +type PurevpnServer struct { + Country string `json:"country"` + Region string `json:"region"` + City string `json:"city"` + IPs []net.IP `json:"ips"` +} + +func (s *PurevpnServer) String() string { + return fmt.Sprintf("{Country: %q, Region: %q, City: %q, IPs: %s}", + s.Country, s.Region, s.City, goStringifyIPs(s.IPs)) } type SurfsharkServer struct { @@ -68,16 +101,6 @@ func (s *TorguardServer) String() string { s.Country, s.City, s.Hostname, goStringifyIP(s.IP)) } -type CyberghostServer struct { - Region string `json:"region"` - Group string `json:"group"` - IPs []net.IP `json:"ips"` -} - -func (s *CyberghostServer) String() string { - return fmt.Sprintf("{Region: %q, Group: %q, IPs: %s}", s.Region, s.Group, goStringifyIPs(s.IPs)) -} - type VyprvpnServer struct { Region string `json:"region"` IPs []net.IP `json:"ips"` @@ -87,39 +110,16 @@ func (s *VyprvpnServer) String() string { return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs)) } -type NordvpnServer struct { //nolint:maligned - Region string `json:"region"` - Number uint16 `json:"number"` - IP net.IP `json:"ip"` - TCP bool `json:"tcp"` - UDP bool `json:"udp"` -} - -func (s *NordvpnServer) String() string { - return fmt.Sprintf("{Region: %q, Number: %d, TCP: %t, UDP: %t, IP: %s}", - s.Region, s.Number, s.TCP, s.UDP, goStringifyIP(s.IP)) -} - -type PurevpnServer struct { - Country string `json:"country"` - Region string `json:"region"` - City string `json:"city"` - IPs []net.IP `json:"ips"` -} - -func (s *PurevpnServer) String() string { - return fmt.Sprintf("{Country: %q, Region: %q, City: %q, IPs: %s}", - s.Country, s.Region, s.City, goStringifyIPs(s.IPs)) -} - -type PrivadoServer struct { - IP net.IP `json:"ip"` +type WindscribeServer struct { + Region string `json:"region"` + City string `json:"city"` Hostname string `json:"hostname"` + IP net.IP `json:"ip"` } -func (s *PrivadoServer) String() string { - return fmt.Sprintf("{Hostname: %q, IP: %s}", - s.Hostname, goStringifyIP(s.IP)) +func (s *WindscribeServer) String() string { + return fmt.Sprintf("{Region: %q, City: %q, Hostname: %q, IP: %s}", + s.Region, s.City, s.Hostname, goStringifyIP(s.IP)) } func goStringifyIP(ip net.IP) string { diff --git a/internal/models/servers.go b/internal/models/servers.go index 76b18dbd..ec6f5e1d 100644 --- a/internal/models/servers.go +++ b/internal/models/servers.go @@ -5,8 +5,8 @@ type AllServers struct { Cyberghost CyberghostServers `json:"cyberghost"` Mullvad MullvadServers `json:"mullvad"` Nordvpn NordvpnServers `json:"nordvpn"` - Pia PiaServers `json:"pia"` Privado PrivadoServers `json:"privado"` + Pia PiaServers `json:"pia"` Purevpn PurevpnServers `json:"purevpn"` Surfshark SurfsharkServers `json:"surfshark"` Torguard TorguardServers `json:"torguard"` @@ -18,8 +18,8 @@ func (a *AllServers) Count() int { return len(a.Cyberghost.Servers) + len(a.Mullvad.Servers) + len(a.Nordvpn.Servers) + - len(a.Pia.Servers) + len(a.Privado.Servers) + + len(a.Pia.Servers) + len(a.Purevpn.Servers) + len(a.Surfshark.Servers) + len(a.Torguard.Servers) + @@ -42,16 +42,16 @@ type NordvpnServers struct { Timestamp int64 `json:"timestamp"` Servers []NordvpnServer `json:"servers"` } -type PiaServers struct { - Version uint16 `json:"version"` - Timestamp int64 `json:"timestamp"` - Servers []PIAServer `json:"servers"` -} type PrivadoServers struct { Version uint16 `json:"version"` Timestamp int64 `json:"timestamp"` Servers []PrivadoServer `json:"servers"` } +type PiaServers struct { + Version uint16 `json:"version"` + Timestamp int64 `json:"timestamp"` + Servers []PIAServer `json:"servers"` +} type PurevpnServers struct { Version uint16 `json:"version"` Timestamp int64 `json:"timestamp"` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 98505c18..15cf9d5e 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -25,26 +25,26 @@ type Provider interface { func New(provider string, allServers models.AllServers, timeNow timeNowFunc) Provider { switch provider { - case constants.PrivateInternetAccess: - return newPrivateInternetAccess(allServers.Pia.Servers, timeNow) - case constants.Mullvad: - return newMullvad(allServers.Mullvad.Servers, timeNow) - case constants.Windscribe: - return newWindscribe(allServers.Windscribe.Servers, timeNow) - case constants.Surfshark: - return newSurfshark(allServers.Surfshark.Servers, timeNow) case constants.Cyberghost: return newCyberghost(allServers.Cyberghost.Servers, timeNow) - case constants.Vyprvpn: - return newVyprvpn(allServers.Vyprvpn.Servers, timeNow) + case constants.Mullvad: + return newMullvad(allServers.Mullvad.Servers, timeNow) case constants.Nordvpn: return newNordvpn(allServers.Nordvpn.Servers, timeNow) - case constants.Purevpn: - return newPurevpn(allServers.Purevpn.Servers, timeNow) case constants.Privado: return newPrivado(allServers.Privado.Servers, timeNow) + case constants.PrivateInternetAccess: + return newPrivateInternetAccess(allServers.Pia.Servers, timeNow) + case constants.Purevpn: + return newPurevpn(allServers.Purevpn.Servers, timeNow) + case constants.Surfshark: + return newSurfshark(allServers.Surfshark.Servers, timeNow) case constants.Torguard: return newTorguard(allServers.Torguard.Servers, timeNow) + case constants.Vyprvpn: + return newVyprvpn(allServers.Vyprvpn.Servers, timeNow) + case constants.Windscribe: + return newWindscribe(allServers.Windscribe.Servers, timeNow) default: return nil // should never occur } diff --git a/internal/storage/merge.go b/internal/storage/merge.go index 39089b96..ec884332 100644 --- a/internal/storage/merge.go +++ b/internal/storage/merge.go @@ -20,8 +20,8 @@ func (s *storage) mergeServers(hardcoded, persisted models.AllServers) models.Al Cyberghost: s.mergeCyberghost(hardcoded.Cyberghost, persisted.Cyberghost), Mullvad: s.mergeMullvad(hardcoded.Mullvad, persisted.Mullvad), Nordvpn: s.mergeNordVPN(hardcoded.Nordvpn, persisted.Nordvpn), - Pia: s.mergePIA(hardcoded.Pia, persisted.Pia), Privado: s.mergePrivado(hardcoded.Privado, persisted.Privado), + Pia: s.mergePIA(hardcoded.Pia, persisted.Pia), Purevpn: s.mergePureVPN(hardcoded.Purevpn, persisted.Purevpn), Surfshark: s.mergeSurfshark(hardcoded.Surfshark, persisted.Surfshark), Torguard: s.mergeTorguard(hardcoded.Torguard, persisted.Torguard), @@ -57,22 +57,6 @@ func (s *storage) mergeNordVPN(hardcoded, persisted models.NordvpnServers) model return persisted } -func (s *storage) mergePIA(hardcoded, persisted models.PiaServers) models.PiaServers { - if persisted.Timestamp <= hardcoded.Timestamp { - return hardcoded - } - versionDiff := hardcoded.Version - persisted.Version - if versionDiff > 0 { - s.logger.Info( - "PIA servers from file discarded because they are %d versions behind", - versionDiff) - return hardcoded - } - s.logger.Info("Using PIA servers from file (%s more recent)", - getUnixTimeDifference(persisted.Timestamp, hardcoded.Timestamp)) - return persisted -} - func (s *storage) mergePrivado(hardcoded, persisted models.PrivadoServers) models.PrivadoServers { if persisted.Timestamp <= hardcoded.Timestamp { return hardcoded @@ -89,6 +73,22 @@ func (s *storage) mergePrivado(hardcoded, persisted models.PrivadoServers) model return persisted } +func (s *storage) mergePIA(hardcoded, persisted models.PiaServers) models.PiaServers { + if persisted.Timestamp <= hardcoded.Timestamp { + return hardcoded + } + versionDiff := hardcoded.Version - persisted.Version + if versionDiff > 0 { + s.logger.Info( + "PIA servers from file discarded because they are %d versions behind", + versionDiff) + return hardcoded + } + s.logger.Info("Using PIA servers from file (%s more recent)", + getUnixTimeDifference(persisted.Timestamp, hardcoded.Timestamp)) + return persisted +} + func (s *storage) mergePureVPN(hardcoded, persisted models.PurevpnServers) models.PurevpnServers { if persisted.Timestamp <= hardcoded.Timestamp { return hardcoded diff --git a/internal/storage/sync.go b/internal/storage/sync.go index d2a40448..6fa7d87e 100644 --- a/internal/storage/sync.go +++ b/internal/storage/sync.go @@ -20,8 +20,8 @@ func countServers(allServers models.AllServers) int { return len(allServers.Cyberghost.Servers) + len(allServers.Mullvad.Servers) + len(allServers.Nordvpn.Servers) + - len(allServers.Pia.Servers) + len(allServers.Privado.Servers) + + len(allServers.Pia.Servers) + len(allServers.Purevpn.Servers) + len(allServers.Surfshark.Servers) + len(allServers.Torguard.Servers) + diff --git a/internal/updater/updater.go b/internal/updater/updater.go index c078792c..2f5acfdd 100644 --- a/internal/updater/updater.go +++ b/internal/updater/updater.go @@ -81,9 +81,9 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe } } - if u.options.PIA { - u.logger.Info("updating Private Internet Access servers...") - if err := u.updatePIA(ctx); err != nil { + if u.options.Privado { + u.logger.Info("updating Privado servers...") + if err := u.updatePrivado(ctx); err != nil { u.logger.Error(err) } if ctx.Err() != nil { @@ -91,9 +91,9 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe } } - if u.options.Privado { - u.logger.Info("updating Privado servers...") - if err := u.updatePrivado(ctx); err != nil { + if u.options.PIA { + u.logger.Info("updating Private Internet Access servers...") + if err := u.updatePIA(ctx); err != nil { u.logger.Error(err) } if ctx.Err() != nil {