diff --git a/Dockerfile b/Dockerfile
index 046053f02..75097c1ed 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -42,7 +42,7 @@ ENV VPNSP=pia \
UID=1000 \
GID=1000 \
IP_STATUS_FILE="/ip" \
- # PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN only
+ # PIA, Windscribe, Surfshark, Cyberghost, Vyprvpn, NordVPN, PureVPN only
USER= \
PASSWORD= \
REGION= \
@@ -50,9 +50,10 @@ ENV VPNSP=pia \
PIA_ENCRYPTION=strong \
PORT_FORWARDING=off \
PORT_FORWARDING_STATUS_FILE="/forwarded_port" \
- # Mullvad only
+ # Mullvad and PureVPN only
COUNTRY= \
CITY= \
+ # Mullvad only
ISP= \
# Mullvad and Windscribe only
PORT= \
@@ -100,7 +101,7 @@ ENTRYPOINT ["/entrypoint"]
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
HEALTHCHECK --interval=10m --timeout=10s --start-period=30s --retries=2 CMD /entrypoint healthcheck
RUN apk add -q --progress --no-cache --update openvpn ca-certificates iptables ip6tables unbound tinyproxy tzdata && \
- echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
+ echo "http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
apk add -q --progress --no-cache --update shadowsocks-libev && \
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/tinyproxy/tinyproxy.conf && \
deluser openvpn && \
diff --git a/README.md b/README.md
index 39bc9f98d..52847cdfe 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Gluetun VPN client
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
-Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN and NordVPN VPN servers, using Go, OpenVPN,
+Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN and PureVPN VPN servers, using Go, OpenVPN,
iptables, DNS over TLS, ShadowSocks and Tinyproxy*
**ANNOUNCEMENT**: *[Video of the Git history of Gluetun](https://youtu.be/khipOYJtGJ0)*
@@ -35,7 +35,7 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy*
## Features
- Based on Alpine 3.12 for a small Docker image of 52MB
-- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn** and **NordVPN** servers
+- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN** and **PureVPN** servers
- 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
- Choose the vpn network protocol, `udp` or `tcp`
@@ -55,6 +55,7 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy*
- **Cyberghost**: Pick the [region](https://github.com/qdm12/private-internet-access-docker/wiki/Cyberghost) and server group.
- **VyprVPN**: Pick the [region](https://www.vyprvpn.com/server-locations), port forwarding works by default (see `FIREWALL_VPN_INPUT_PORTS` though)
- **NordVPN**: Pick the region and optionally the server number
+- **PureVPN**: Pick the region, and optionally the country and city
### Extra niche features
@@ -77,6 +78,7 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy*
- Cyberghost: **username**, **password** and **device client key file** ([sign up](https://www.cyberghostvpn.com/en_US/buy/cyberghost-vpn-4))
- Vyprvpn: **username** and **password**
- NordVPN: **username** and **password**
+ - PureVPN: **username** and **password**
- If you have a host or router firewall, please refer [to the firewall documentation](https://github.com/qdm12/private-internet-access-docker/blob/master/doc/firewall.md)
1. On some devices you may need to setup your tunnel kernel module on your host with `insmod /lib/modules/tun.ko` or `modprobe tun`
@@ -125,7 +127,7 @@ Want more testing? โถ [see the Wiki](https://github.com/qdm12/private-internet-
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
-| ๐ `VPNSP` | `private internet access` | `private internet access`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn` | VPN Service Provider |
+| ๐ `VPNSP` | `private internet access` | `private internet access`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn`, `purevpn` | VPN Service Provider |
| `IP_STATUS_FILE` | `/ip` | Any filepath | Filepath to store the public IP address assigned |
| `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use |
| `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level |
@@ -212,6 +214,15 @@ Want more testing? โถ [see the Wiki](https://github.com/qdm12/private-internet-
| `REGION` | | One of the NordVPN server country, i.e. `Switzerland` | VPN server country |
| `SERVER_NUMBER` | | Server integer number | Optional server number. For example `251` for `Italy #251` |
+- PureVPN
+
+ | Variable | Default | Choices | Description |
+ | --- | --- | --- | --- |
+ | ๐ `USER` | | | Your user ID |
+ | ๐ `REGION` | | One of the [PureVPN regions](https://support.purevpn.com/vpn-servers) | VPN server region |
+ | `COUNTRY` | | One of the [PureVPN countries](https://support.purevpn.com/vpn-servers) | VPN server country |
+ | `CITY` | | One of the [PureVPN cities](https://support.purevpn.com/vpn-servers) | VPN server city |
+
### DNS over TLS
None of the following values are required.
diff --git a/cmd/locationToSubdomain/main.go b/cmd/locationToSubdomain/main.go
new file mode 100644
index 000000000..addb323c6
--- /dev/null
+++ b/cmd/locationToSubdomain/main.go
@@ -0,0 +1,111 @@
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "net/http"
+ "os"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/qdm12/golibs/network"
+)
+
+func main() {
+ os.Exit(_main())
+}
+
+func _main() int {
+ provider := flag.String("provider", "purevpn", "VPN provider to map location to subdomain, can be 'purevpn'")
+ flag.Parse()
+
+ client := network.NewClient(5 * time.Second)
+ switch *provider {
+ case "purevpn":
+ servers, warnings, err := purevpn(client)
+ if err != nil {
+ fmt.Println(err)
+ return 1
+ }
+ for _, server := range servers {
+ fmt.Printf(
+ "{subdomain: %q, region: %q, country: %q, city: %q},\n",
+ server.subdomain, server.region, server.country, server.city,
+ )
+ }
+ fmt.Print("\n\n")
+ for _, warning := range warnings {
+ fmt.Println(warning)
+ }
+ default:
+ fmt.Printf("Provider %q is not supported\n", *provider)
+ return 1
+ }
+ return 0
+}
+
+type purevpnServer struct {
+ region string
+ country string
+ city string
+ subdomain string // without -tcp or -udp suffix
+}
+
+func purevpn(client network.Client) (servers []purevpnServer, warnings []string, err error) {
+ content, status, err := client.GetContent("https://support.purevpn.com/vpn-servers")
+ if err != nil {
+ return nil, nil, err
+ } else if status != http.StatusOK {
+ return nil, nil, fmt.Errorf("HTTP status %d from Purevpn", status)
+ }
+ const jsonPrefix = ""
+ s := string(content)
+ jsonPrefixIndex := strings.Index(s, jsonPrefix)
+ if jsonPrefixIndex == -1 {
+ return nil, nil, fmt.Errorf("cannot find prefix %s in html", jsonPrefix)
+ }
+ if len(s[jsonPrefixIndex:]) == len(jsonPrefix) {
+ return nil, nil, fmt.Errorf("no body after json prefix %s", jsonPrefix)
+ }
+ s = s[jsonPrefixIndex+len(jsonPrefix):]
+ endIndex := strings.Index(s, jsonSuffix)
+ s = s[:endIndex]
+ var data []struct {
+ Region string `json:"region_name"`
+ Country string `json:"country_name"`
+ City string `json:"city_name"`
+ TCP string `json:"tcp"`
+ UDP string `json:"udp"`
+ }
+ if err := json.Unmarshal([]byte(s), &data); err != nil {
+ return nil, nil, err
+ }
+ sort.Slice(data, func(i, j int) bool {
+ if data[i].Region == data[j].Region {
+ if data[i].Country == data[j].Country {
+ return data[i].City < data[j].City
+ }
+ return data[i].Country < data[j].Country
+ }
+ return data[i].Region < data[j].Region
+ })
+ for i := range data {
+ if data[i].UDP == "" && data[i].TCP == "" {
+ warnings = append(warnings, fmt.Sprintf("server %s %s %s does not support TCP and UDP for openvpn", data[i].Region, data[i].Country, data[i].City))
+ continue
+ }
+ if data[i].UDP == "" || data[i].TCP == "" {
+ warnings = append(warnings, fmt.Sprintf("server %s %s %s does not support TCP or udp for openvpn", data[i].Region, data[i].Country, data[i].City))
+ }
+ servers = append(servers, purevpnServer{
+ region: data[i].Region,
+ country: data[i].Country,
+ city: data[i].City,
+ subdomain: strings.TrimSuffix(data[i].TCP, "-tcp.pointtoserver.com"),
+ })
+ }
+ return servers, warnings, nil
+}
diff --git a/cmd/resolver/main.go b/cmd/resolver/main.go
index 2c3e9d9ee..6d9f30a04 100644
--- a/cmd/resolver/main.go
+++ b/cmd/resolver/main.go
@@ -18,7 +18,7 @@ func main() {
func _main(ctx context.Context) int {
resolverAddress := flag.String("resolver", "1.1.1.1", "DNS Resolver IP address to use")
- provider := flag.String("provider", "pia", "VPN provider to resolve for, 'pia', 'windscribe', 'cyberghost' or 'vyprvpn'")
+ provider := flag.String("provider", "pia", "VPN provider to resolve for, 'pia', 'windscribe', 'cyberghost', 'vyprvpn' or 'purevpn'")
region := flag.String("region", "all", "Comma separated list of VPN provider region names to resolve for, use 'all' to resolve all")
flag.Parse()
@@ -43,6 +43,9 @@ func _main(ctx context.Context) int {
case "vyprvpn":
domain = "vyprvpn.com"
servers = vyprvpnServers()
+ case "purevpn":
+ domain = "pointtoserver.com"
+ servers = purevpnServers()
default:
fmt.Printf("Provider %q is not supported\n", *provider)
return 1
@@ -135,6 +138,11 @@ func formatLine(provider string, s server, ips []net.IP) string {
"{Region: %q, IPs: []net.IP{%s}},",
s.region, ipString,
)
+ case "purevpn":
+ return fmt.Sprintf(
+ "{Region: %q, Country: %q, City: %q, IPs: []net.IP{%s}},",
+ s.region, s.country, s.city, ipString,
+ )
}
return ""
}
@@ -197,6 +205,8 @@ type server struct {
subdomain string
region string
group string // only for cyberghost
+ country string // only for purevpn
+ city string // only for purevpn
}
func piaServers() []server {
@@ -747,3 +757,164 @@ func vyprvpnServers() []server {
{subdomain: "vn1", region: "Vietnam"},
}
}
+
+func purevpnServers() []server {
+ servers := []server{
+ {subdomain: "vlus-dz1-ovpn", region: "Africa", country: "Algeria", city: "Algiers"},
+ {subdomain: "vlus-ao1-ovpn", region: "Africa", country: "Angola", city: "Benguela"},
+ {subdomain: "vleu-cv-ovpn", region: "Africa", country: "Cape Verde", city: "Praia"},
+ {subdomain: "vlus-eg1-ovpn", region: "Africa", country: "Egypt", city: "Cairo"},
+ {subdomain: "et1-ovpn", region: "Africa", country: "Ethiopia", city: "Addis Ababa"},
+ {subdomain: "gh1-ovpn", region: "Africa", country: "Ghana", city: "Accra"},
+ {subdomain: "ke1-ovpn", region: "Africa", country: "Kenya", city: "Mombasa"},
+ {subdomain: "vlus-mg1-ovpn", region: "Africa", country: "Madagascar", city: "Antananarivo"},
+ {subdomain: "vlus-mr1-ovpn", region: "Africa", country: "Mauritania", city: "Nouakchott"},
+ {subdomain: "mu1-ovpn", region: "Africa", country: "Mauritius", city: "Port Louis"},
+ {subdomain: "ma1-ovpn", region: "Africa", country: "Morocco", city: "Rabat"},
+ {subdomain: "vlus-ne1-ovpn", region: "Africa", country: "Niger", city: "Niamey"},
+ {subdomain: "ng1-ovpn", region: "Africa", country: "Nigeria", city: "Suleja"},
+ {subdomain: "vlus-sn1-ovpn", region: "Africa", country: "Senegal", city: "Dakar"},
+ {subdomain: "sc1-ovpn", region: "Africa", country: "Seychelles", city: "Victoria"},
+ {subdomain: "za2-ovpn", region: "Africa", country: "South Africa", city: "Johannesburg"},
+ {subdomain: "vlus-tz1-ovpn", region: "Africa", country: "Tanzania", city: "Dar Es Salaam"},
+ {subdomain: "vlus-tn1-ovpn", region: "Africa", country: "Tunisia", city: "Tunis"},
+ {subdomain: "vlus-af1-ovpn", region: "Asia", country: "Afghanistan", city: "Kabul"},
+ {subdomain: "sg2-ovpn", region: "Asia", country: "Armenia", city: "Singapore"},
+ {subdomain: "az1-ovpn", region: "Asia", country: "Azerbaijan", city: "Baku"},
+ {subdomain: "vlus-bd1-ovpn", region: "Asia", country: "Bangladesh", city: "Dhaka"},
+ {subdomain: "bn2-ovpn", region: "Asia", country: "Brunei Darussalam", city: "Bandar Seri Begawan"},
+ {subdomain: "kh1-ovpn", region: "Asia", country: "Cambodia", city: "Phnom Penh"},
+ {subdomain: "hk2-ovpn", region: "Asia", country: "Hong Kong (SAR)", city: "Hong Kong"},
+ {subdomain: "in2-ovpn", region: "Asia", country: "India", city: "Chennai"},
+ {subdomain: "idn1-ovpn", region: "Asia", country: "Indonesia", city: "Jakarta"},
+ {subdomain: "jp-tk1-ovpn", region: "Asia", country: "Japan", city: "Tokyo"},
+ {subdomain: "vlus-kz1-ovpn", region: "Asia", country: "Kazakhstan", city: "Almaty"},
+ {subdomain: "kr2-ovpn", region: "Asia", country: "Korea, South", city: "Seoul"},
+ {subdomain: "vlus-kg1-ovpn", region: "Asia", country: "Kyrgyzstan", city: "Bishkek"},
+ {subdomain: "vlus-la1-ovpn", region: "Asia", country: "Laos", city: "Vientiane"},
+ {subdomain: "mo1-ovpn", region: "Asia", country: "Macao", city: "Beyrouth"},
+ {subdomain: "my2-ovpn", region: "Asia", country: "Malaysia", city: "Johor Baharu"},
+ {subdomain: "my-kl2-ovpn", region: "Asia", country: "Malaysia", city: "Kuala Lumpur"},
+ {subdomain: "vlus-mn1-ovpn", region: "Asia", country: "Mongolia", city: "Ulaanbaatar"},
+ {subdomain: "pk1-ovpn", region: "Asia", country: "Pakistan", city: "Islamabad"},
+ {subdomain: "vlus-pg1-ovpn", region: "Asia", country: "Papua New Guinea", city: "Port Moresby"},
+ {subdomain: "vlap-ph2-ovpn", region: "Asia", country: "Philippines", city: "Manila"},
+ {subdomain: "vlus-lk1-ovpn", region: "Asia", country: "Sri Lanka", city: "Colombo"},
+ {subdomain: "tw2-ovpn", region: "Asia", country: "Taiwan", city: "Taipei"},
+ {subdomain: "vlus-tj-ovpn", region: "Asia", country: "Tajikistan", city: "Dushanbe"},
+ {subdomain: "vlap-th2-ovpn", region: "Asia", country: "Thailand", city: "Bangkok"},
+ {subdomain: "tr2-ovpn", region: "Asia", country: "Turkey", city: "Istanbul"},
+ {subdomain: "vlus-tm1-ovpn", region: "Asia", country: "Turkmenistan", city: "Ashgabat"},
+ {subdomain: "vlus-uz-ovpn", region: "Asia", country: "Uzbekistan", city: "Tashkent"},
+ {subdomain: "vlap-vn2-ovpn", region: "Asia", country: "Vietnam", city: "Hanoi"},
+ {subdomain: "al1-ovpn", region: "Europe", country: "Albania", city: "Tirane"},
+ {subdomain: "vleu-am1-ovpn", region: "Europe", country: "Armenia", city: "Yerevan"},
+ {subdomain: "at2-ovpn", region: "Europe", country: "Austria", city: "Vienna"},
+ {subdomain: "vleu-be2-ovpn", region: "Europe", country: "Belgium", city: "Brussels"},
+ {subdomain: "ba1-ovpn", region: "Europe", country: "Bosnia and Herzegovina", city: "Sarajevo"},
+ {subdomain: "bg2-ovpn", region: "Europe", country: "Bulgaria", city: "Sofia"},
+ {subdomain: "vlus-hr1-ovpn", region: "Europe", country: "Croatia", city: "Zagreb"},
+ {subdomain: "cy1-ovpn", region: "Europe", country: "Cyprus", city: "Nicosia"},
+ {subdomain: "dk2-ovpn", region: "Europe", country: "Denmark", city: "Copenhagen"},
+ {subdomain: "ee1-ovpn", region: "Europe", country: "Estonia", city: "Tallinn"},
+ {subdomain: "fr2-ovpn", region: "Europe", country: "France", city: "Paris"},
+ {subdomain: "vlus-ge1-ovpn", region: "Europe", country: "Georgia", city: "Tbilisi"},
+ {subdomain: "de2-ovpn", region: "Europe", country: "Germany", city: "Frankfurt"},
+ {subdomain: "de2-ovpn", region: "Europe", country: "Germany", city: "Munich"},
+ {subdomain: "de-ao1-ovpn", region: "Europe", country: "Germany", city: "Nuremberg"},
+ {subdomain: "gr2-ovpn", region: "Europe", country: "Greece", city: "Thessaloniki"},
+ {subdomain: "hu2-ovpn", region: "Europe", country: "Hungary", city: "Budapest"},
+ {subdomain: "is1-ovpn", region: "Europe", country: "Iceland", city: "Reykjavik"},
+ {subdomain: "ie2-ovpn", region: "Europe", country: "Ireland", city: "Dublin"},
+ {subdomain: "im1-ovpn", region: "Europe", country: "Isle of Man", city: "Onchan"},
+ {subdomain: "vlus-it1-ovpn", region: "Europe", country: "Italy", city: "Milano"},
+ {subdomain: "lv1-ovpn", region: "Europe", country: "Latvia", city: "RIGA"},
+ {subdomain: "li1-ovpn", region: "Europe", country: "Liechtenstein", city: "Vaduz"},
+ {subdomain: "lt1-ovpn", region: "Europe", country: "Lithuania", city: "Vilnius"},
+ {subdomain: "lu2-ovpn", region: "Europe", country: "Luxembourg", city: "Luxembourg"},
+ {subdomain: "mt1-ovpn", region: "Europe", country: "Malta", city: "Sliema"},
+ {subdomain: "mn1-ovpn", region: "Europe", country: "Monaco", city: "Monaco"},
+ {subdomain: "vleu-me1-ovpn", region: "Europe", country: "Montenegro", city: "Podgorica"},
+ {subdomain: "nl2-ovpn", region: "Europe", country: "Netherlands", city: "Amsterdam"},
+ {subdomain: "vleu-no2-ovpn", region: "Europe", country: "Norway", city: "Oslo"},
+ {subdomain: "pl2-ovpn", region: "Europe", country: "Poland", city: "Warsaw"},
+ {subdomain: "pt2-ovpn", region: "Europe", country: "Portugal", city: "Lisbon"},
+ {subdomain: "ro2-ovpn", region: "Europe", country: "Romania", city: "Bucharest"},
+ {subdomain: "rs2-ovpn", region: "Europe", country: "Serbia", city: "Niลก"},
+ {subdomain: "sk1-ovpn", region: "Europe", country: "Slovakia", city: "Bratislava"},
+ {subdomain: "si1-ovpn", region: "Europe", country: "Slovenia", city: "Ljubljana"},
+ {subdomain: "es-ovpn", region: "Europe", country: "Spain", city: "Barcelona"},
+ {subdomain: "vlus-se1-ovpn", region: "Europe", country: "Sweden", city: "Stockholm"},
+ {subdomain: "ch2-ovpn", region: "Europe", country: "Switzerland", city: "Zurich"},
+ {subdomain: "ukg2-ovpn", region: "Europe", country: "United Kingdom", city: "Gosport"},
+ {subdomain: "ukl2-ovpn", region: "Europe", country: "United Kingdom", city: "London"},
+ {subdomain: "ukm2-ovpn", region: "Europe", country: "United Kingdom", city: "Maidenhead"},
+ {subdomain: "vlus-uk-man1-ovpn", region: "Europe", country: "United Kingdom", city: "Manchester"},
+ {subdomain: "bh-ovpn", region: "Middle East", country: "Bahrain", city: "Manama"},
+ {subdomain: "vlus-jo1-ovpn", region: "Middle East", country: "Jordan", city: "Amman"},
+ {subdomain: "vlus-kw1-ovpn", region: "Middle East", country: "Kuwait", city: "Kuwait"},
+ {subdomain: "om1-ovpn", region: "Middle East", country: "Oman", city: "Salalah"},
+ {subdomain: "qa1-ovpn", region: "Middle East", country: "Qatar", city: "Doha"},
+ {subdomain: "sa1-ovpn", region: "Middle East", country: "Saudi Arabia", city: "Jeddah"},
+ {subdomain: "ae2-ovpn", region: "Middle East", country: "United Arab Emirates", city: "Dubai"},
+ {subdomain: "aw1-ovpn", region: "North America", country: "Aruba", city: "Oranjestad"},
+ {subdomain: "vleu-bb-ovpn", region: "North America", country: "Barbados", city: "Bridgetown"},
+ {subdomain: "bz1-ovpn", region: "North America", country: "Belize", city: "Belmopan"},
+ {subdomain: "vleu-bm-ovpn", region: "North America", country: "Bermuda", city: "Hamilton"},
+ {subdomain: "caq1-ovpn", region: "North America", country: "Canada", city: "Montreal"},
+ {subdomain: "cato-ovpn", region: "North America", country: "Canada", city: "Toronto"},
+ {subdomain: "cav2-ovpn", region: "North America", country: "Canada", city: "Vancouver"},
+ {subdomain: "vleu-ky-ovpn", region: "North America", country: "Cayman Islands", city: "George Town"},
+ {subdomain: "vlus-cr1-ovpn", region: "North America", country: "Costa Rica", city: "San Jose"},
+ {subdomain: "vleu-dm-ovpn", region: "North America", country: "Dominica", city: "Roseau"},
+ {subdomain: "vleu-do-ovpn", region: "North America", country: "Dominican Republic", city: "Santo Domingo"},
+ {subdomain: "vleu-sv-ovpn", region: "North America", country: "El Salvador", city: "San Salvador"},
+ {subdomain: "vleu-gd-ovpn", region: "North America", country: "Grenada", city: "St George's"},
+ {subdomain: "vleu-gt-ovpn", region: "North America", country: "Guatemala", city: "Guatemala"},
+ {subdomain: "vleu-ht1-ovpn", region: "North America", country: "Haiti", city: "PORT-AU-PRINCE"},
+ {subdomain: "vleu-hn-ovpn", region: "North America", country: "Honduras", city: "TEGUCIGALPA"},
+ {subdomain: "jm1-ovpn", region: "North America", country: "Jamaica", city: "Kingston"},
+ {subdomain: "vlus-mx2-ovpn", region: "North America", country: "Mexico", city: "Mexico City"},
+ {subdomain: "vleu-ms-ovpn", region: "North America", country: "Montserrat", city: "plymouth"},
+ {subdomain: "pr1-ovpn", region: "North America", country: "Puerto Rico", city: "San Juan"},
+ {subdomain: "vleu-lc-ovpn", region: "North America", country: "Saint Lucia", city: "Castries"},
+ {subdomain: "bs1-ovpn", region: "North America", country: "The Bahamas", city: "Freeport"},
+ {subdomain: "vleu-tt-ovpn", region: "North America", country: "Trinidad and Tobago", city: "Port of Spain"},
+ {subdomain: "vleu-tc-ovpn", region: "North America", country: "Turks and Caicos Islands", city: "Balfour Town"},
+ {subdomain: "usva-ovpn", region: "North America", country: "United States", city: "Ashburn"},
+ {subdomain: "usil2-ovpn", region: "North America", country: "United States", city: "Chicago"},
+ {subdomain: "usoh1-ovpn", region: "North America", country: "United States", city: "Columbus"},
+ {subdomain: "usga2-ovpn", region: "North America", country: "United States", city: "Georgia"},
+ {subdomain: "ustx2-ovpn", region: "North America", country: "United States", city: "Houston"},
+ {subdomain: "usla2-ovpn", region: "North America", country: "United States", city: "Los Angeles"},
+ {subdomain: "usfl2-ovpn", region: "North America", country: "United States", city: "Miami"},
+ {subdomain: "usnj2-ovpn", region: "North America", country: "United States", city: "New Jersey"},
+ {subdomain: "usny2-ovpn", region: "North America", country: "United States", city: "New York"},
+ {subdomain: "usphx2-ovpn", region: "North America", country: "United States", city: "Phoenix"},
+ {subdomain: "usut2-ovpn", region: "North America", country: "United States", city: "Salt Lake City"},
+ {subdomain: "ussf2-ovpn", region: "North America", country: "United States", city: "San Francisco"},
+ {subdomain: "ussa-ovpn", region: "North America", country: "United States", city: "Seattle"},
+ {subdomain: "uswdc2-ovpn", region: "North America", country: "United States", city: "Washington, D.C."},
+ {subdomain: "au-bn-ovpn", region: "Oceania", country: "Australia", city: "Brisbane"},
+ {subdomain: "au-me1-ovpn", region: "Oceania", country: "Australia", city: "Melbourne"},
+ {subdomain: "au2-pe-ovpn", region: "Oceania", country: "Australia", city: "Perth"},
+ {subdomain: "au-sd2-ovpn", region: "Oceania", country: "Australia", city: "Sydney"},
+ {subdomain: "nz2-ovpn", region: "Oceania", country: "New Zealand", city: "Auckland"},
+ {subdomain: "vlus-ar1-ovpn", region: "South America", country: "Argentina", city: "Buenos Aires"},
+ {subdomain: "vleu-bo-ovpn", region: "South America", country: "Bolivia", city: "Sucre"},
+ {subdomain: "br2-ovpn", region: "South America", country: "Brazil", city: "Sao Paulo"},
+ {subdomain: "vg1-ovpn", region: "South America", country: "British Virgin Island", city: "Road Town"},
+ {subdomain: "vlbr-cl-ovpn", region: "South America", country: "Chile", city: "Santiago"},
+ {subdomain: "co1-ovpn", region: "South America", country: "Colombia", city: "Bogota"},
+ {subdomain: "ec1-ovpn", region: "South America", country: "Ecuador", city: "Quito"},
+ {subdomain: "vleu-gy-ovpn", region: "South America", country: "Guyana", city: "Georgetown"},
+ {subdomain: "pa2-ovpn", region: "South America", country: "Panama", city: "Panama City"},
+ {subdomain: "vleu-py-ovpn", region: "South America", country: "Paraguay", city: "Asuncion"},
+ {subdomain: "pe1-ovpn", region: "South America", country: "Peru", city: "Lima"},
+ {subdomain: "vleu-sr-ovpn", region: "South America", country: "Suriname", city: "Paramaribo"},
+ }
+ for i := range servers {
+ servers[i].subdomain += "-udp"
+ }
+ return servers
+}
diff --git a/internal/constants/purevpn.go b/internal/constants/purevpn.go
new file mode 100644
index 000000000..d64bb55ed
--- /dev/null
+++ b/internal/constants/purevpn.go
@@ -0,0 +1,198 @@
+package constants
+
+import (
+ "net"
+
+ "github.com/qdm12/private-internet-access-docker/internal/models"
+)
+
+const (
+ PurevpnCertificateAuthority = "MIIE6DCCA9CgAwIBAgIJAMjXFoeo5uSlMA0GCSqGSIb3DQEBCwUAMIGoMQswCQYDVQQGEwJISzEQMA4GA1UECBMHQ2VudHJhbDELMAkGA1UEBxMCSEsxGDAWBgNVBAoTD1NlY3VyZS1TZXJ2ZXJDQTELMAkGA1UECxMCSVQxGDAWBgNVBAMTD1NlY3VyZS1TZXJ2ZXJDQTEYMBYGA1UEKRMPU2VjdXJlLVNlcnZlckNBMR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3QuZG9tYWluMB4XDTE2MDExNTE1MzQwOVoXDTI2MDExMjE1MzQwOVowgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDluufhyLlyvXzPUL16kAWAdivl1roQv3QHbuRshyKacf/1Er1JqEbtW3Mx9Fvr/u27qU2W8lQI6DaJhU2BfijPe/KHkib55mvHzIVvoexxya26nk79F2c+d9PnuuMdThWQO3El5a/i2AASnM7T7piIBT2WRZW2i8RbfJaTT7G7LP7OpMKIV1qyBg/cWoO7cIWQW4jmzqrNryIkF0AzStLN1DxvnQZwgXBGv0CwuAkfQuNSLu0PQgPp0PhdukNZFllv5D29IhPr0Z+kwPtrAgPQo+lHlOBHBMUpDT4XChTPeAvMaUSBsqmonAE8UUHEabWrqYN/kWNHCNkYXMkiVmK1AgMBAAGjggERMIIBDTAdBgNVHQ4EFgQU456ijsFrYnzHBShLAPpOUqQ+Z2cwgd0GA1UdIwSB1TCB0oAU456ijsFrYnzHBShLAPpOUqQ+Z2ehga6kgaswgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQDI1xaHqObkpTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCvga2HMwOtUxWH/inL2qk24KX2pxLg939JNhqoyNrUpbDHag5xPQYXUmUpKrNJZ0z+o/ZnNUPHydTSXE7Z7E45J0GDN5E7g4pakndKnDLSjp03NgGsCGW+cXnz6UBPM5FStFvGdDeModeSUyoS9fjk+mYROvmiy5EiVDP91sKGcPLR7Ym0M7zl2aaqV7bb98HmMoBOxpeZQinof67nKrCsgz/xjktWFgcmPl4/PQSsmqQD0fTtWxGuRX+FzwvF2OCMCAJgp1RqJNlk2g50/kBIoJVPPCfjDFeDU5zGaWGSQ9+z1L6/z7VXdjUiHL0ouOcHwbiS4ZjTr9nMn6WdAHU2"
+ PurevpnCertificate = "MIIEnzCCA4egAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBqDELMAkGA1UEBhMCSEsxEDAOBgNVBAgTB0NlbnRyYWwxCzAJBgNVBAcTAkhLMRgwFgYDVQQKEw9TZWN1cmUtU2VydmVyQ0ExCzAJBgNVBAsTAklUMRgwFgYDVQQDEw9TZWN1cmUtU2VydmVyQ0ExGDAWBgNVBCkTD1NlY3VyZS1TZXJ2ZXJDQTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRvbWFpbjAeFw0xNjAxMTUxNjE1MzhaFw0yNjAxMTIxNjE1MzhaMIGdMQswCQYDVQQGEwJISzEQMA4GA1UECBMHQ2VudHJhbDELMAkGA1UEBxMCSEsxFjAUBgNVBAoTDVNlY3VyZS1DbGllbnQxCzAJBgNVBAsTAklUMRYwFAYDVQQDEw1TZWN1cmUtQ2xpZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxsnyn4v6xxDPnuDaYS0b9M1N8nxgg7OBPBlK+FWRxdTQ8yxt5U5CZGm7riVp7fya2J2iPZIgmHQEv/KbxztsHAVlYSfYYlalrnhEL3bDP2tY+N43AwB1k5BrPq2s1pPLT2XG951drDKG4PUuFHUP1sHzW5oQlfVCmxgIMAP8OYkCAwEAAaOCAV8wggFbMAkGA1UdEwQCMAAwLQYJYIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU9MwUnUDbQKKZKjoeieD2OD5NlAEwgd0GA1UdIwSB1TCB0oAU456ijsFrYnzHBShLAPpOUqQ+Z2ehga6kgaswgagxCzAJBgNVBAYTAkhLMRAwDgYDVQQIEwdDZW50cmFsMQswCQYDVQQHEwJISzEYMBYGA1UEChMPU2VjdXJlLVNlcnZlckNBMQswCQYDVQQLEwJJVDEYMBYGA1UEAxMPU2VjdXJlLVNlcnZlckNBMRgwFgYDVQQpEw9TZWN1cmUtU2VydmVyQ0ExHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQDI1xaHqObkpTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBAFyFo2VUX/UFixsdPdK9/Yt6mkCWc+XS1xbapGXXb9U1d+h1iBCIV9odUHgNCXWpz1hR5Uu/OCzaZ0asLE4IFMZlQmJs8sMT0c1tfPPGW45vxbL0lhqnQ8PNcBH7huNK7VFjUh4szXRKmaQPaM4S91R3L4CaNfVeHfAg7mN2m9Zn5Gto1Q1/CFMGKu2hxwGEw5p+X1czBWEvg/O09ckx/ggkkI1NcZsNiYQ+6Pz8DdGGX3+05YwLZu94+O6iIMrzxl/il0eK83g3YPbsOrASARvw6w/8sOnJCK5eOacl21oww875KisnYdWjHB1FiI+VzQ1/gyoDsL5kPTJVuu2CoG8="
+ PurevpnKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbJ8p+L+scQz57g2mEtG/TNTfJ8YIOzgTwZSvhVkcXU0PMsbeVOQmRpu64lae38mtidoj2SIJh0BL/ym8c7bBwFZWEn2GJWpa54RC92wz9rWPjeNwMAdZOQaz6trNaTy09lxvedXawyhuD1LhR1D9bB81uaEJX1QpsYCDAD/DmJAgMBAAECgYEAvTHbDupE5U0krUvHzBEIuHblptGlcfNYHoDcD3oxYR3pOGeiuElBexv+mgHVzcFLBrsQfJUlHLPfCWi3xmjRvDQcr7N7U1u7NIzazy/PpRBaKolMRiM1KMYi2DG0i4ZONwFT8bvNHOIrZzCLY54KDrqOn55OzC70WYjWh4t5evkCQQDkkzZUAeskBC9+JP/zLps8jhwfoLBWGw/zbC9ePDmX0N8MTZdcUpg6KUTf1wbkLUyVtIRjS2ao6qu1jWG6K0x3AkEA3qPWyaWQWCynhNDqu2U1cPb2kh5AJip+gqxO3emikAdajsSxeoyEC2AfyBITbeB1tvCUZH17J4i/0+OFTEQp/wJAb/zEOGJ8PzghwK8GC7JA8mk51DEZVAaMSRovFv9wxDXcoh191AjPdmdzzCuAv9iF1i8MUc3GbWoUWK39PIYsPwJAWh63sqfx5b8tj/WBDpnJKBDPfhYAoXJSA1L8GZeY1fQkE+ZKcPCwAmrGcpXeh3t0Krj3WDXyw+32uC5Apr5wwQJAPZwOOReaC4YNfBPZN9BdHvVjOYGGUffpI+X+hWpLRnQFJteAi+eqwyk0Oi0SkJB+a7jcerK2d7q7xhec5WHlng=="
+ PurevpnOpenvpnStaticKeyV1 = "e30af995f56d07426d9ba1f824730521d4283db4b4d0cdda9c6e8759a3799dcb7939b6a5989160c9660de0f6125cbb1f585b41c074b2fe88ecfcf17eab9a33be1352379cdf74952b588fb161a93e13df9135b2b29038231e02d657a6225705e6868ccb0c384ed11614690a1894bfbeb274cebf1fe9c2329bdd5c8a40fe8820624d2ea7540cd79ab76892db51fc371a3ac5fc9573afecb3fffe3281e61d72e91579d9b03d8cbf7909b3aebf4d90850321ee6b7d0a7846d15c27d8290e031e951e19438a4654663cad975e138f5bc5af89c737ad822f27e19057731f41e1e254cc9c95b7175c622422cde9f1f2cfd3510add94498b4d7133d3729dd214a16b27fb"
+)
+
+func PurevpnRegionChoices() (choices []string) {
+ servers := PurevpnServers()
+ choices = make([]string, len(servers))
+ for i := range servers {
+ choices[i] = servers[i].Region
+ }
+ return choices
+}
+
+func PurevpnCountryChoices() (choices []string) {
+ servers := PurevpnServers()
+ choices = make([]string, len(servers))
+ for i := range servers {
+ choices[i] = servers[i].Country
+ }
+ return choices
+}
+
+func PurevpnCityChoices() (choices []string) {
+ servers := PurevpnServers()
+ choices = make([]string, len(servers))
+ for i := range servers {
+ choices[i] = servers[i].City
+ }
+ return choices
+}
+
+func PurevpnServers() []models.PurevpnServer {
+ return []models.PurevpnServer{
+ {Region: "Africa", Country: "Algeria", City: "Algiers", IPs: []net.IP{{172, 94, 64, 2}}},
+ {Region: "Africa", Country: "Angola", City: "Benguela", IPs: []net.IP{{45, 115, 26, 2}}},
+ {Region: "Africa", Country: "Cape Verde", City: "Praia", IPs: []net.IP{{45, 74, 25, 2}}},
+ {Region: "Africa", Country: "Egypt", City: "Cairo", IPs: []net.IP{{192, 198, 120, 122}}},
+ {Region: "Africa", Country: "Ethiopia", City: "Addis Ababa", IPs: []net.IP{{104, 250, 178, 4}}},
+ {Region: "Africa", Country: "Ghana", City: "Accra", IPs: []net.IP{{196, 251, 67, 4}}},
+ {Region: "Africa", Country: "Kenya", City: "Mombasa", IPs: []net.IP{{154, 127, 57, 151}}},
+ {Region: "Africa", Country: "Madagascar", City: "Antananarivo", IPs: []net.IP{{206, 123, 156, 131}}},
+ {Region: "Africa", Country: "Mauritania", City: "Nouakchott", IPs: []net.IP{{206, 123, 158, 63}}},
+ {Region: "Africa", Country: "Mauritius", City: "Port Louis", IPs: []net.IP{{104, 250, 181, 4}}},
+ {Region: "Africa", Country: "Morocco", City: "Rabat", IPs: []net.IP{{104, 243, 250, 126}}},
+ {Region: "Africa", Country: "Niger", City: "Niamey", IPs: []net.IP{{206, 123, 157, 131}}},
+ {Region: "Africa", Country: "Nigeria", City: "Suleja", IPs: []net.IP{{102, 165, 25, 38}}},
+ {Region: "Africa", Country: "Senegal", City: "Dakar", IPs: []net.IP{{206, 123, 158, 131}}},
+ {Region: "Africa", Country: "Seychelles", City: "Victoria", IPs: []net.IP{{172, 111, 128, 126}}},
+ {Region: "Africa", Country: "South Africa", City: "Johannesburg", IPs: []net.IP{{102, 165, 3, 34}}},
+ {Region: "Africa", Country: "Tanzania", City: "Dar Es Salaam", IPs: []net.IP{{102, 135, 0, 2}}},
+ {Region: "Africa", Country: "Tunisia", City: "Tunis", IPs: []net.IP{{206, 123, 159, 4}}},
+ {Region: "Asia", Country: "Afghanistan", City: "Kabul", IPs: []net.IP{{172, 111, 208, 2}}},
+ {Region: "Asia", Country: "Armenia", City: "Singapore", IPs: []net.IP{{37, 120, 208, 147}}},
+ {Region: "Asia", Country: "Azerbaijan", City: "Baku", IPs: []net.IP{{104, 250, 177, 4}}},
+ {Region: "Asia", Country: "Bangladesh", City: "Dhaka", IPs: []net.IP{{206, 123, 154, 190}}},
+ {Region: "Asia", Country: "Brunei Darussalam", City: "Bandar Seri Begawan", IPs: []net.IP{{119, 81, 75, 84}, {119, 81, 242, 8}}},
+ {Region: "Asia", Country: "Cambodia", City: "Phnom Penh", IPs: []net.IP{{104, 250, 176, 122}}},
+ {Region: "Asia", Country: "Hong Kong (SAR)", City: "Hong Kong", IPs: []net.IP{{46, 243, 250, 4}}},
+ {Region: "Asia", Country: "India", City: "Chennai", IPs: []net.IP{{129, 227, 107, 242}}},
+ {Region: "Asia", Country: "Indonesia", City: "Jakarta", IPs: []net.IP{{103, 55, 9, 2}}},
+ {Region: "Asia", Country: "Japan", City: "Tokyo", IPs: []net.IP{{172, 94, 56, 2}}},
+ {Region: "Asia", Country: "Kazakhstan", City: "Almaty", IPs: []net.IP{{206, 123, 152, 4}}},
+ {Region: "Asia", Country: "Korea, South", City: "Seoul", IPs: []net.IP{{45, 115, 25, 2}}},
+ {Region: "Asia", Country: "Kyrgyzstan", City: "Bishkek", IPs: []net.IP{{206, 123, 151, 131}}},
+ {Region: "Asia", Country: "Laos", City: "Vientiane", IPs: []net.IP{{206, 123, 153, 4}}},
+ {Region: "Asia", Country: "Macao", City: "Beyrouth", IPs: []net.IP{{104, 243, 240, 121}}},
+ {Region: "Asia", Country: "Malaysia", City: "Johor Baharu", IPs: []net.IP{{43, 226, 230, 4}}},
+ {Region: "Asia", Country: "Malaysia", City: "Kuala Lumpur", IPs: []net.IP{{104, 250, 160, 4}}},
+ {Region: "Asia", Country: "Mongolia", City: "Ulaanbaatar", IPs: []net.IP{{206, 123, 153, 131}}},
+ {Region: "Asia", Country: "Pakistan", City: "Islamabad", IPs: []net.IP{{104, 250, 187, 3}}},
+ {Region: "Asia", Country: "Papua New Guinea", City: "Port Moresby", IPs: []net.IP{{206, 123, 155, 131}}},
+ {Region: "Asia", Country: "Philippines", City: "Manila", IPs: []net.IP{{36, 255, 97, 3}}},
+ {Region: "Asia", Country: "Sri Lanka", City: "Colombo", IPs: []net.IP{{206, 123, 154, 4}}},
+ {Region: "Asia", Country: "Taiwan", City: "Taipei", IPs: []net.IP{{203, 69, 105, 5}}},
+ {Region: "Asia", Country: "Tajikistan", City: "Dushanbe", IPs: []net.IP{{206, 123, 151, 4}}},
+ {Region: "Asia", Country: "Thailand", City: "Bangkok", IPs: []net.IP{{104, 37, 6, 4}}},
+ {Region: "Asia", Country: "Turkey", City: "Istanbul", IPs: []net.IP{{82, 102, 22, 212}}},
+ {Region: "Asia", Country: "Turkmenistan", City: "Ashgabat", IPs: []net.IP{{206, 123, 152, 131}}},
+ {Region: "Asia", Country: "Uzbekistan", City: "Tashkent", IPs: []net.IP{{206, 123, 150, 131}}},
+ {Region: "Asia", Country: "Vietnam", City: "Hanoi", IPs: []net.IP{{192, 253, 249, 132}}},
+ {Region: "Europe", Country: "Albania", City: "Tirane", IPs: []net.IP{{46, 243, 224, 2}}},
+ {Region: "Europe", Country: "Armenia", City: "Yerevan", IPs: []net.IP{{172, 94, 35, 4}}},
+ {Region: "Europe", Country: "Austria", City: "Vienna", IPs: []net.IP{{172, 94, 125, 4}}},
+ {Region: "Europe", Country: "Belgium", City: "Brussels", IPs: []net.IP{{172, 111, 223, 4}, {172, 111, 244, 4}}},
+ {Region: "Europe", Country: "Bosnia and Herzegovina", City: "Sarajevo", IPs: []net.IP{{104, 250, 169, 122}}},
+ {Region: "Europe", Country: "Bulgaria", City: "Sofia", IPs: []net.IP{{37, 120, 152, 52}}},
+ {Region: "Europe", Country: "Croatia", City: "Zagreb", IPs: []net.IP{{104, 250, 163, 2}}},
+ {Region: "Europe", Country: "Cyprus", City: "Nicosia", IPs: []net.IP{{188, 72, 119, 4}}},
+ {Region: "Europe", Country: "Denmark", City: "Copenhagen", IPs: []net.IP{{172, 111, 223, 4}}},
+ {Region: "Europe", Country: "Estonia", City: "Tallinn", IPs: []net.IP{{185, 166, 87, 2}, {188, 72, 111, 4}}},
+ {Region: "Europe", Country: "France", City: "Paris", IPs: []net.IP{{172, 111, 223, 4}}},
+ {Region: "Europe", Country: "Georgia", City: "Tbilisi", IPs: []net.IP{{141, 101, 156, 2}}},
+ {Region: "Europe", Country: "Germany", City: "Frankfurt", IPs: []net.IP{{82, 102, 16, 107}}},
+ {Region: "Europe", Country: "Germany", City: "Munich", IPs: []net.IP{{82, 102, 16, 107}}},
+ {Region: "Europe", Country: "Germany", City: "Nuremberg", IPs: []net.IP{{172, 94, 125, 2}}},
+ {Region: "Europe", Country: "Greece", City: "Thessaloniki", IPs: []net.IP{{5, 172, 199, 2}}},
+ {Region: "Europe", Country: "Hungary", City: "Budapest", IPs: []net.IP{{217, 138, 192, 136}}},
+ {Region: "Europe", Country: "Iceland", City: "Reykjavik", IPs: []net.IP{{192, 253, 250, 1}}},
+ {Region: "Europe", Country: "Ireland", City: "Dublin", IPs: []net.IP{{78, 153, 208, 173}}},
+ {Region: "Europe", Country: "Isle of Man", City: "Onchan", IPs: []net.IP{{46, 243, 144, 2}}},
+ {Region: "Europe", Country: "Italy", City: "Milano", IPs: []net.IP{{45, 9, 251, 2}}},
+ {Region: "Europe", Country: "Latvia", City: "RIGA", IPs: []net.IP{{185, 118, 76, 5}}},
+ {Region: "Europe", Country: "Liechtenstein", City: "Vaduz", IPs: []net.IP{{104, 250, 164, 4}}},
+ {Region: "Europe", Country: "Lithuania", City: "Vilnius", IPs: []net.IP{{188, 72, 116, 3}}},
+ {Region: "Europe", Country: "Luxembourg", City: "Luxembourg", IPs: []net.IP{{94, 242, 225, 132}}},
+ {Region: "Europe", Country: "Malta", City: "Sliema", IPs: []net.IP{{46, 243, 241, 4}}},
+ {Region: "Europe", Country: "Monaco", City: "Monaco", IPs: []net.IP{{104, 250, 168, 132}}},
+ {Region: "Europe", Country: "Montenegro", City: "Podgorica", IPs: []net.IP{{104, 250, 165, 121}}},
+ {Region: "Europe", Country: "Netherlands", City: "Amsterdam", IPs: []net.IP{{37, 120, 192, 212}}},
+ {Region: "Europe", Country: "Norway", City: "Oslo", IPs: []net.IP{{82, 102, 22, 212}}},
+ {Region: "Europe", Country: "Poland", City: "Warsaw", IPs: []net.IP{{5, 253, 206, 251}}},
+ {Region: "Europe", Country: "Portugal", City: "Lisbon", IPs: []net.IP{{5, 154, 174, 3}}},
+ {Region: "Europe", Country: "Romania", City: "Bucharest", IPs: []net.IP{{188, 240, 220, 35}}},
+ {Region: "Europe", Country: "Serbia", City: "Niลก", IPs: []net.IP{{152, 89, 160, 201}}},
+ {Region: "Europe", Country: "Slovakia", City: "Bratislava", IPs: []net.IP{{188, 72, 112, 3}}},
+ {Region: "Europe", Country: "Slovenia", City: "Ljubljana", IPs: []net.IP{{104, 243, 246, 129}}},
+ {Region: "Europe", Country: "Spain", City: "Barcelona", IPs: []net.IP{{185, 230, 124, 147}}},
+ {Region: "Europe", Country: "Sweden", City: "Stockholm", IPs: []net.IP{{45, 74, 46, 2}}},
+ {Region: "Europe", Country: "Switzerland", City: "Zurich", IPs: []net.IP{{45, 12, 222, 98}, {45, 12, 222, 99}}},
+ {Region: "Europe", Country: "United Kingdom", City: "Gosport", IPs: []net.IP{{45, 141, 154, 70}}},
+ {Region: "Europe", Country: "United Kingdom", City: "London", IPs: []net.IP{{193, 9, 113, 66}}},
+ {Region: "Europe", Country: "United Kingdom", City: "Maidenhead", IPs: []net.IP{{188, 72, 89, 4}}},
+ {Region: "Europe", Country: "United Kingdom", City: "Manchester", IPs: []net.IP{{172, 111, 183, 2}}},
+ {Region: "Middle East", Country: "Bahrain", City: "Manama", IPs: []net.IP{{46, 243, 150, 4}}},
+ {Region: "Middle East", Country: "Jordan", City: "Amman", IPs: []net.IP{{172, 111, 152, 3}}},
+ {Region: "Middle East", Country: "Kuwait", City: "Kuwait", IPs: []net.IP{{206, 123, 146, 4}}},
+ {Region: "Middle East", Country: "Oman", City: "Salalah", IPs: []net.IP{{46, 243, 148, 125}}},
+ {Region: "Middle East", Country: "Qatar", City: "Doha", IPs: []net.IP{{46, 243, 147, 2}}},
+ {Region: "Middle East", Country: "Saudi Arabia", City: "Jeddah", IPs: []net.IP{{45, 74, 1, 4}}},
+ {Region: "Middle East", Country: "United Arab Emirates", City: "Dubai", IPs: []net.IP{{104, 37, 6, 4}}},
+ {Region: "North America", Country: "Aruba", City: "Oranjestad", IPs: []net.IP{{104, 243, 246, 129}}},
+ {Region: "North America", Country: "Barbados", City: "Bridgetown", IPs: []net.IP{{172, 94, 97, 2}}},
+ {Region: "North America", Country: "Belize", City: "Belmopan", IPs: []net.IP{{104, 243, 241, 4}}},
+ {Region: "North America", Country: "Bermuda", City: "Hamilton", IPs: []net.IP{{172, 94, 76, 2}}},
+ {Region: "North America", Country: "Canada", City: "Montreal", IPs: []net.IP{{172, 94, 7, 2}}},
+ {Region: "North America", Country: "Canada", City: "Toronto", IPs: []net.IP{{172, 94, 7, 2}}},
+ {Region: "North America", Country: "Canada", City: "Vancouver", IPs: []net.IP{{172, 94, 34, 4}}},
+ {Region: "North America", Country: "Cayman Islands", City: "George Town", IPs: []net.IP{{172, 94, 113, 2}}},
+ {Region: "North America", Country: "Costa Rica", City: "San Jose", IPs: []net.IP{{104, 243, 245, 1}}},
+ {Region: "North America", Country: "Dominica", City: "Roseau", IPs: []net.IP{{45, 74, 22, 2}}},
+ {Region: "North America", Country: "Dominican Republic", City: "Santo Domingo", IPs: []net.IP{{45, 74, 23, 129}}},
+ {Region: "North America", Country: "El Salvador", City: "San Salvador", IPs: []net.IP{{45, 74, 17, 129}}},
+ {Region: "North America", Country: "Grenada", City: "St George's", IPs: []net.IP{{45, 74, 21, 129}}},
+ {Region: "North America", Country: "Guatemala", City: "Guatemala", IPs: []net.IP{{45, 74, 17, 129}}},
+ {Region: "North America", Country: "Haiti", City: "PORT-AU-PRINCE", IPs: []net.IP{{45, 74, 24, 2}}},
+ {Region: "North America", Country: "Honduras", City: "TEGUCIGALPA", IPs: []net.IP{{45, 74, 18, 2}}},
+ {Region: "North America", Country: "Jamaica", City: "Kingston", IPs: []net.IP{{104, 250, 182, 126}}},
+ {Region: "North America", Country: "Mexico", City: "Mexico City", IPs: []net.IP{{104, 243, 243, 131}}},
+ {Region: "North America", Country: "Montserrat", City: "plymouth", IPs: []net.IP{{45, 74, 26, 190}}},
+ {Region: "North America", Country: "Puerto Rico", City: "San Juan", IPs: []net.IP{{104, 37, 2, 2}}},
+ {Region: "North America", Country: "Saint Lucia", City: "Castries", IPs: []net.IP{{45, 74, 23, 2}}},
+ {Region: "North America", Country: "The Bahamas", City: "Freeport", IPs: []net.IP{{104, 243, 242, 2}}},
+ {Region: "North America", Country: "Trinidad and Tobago", City: "Port of Spain", IPs: []net.IP{{45, 74, 21, 2}}},
+ {Region: "North America", Country: "Turks and Caicos Islands", City: "Balfour Town", IPs: []net.IP{{172, 94, 60, 4}}},
+ {Region: "North America", Country: "United States", City: "Ashburn", IPs: []net.IP{{46, 243, 249, 2}}},
+ {Region: "North America", Country: "United States", City: "Chicago", IPs: []net.IP{{37, 230, 169, 4}}},
+ {Region: "North America", Country: "United States", City: "Columbus", IPs: []net.IP{{172, 94, 115, 2}}},
+ {Region: "North America", Country: "United States", City: "Georgia", IPs: []net.IP{{141, 101, 168, 4}}},
+ {Region: "North America", Country: "United States", City: "Houston", IPs: []net.IP{{172, 94, 1, 4}}},
+ {Region: "North America", Country: "United States", City: "Los Angeles", IPs: []net.IP{{172, 111, 147, 4}}},
+ {Region: "North America", Country: "United States", City: "Miami", IPs: []net.IP{{172, 94, 108, 4}}},
+ {Region: "North America", Country: "United States", City: "New Jersey", IPs: []net.IP{{141, 101, 168, 4}}},
+ {Region: "North America", Country: "United States", City: "New York", IPs: []net.IP{{172, 111, 147, 4}}},
+ {Region: "North America", Country: "United States", City: "Phoenix", IPs: []net.IP{{172, 94, 26, 4}}},
+ {Region: "North America", Country: "United States", City: "Salt Lake City", IPs: []net.IP{{141, 101, 168, 4}}},
+ {Region: "North America", Country: "United States", City: "San Francisco", IPs: []net.IP{{172, 111, 147, 4}}},
+ {Region: "North America", Country: "United States", City: "Seattle", IPs: []net.IP{{172, 94, 86, 2}}},
+ {Region: "North America", Country: "United States", City: "Washington, D.C.", IPs: []net.IP{{37, 230, 169, 4}}},
+ {Region: "Oceania", Country: "Australia", City: "Brisbane", IPs: []net.IP{{172, 111, 236, 2}}},
+ {Region: "Oceania", Country: "Australia", City: "Melbourne", IPs: []net.IP{{118, 127, 62, 2}}},
+ {Region: "Oceania", Country: "Australia", City: "Perth", IPs: []net.IP{{172, 94, 123, 4}}},
+ {Region: "Oceania", Country: "Australia", City: "Sydney", IPs: []net.IP{{46, 243, 245, 4}}},
+ {Region: "Oceania", Country: "New Zealand", City: "Auckland", IPs: []net.IP{{43, 228, 156, 4}}},
+ {Region: "South America", Country: "Argentina", City: "Buenos Aires", IPs: []net.IP{{104, 243, 244, 1}}},
+ {Region: "South America", Country: "Bolivia", City: "Sucre", IPs: []net.IP{{172, 94, 77, 2}}},
+ {Region: "South America", Country: "Brazil", City: "Sao Paulo", IPs: []net.IP{{104, 243, 244, 2}}},
+ {Region: "South America", Country: "British Virgin Island", City: "Road Town", IPs: []net.IP{{104, 250, 184, 130}}},
+ {Region: "South America", Country: "Chile", City: "Santiago", IPs: []net.IP{{191, 96, 183, 251}}},
+ {Region: "South America", Country: "Colombia", City: "Bogota", IPs: []net.IP{{172, 111, 132, 1}}},
+ {Region: "South America", Country: "Ecuador", City: "Quito", IPs: []net.IP{{104, 250, 180, 126}}},
+ {Region: "South America", Country: "Guyana", City: "Georgetown", IPs: []net.IP{{45, 74, 20, 129}}},
+ {Region: "South America", Country: "Panama", City: "Panama City", IPs: []net.IP{{104, 243, 243, 131}}},
+ {Region: "South America", Country: "Paraguay", City: "Asuncion", IPs: []net.IP{{45, 74, 19, 129}}},
+ {Region: "South America", Country: "Peru", City: "Lima", IPs: []net.IP{{172, 111, 131, 1}}},
+ {Region: "South America", Country: "Suriname", City: "Paramaribo", IPs: []net.IP{{45, 74, 20, 4}}},
+ }
+}
diff --git a/internal/constants/vpn.go b/internal/constants/vpn.go
index 4cb20ac81..e9a64c896 100644
--- a/internal/constants/vpn.go
+++ b/internal/constants/vpn.go
@@ -19,6 +19,8 @@ const (
Vyprvpn models.VPNProvider = "vyprvpn"
// NordVPN is a VPN provider
Nordvpn models.VPNProvider = "nordvpn"
+ // PureVPN is a VPN provider
+ Purevpn models.VPNProvider = "purevpn"
)
const (
diff --git a/internal/models/selection.go b/internal/models/selection.go
index 0fc54fdb8..b7732b65d 100644
--- a/internal/models/selection.go
+++ b/internal/models/selection.go
@@ -25,11 +25,13 @@ type ServerSelection struct { //nolint:maligned
// Cyberghost
Group string `json:"group"`
- // Mullvad
+ // Mullvad, PureVPN
Country string `json:"country"`
City string `json:"city"`
- ISP string `json:"isp"`
- Owned bool `json:"owned"`
+
+ // Mullvad
+ ISP string `json:"isp"`
+ Owned bool `json:"owned"`
// Mullvad, Windscribe
CustomPort uint16 `json:"customPort"`
@@ -110,6 +112,12 @@ func (p *ProviderSettings) String() string {
"Region: "+p.ServerSelection.Region,
"Number: "+number,
)
+ case "purevpn":
+ settingsList = append(settingsList,
+ "Region: "+p.ServerSelection.Region,
+ "Country: "+p.ServerSelection.Country,
+ "City: "+p.ServerSelection.City,
+ )
default:
settingsList = append(settingsList,
"",
diff --git a/internal/models/servers.go b/internal/models/servers.go
index 8b3848da9..02e6c1e03 100644
--- a/internal/models/servers.go
+++ b/internal/models/servers.go
@@ -44,3 +44,10 @@ type NordvpnServer struct { //nolint:maligned
TCP bool
UDP bool
}
+
+type PurevpnServer struct {
+ Region string
+ Country string
+ City string
+ IPs []net.IP
+}
diff --git a/internal/params/params.go b/internal/params/params.go
index 85c1df036..5a6d9b57c 100644
--- a/internal/params/params.go
+++ b/internal/params/params.go
@@ -86,6 +86,11 @@ type Reader interface {
GetNordvpnRegion() (region string, err error)
GetNordvpnNumber() (number uint16, err error)
+ // PureVPN getters
+ GetPurevpnRegion() (region string, err error)
+ GetPurevpnCountry() (country string, err error)
+ GetPurevpnCity() (city string, err error)
+
// Shadowsocks getters
GetShadowSocks() (activated bool, err error)
GetShadowSocksLog() (activated bool, err error)
@@ -131,7 +136,7 @@ func NewReader(logger logging.Logger, fileManager files.FileManager) Reader {
// GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP
func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
- s, err := r.envParams.GetValueIfInside("VPNSP", []string{"pia", "private internet access", "mullvad", "windscribe", "surfshark", "cyberghost", "vyprvpn", "nordvpn"})
+ s, err := r.envParams.GetValueIfInside("VPNSP", []string{"pia", "private internet access", "mullvad", "windscribe", "surfshark", "cyberghost", "vyprvpn", "nordvpn", "purevpn"})
if s == "pia" {
s = "private internet access"
}
diff --git a/internal/params/purevpn.go b/internal/params/purevpn.go
new file mode 100644
index 000000000..2c30abad5
--- /dev/null
+++ b/internal/params/purevpn.go
@@ -0,0 +1,26 @@
+package params
+
+import (
+ "github.com/qdm12/private-internet-access-docker/internal/constants"
+)
+
+// GetPurevpnRegion obtains the region (continent) for the PureVPN server from the
+// environment variable REGION
+func (r *reader) GetPurevpnRegion() (region string, err error) {
+ choices := append(constants.PurevpnRegionChoices(), "")
+ return r.envParams.GetValueIfInside("REGION", choices)
+}
+
+// GetPurevpnCountry obtains the country for the PureVPN server from the
+// environment variable COUNTRY
+func (r *reader) GetPurevpnCountry() (country string, err error) {
+ choices := append(constants.PurevpnCountryChoices(), "")
+ return r.envParams.GetValueIfInside("COUNTRY", choices)
+}
+
+// GetPurevpnCity obtains the city for the PureVPN server from the
+// environment variable CITY
+func (r *reader) GetPurevpnCity() (city string, err error) {
+ choices := append(constants.PurevpnCityChoices(), "")
+ return r.envParams.GetValueIfInside("CITY", choices)
+}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index cd70ec3c4..e7b907557 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -29,6 +29,8 @@ func New(provider models.VPNProvider) Provider {
return newVyprvpn()
case constants.Nordvpn:
return newNordvpn()
+ case constants.Purevpn:
+ return newPurevpn()
default:
return nil // should never occur
}
diff --git a/internal/provider/purevpn.go b/internal/provider/purevpn.go
new file mode 100644
index 000000000..a90857b10
--- /dev/null
+++ b/internal/provider/purevpn.go
@@ -0,0 +1,159 @@
+package provider
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/qdm12/golibs/network"
+ "github.com/qdm12/private-internet-access-docker/internal/constants"
+ "github.com/qdm12/private-internet-access-docker/internal/models"
+)
+
+type purevpn struct{}
+
+func newPurevpn() *purevpn {
+ return &purevpn{}
+}
+
+func (p *purevpn) filterServers(region, country, city string) (servers []models.PurevpnServer) {
+ allServers := constants.PurevpnServers()
+ for i, server := range allServers {
+ if len(region) == 0 {
+ server.Region = ""
+ }
+ if len(country) == 0 {
+ server.Country = ""
+ }
+ if len(city) == 0 {
+ server.City = ""
+ }
+ if strings.EqualFold(server.Region, region) &&
+ strings.EqualFold(server.Country, country) &&
+ strings.EqualFold(server.City, city) {
+ servers = append(servers, allServers[i])
+ }
+ }
+ return servers
+}
+
+func (p *purevpn) GetOpenVPNConnections(selection models.ServerSelection) (connections []models.OpenVPNConnection, err error) { //nolint:dupl
+ servers := p.filterServers(selection.Region, selection.Country, selection.City)
+ if len(servers) == 0 {
+ return nil, fmt.Errorf("no server found for region %q, country %q and city %q", selection.Region, selection.Country, selection.City)
+ }
+
+ var port uint16
+ switch {
+ case selection.Protocol == constants.UDP:
+ port = 53
+ case selection.Protocol == constants.TCP:
+ port = 80
+ default:
+ return nil, fmt.Errorf("protocol %q is unknown", selection.Protocol)
+ }
+
+ for _, server := range servers {
+ for _, IP := range server.IPs {
+ if selection.TargetIP != nil {
+ if IP.Equal(selection.TargetIP) {
+ return []models.OpenVPNConnection{{IP: IP, Port: port, Protocol: selection.Protocol}}, nil
+ }
+ } else {
+ connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: selection.Protocol})
+ }
+ }
+ }
+
+ if selection.TargetIP != nil {
+ return nil, fmt.Errorf("target IP address %q not found in IP addresses", selection.TargetIP)
+ }
+
+ if len(connections) > 64 {
+ connections = connections[:64]
+ }
+
+ return connections, nil
+}
+
+func (p *purevpn) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { //nolint:dupl
+ if len(cipher) == 0 {
+ cipher = aes256cbc
+ }
+ lines = []string{
+ "client",
+ "dev tun",
+ "nobind",
+ "persist-key",
+ "remote-cert-tls server",
+
+ // Purevpn specific
+ "key-direction 1",
+ "remote-cert-tls server",
+ "cipher AES-256-CBC",
+ "route-method exe",
+ "route-delay 0",
+ "route 0.0.0.0 0.0.0.0",
+ "script-security 2",
+
+ // Added constant values
+ "auth-nocache",
+ "mute-replay-warnings",
+ "pull-filter ignore \"auth-token\"", // prevent auth failed loops
+ "auth-retry nointeract",
+ "remote-random",
+ "suppress-timestamps",
+
+ // Modified variables
+ fmt.Sprintf("verb %d", verbosity),
+ fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
+ fmt.Sprintf("proto %s", string(connections[0].Protocol)),
+ fmt.Sprintf("cipher %s", cipher),
+ }
+ if !root {
+ lines = append(lines, "user nonrootuser")
+ }
+ for _, connection := range connections {
+ lines = append(lines, fmt.Sprintf("remote %s %d", connection.IP.String(), connection.Port))
+ }
+ lines = append(lines, []string{
+ "",
+ "-----BEGIN CERTIFICATE-----",
+ constants.PurevpnCertificateAuthority,
+ "-----END CERTIFICATE-----",
+ "",
+ }...)
+ lines = append(lines, []string{
+ "",
+ "-----BEGIN CERTIFICATE-----",
+ constants.PurevpnCertificate,
+ "-----END CERTIFICATE-----",
+ "",
+ }...)
+ lines = append(lines, []string{
+ "",
+ "-----BEGIN PRIVATE KEY-----",
+ constants.PurevpnKey,
+ "-----END PRIVATE KEY-----",
+ "",
+ "",
+ }...)
+ lines = append(lines, []string{
+ "",
+ "-----BEGIN OpenVPN Static key V1-----",
+ constants.PurevpnOpenvpnStaticKeyV1,
+ "-----END OpenVPN Static key V1-----",
+ "",
+ "",
+ }...)
+ if len(auth) > 0 {
+ lines = append(lines, "auth "+auth)
+ }
+ if connections[0].Protocol == constants.UDP {
+ lines = append(lines, "explicit-exit-notify")
+ }
+ return lines
+}
+
+func (p *purevpn) GetPortForward(client network.Client) (port uint16, err error) {
+ panic("port forwarding is not supported for purevpn")
+}
diff --git a/internal/settings/openvpn.go b/internal/settings/openvpn.go
index db61211d7..787fb463b 100644
--- a/internal/settings/openvpn.go
+++ b/internal/settings/openvpn.go
@@ -66,6 +66,8 @@ func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvid
settings.Provider, err = GetVyprvpnSettings(paramsReader)
case constants.Nordvpn:
settings.Provider, err = GetNordvpnSettings(paramsReader)
+ case constants.Purevpn:
+ settings.Provider, err = GetPurevpnSettings(paramsReader)
default:
err = fmt.Errorf("VPN service provider %q is not valid", vpnProvider)
}
diff --git a/internal/settings/providers.go b/internal/settings/providers.go
index 43d40313d..ecfe8acfe 100644
--- a/internal/settings/providers.go
+++ b/internal/settings/providers.go
@@ -175,3 +175,29 @@ func GetNordvpnSettings(paramsReader params.Reader) (settings models.ProviderSet
}
return settings, nil
}
+
+// GetPurevpnSettings obtains Purevpn settings from environment variables using the params package.
+func GetPurevpnSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
+ settings.Name = constants.Mullvad
+ settings.ServerSelection.Protocol, err = paramsReader.GetNetworkProtocol()
+ if err != nil {
+ return settings, err
+ }
+ settings.ServerSelection.TargetIP, err = paramsReader.GetTargetIP()
+ if err != nil {
+ return settings, err
+ }
+ settings.ServerSelection.Region, err = paramsReader.GetPurevpnRegion()
+ if err != nil {
+ return settings, err
+ }
+ settings.ServerSelection.Country, err = paramsReader.GetPurevpnCountry()
+ if err != nil {
+ return settings, err
+ }
+ settings.ServerSelection.City, err = paramsReader.GetPurevpnCity()
+ if err != nil {
+ return settings, err
+ }
+ return settings, nil
+}