Privado support, fix #285 (#288)

This commit is contained in:
Quentin McGaw
2020-11-08 20:56:49 -05:00
committed by GitHub
parent 0423388b52
commit f1e4b9937b
24 changed files with 610 additions and 9 deletions

View File

@@ -1,7 +1,7 @@
# Gluetun VPN client # Gluetun VPN client
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access, *Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN and PureVPN VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy* Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
**ANNOUNCEMENT**: *Github Wiki reworked* **ANNOUNCEMENT**: *Github Wiki reworked*
@@ -28,7 +28,7 @@ Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN and PureVPN VPN serv
## Features ## Features
- Based on Alpine 3.12 for a small Docker image of 52MB - Based on Alpine 3.12 for a small Docker image of 52MB
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN** and **PureVPN** servers - Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN** and **Privado** servers
- Supports Openvpn only for now - Supports Openvpn only for now
- DNS over TLS baked in with service provider(s) of your choice - 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 - DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
@@ -96,7 +96,7 @@ docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo
| Variable | Default | Choices | Description | | Variable | Default | Choices | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 🏁 `VPNSP` | `private internet access` | `private internet access`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn`, `purevpn` | VPN Service Provider | | 🏁 `VPNSP` | `private internet access` | `private internet access`, `mullvad`, `windscribe`, `surfshark`, `vyprvpn`, `nordvpn`, `purevpn`, `privado` | VPN Service Provider |
| `IP_STATUS_FILE` | `/tmp/gluetun/ip` | Any filepath | Filepath to store the public IP address assigned | | `IP_STATUS_FILE` | `/tmp/gluetun/ip` | Any filepath | Filepath to store the public IP address assigned |
| `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use | | `PROTOCOL` | `udp` | `udp` or `tcp` | Network protocol to use |
| `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level | | `OPENVPN_VERBOSITY` | `1` | `0` to `6` | Openvpn verbosity level |
@@ -202,6 +202,15 @@ docker run --rm --network=container:gluetun alpine:3.12 wget -qO- https://ipinfo
| `COUNTRY` | | One of the [PureVPN countries](https://support.purevpn.com/vpn-servers) | VPN server country | | `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 | | `CITY` | | One of the [PureVPN cities](https://support.purevpn.com/vpn-servers) | VPN server city |
- Privado
| Variable | Default | Choices | Description |
| --- | --- | --- | --- |
| 🏁 `USER` | | | Your username |
| 🏁 `PASSWORD` | | | Your password |
| `CITY` | | One of the Privado city codes, i.e. `ams` | VPN server city |
| `SERVER_NUMBER` | | Server integer number | Optional server number. For example `2` for `sof-002` |
### DNS over TLS ### DNS over TLS
None of the following values are required. None of the following values are required.

View File

@@ -93,6 +93,7 @@ func Update(args []string) error {
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers") flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers") flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers")
flagSet.BoolVar(&options.PIA, "pia", false, "Update Private Internet Access post-summer 2020 servers") flagSet.BoolVar(&options.PIA, "pia", false, "Update Private Internet Access post-summer 2020 servers")
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers") flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers") flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers") flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")

View File

@@ -0,0 +1,206 @@
package constants
import (
"net"
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll
const (
PrivadoCertificate = "MIIFKDCCAxCgAwIBAgIJAMtrmqZxIV/OMA0GCSqGSIb3DQEBDQUAMBIxEDAOBgNVBAMMB1ByaXZhZG8wHhcNMjAwMTA4MjEyODQ1WhcNMzUwMTA5MjEyODQ1WjASMRAwDgYDVQQDDAdQcml2YWRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxPwOgiwNJzZTnKIXwAB0TSu/Lu2qt2U2I8obtQjwhi/7OrfmbmYykSdro70al2XPhnwAGGdCxW6LDnp0UN/IOhD11mgBPo14f5CLkBQjSJ6VN5miPbvK746LsNZl9H8rQGvDuPo4CG9BfPZMiDRGlsMxij/jztzgT1gmuxQ7WHfFRcNzBas1dHa9hV/d3TU6/t47x4SE/ljdcCtJiu7Zn6ODKQoys3mB7Luz2ngqUJWvkqsg+E4+3eJ0M8Hlbn5TPaRJBID7DAdYo6Vs6xGCYr981ThFcmoIQ10js10yANrrfGAzd03b3TnLAgko0uQMHjliMZL6L8sWOPHxyxJI0us88SFh4UgcFyRHKHPKux7w24SxAlZUYoUcTHp9VjG5XvDKYxzgV2RdM4ulBGbQRQ3y3/CyddsyQYMvA55Ets0LfPaBvDIcct70iXijGsdvlX1du3ArGpG7Vaje/RU4nbbGT6HYRdt5YyZfof288ukMOSj20nVcmS+c/4tqsxSerRb1aq5LOi1IemSkTMeC5gCbexk+L1vl7NT/58sxjGmu5bXwnvev/lIItfi2AlITrfUSEv19iDMKkeshwn/+sFJBMWYyluP+yJ56yR+MWoXvLlSWphLDTqq19yx3BZn0P1tgbXoR0g8PTdJFcz8z3RIb7myVLYulV1oGG/3rka0CAwEAAaOBgDB+MB0GA1UdDgQWBBTFtJkZCVDuDAD6k5bJzefjJdO3DTBCBgNVHSMEOzA5gBTFtJkZCVDuDAD6k5bJzefjJdO3DaEWpBQwEjEQMA4GA1UEAwwHUHJpdmFkb4IJAMtrmqZxIV/OMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQB7MUSXMeBb9wlSv4sUaT1JHEwE26nlBw+TKmezfuPU5pBlY0LYr6qQZY95DHqsRJ7ByUzGUrGo17dNGXlcuNc6TAaQQEDRPo6y+LVh2TWMk15TUMI+MkqryJtCret7xGvDigKYMJgBy58HN3RAVr1B7cL9youwzLgc2Y/NcFKvnQJKeiIYAJ7g0CcnJiQvgZTS7xdwkEBXfsngmUCIG320DLPEL+Ze0HiUrxwWljMRya6i40AeH3Zu2i532xX1wV5+cjA4RJWIKg6ri/Q54iFGtZrA9/nc6y9uoQHkmz8cGyVUmJxFzMrrIICVqUtVRxLhkTMe4UzwRWTBeGgtW4tS0yq1QonAKfOyjgRw/CeY55D2UGvnAFZdTadtYXS4Alu2P9zdwoEk3fzHiVmDjqfJVr5wz9383aABUFrPI3nz6ed/Z6LZflKh1k+DUDEp8NxU4klUULWsSOKoa5zGX51G8cdHxwQLImXvtGuN5eSR8jCTgxFZhdps/xes4KkyfIz9FMYG748M+uOTgKITf4zdJ9BAyiQaOufVQZ8WjhWzWk9YHec9VqPkzpWNGkVjiRI5ewuXwZzZ164tMv2hikBXSuUCnFz37/ZNwGlDi0oBdDszCk2GxccdFHHaCSmpjU5MrdJ+5IhtTKGeTx+US2hTIVHQFIO99DmacxSYvLNcSQ=="
)
func PrivadoCityChoices() (choices []string) {
servers := PrivadoServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return choices
}
//nolint:gomnd
func PrivadoServers() []models.PrivadoServer {
return []models.PrivadoServer{
{City: "akl", Number: 1, IP: net.IP{23, 254, 104, 114}},
{City: "akl", Number: 2, IP: net.IP{23, 254, 104, 120}},
{City: "akl", Number: 3, IP: net.IP{23, 254, 104, 51}},
{City: "ams", Number: 1, IP: net.IP{91, 148, 224, 10}},
{City: "ams", Number: 10, IP: net.IP{91, 148, 228, 20}},
{City: "ams", Number: 11, IP: net.IP{91, 148, 228, 30}},
{City: "ams", Number: 12, IP: net.IP{91, 148, 228, 40}},
{City: "ams", Number: 13, IP: net.IP{91, 148, 228, 50}},
{City: "ams", Number: 14, IP: net.IP{91, 148, 228, 60}},
{City: "ams", Number: 15, IP: net.IP{91, 148, 228, 70}},
{City: "ams", Number: 16, IP: net.IP{91, 148, 228, 80}},
{City: "ams", Number: 2, IP: net.IP{91, 148, 224, 20}},
{City: "ams", Number: 3, IP: net.IP{91, 148, 224, 30}},
{City: "ams", Number: 4, IP: net.IP{91, 148, 224, 40}},
{City: "ams", Number: 5, IP: net.IP{91, 148, 224, 50}},
{City: "ams", Number: 6, IP: net.IP{91, 148, 224, 60}},
{City: "ams", Number: 7, IP: net.IP{91, 148, 224, 70}},
{City: "ams", Number: 8, IP: net.IP{91, 148, 224, 80}},
{City: "ams", Number: 9, IP: net.IP{91, 148, 228, 10}},
{City: "arn", Number: 1, IP: net.IP{86, 106, 103, 67}},
{City: "arn", Number: 2, IP: net.IP{86, 106, 103, 74}},
{City: "arn", Number: 3, IP: net.IP{86, 106, 103, 81}},
{City: "ath", Number: 1, IP: net.IP{188, 123, 126, 61}},
{City: "ath", Number: 2, IP: net.IP{188, 123, 126, 64}},
{City: "ath", Number: 3, IP: net.IP{188, 123, 126, 68}},
{City: "ath", Number: 4, IP: net.IP{188, 123, 126, 72}},
{City: "beg", Number: 1, IP: net.IP{89, 38, 224, 19}},
{City: "beg", Number: 2, IP: net.IP{89, 38, 224, 25}},
{City: "bkk", Number: 1, IP: net.IP{119, 59, 111, 3}},
{City: "bkk", Number: 2, IP: net.IP{119, 59, 111, 11}},
{City: "bom", Number: 1, IP: net.IP{103, 26, 204, 61}},
{City: "bom", Number: 2, IP: net.IP{103, 26, 204, 70}},
{City: "bru", Number: 1, IP: net.IP{217, 138, 211, 163}},
{City: "bru", Number: 2, IP: net.IP{217, 138, 211, 170}},
{City: "bru", Number: 3, IP: net.IP{217, 138, 211, 177}},
{City: "bru", Number: 4, IP: net.IP{217, 138, 211, 184}},
{City: "bts", Number: 1, IP: net.IP{37, 120, 221, 227}},
{City: "bts", Number: 2, IP: net.IP{37, 120, 221, 233}},
{City: "bud", Number: 1, IP: net.IP{185, 128, 26, 194}},
{City: "bud", Number: 2, IP: net.IP{185, 128, 26, 200}},
{City: "cdg", Number: 1, IP: net.IP{89, 40, 183, 99}},
{City: "cdg", Number: 2, IP: net.IP{89, 40, 183, 106}},
{City: "cdg", Number: 3, IP: net.IP{89, 40, 183, 113}},
{City: "cdg", Number: 4, IP: net.IP{89, 40, 183, 120}},
{City: "cph", Number: 1, IP: net.IP{2, 58, 46, 35}},
{City: "cph", Number: 2, IP: net.IP{2, 58, 46, 42}},
{City: "cph", Number: 3, IP: net.IP{2, 58, 46, 49}},
{City: "cph", Number: 4, IP: net.IP{2, 58, 46, 56}},
{City: "dca", Number: 1, IP: net.IP{85, 12, 61, 10}},
{City: "dca", Number: 13, IP: net.IP{185, 247, 68, 3}},
{City: "dca", Number: 14, IP: net.IP{185, 247, 68, 10}},
{City: "dca", Number: 15, IP: net.IP{185, 247, 68, 17}},
{City: "dca", Number: 16, IP: net.IP{185, 247, 68, 24}},
{City: "dca", Number: 2, IP: net.IP{85, 12, 61, 20}},
{City: "dca", Number: 3, IP: net.IP{85, 12, 61, 30}},
{City: "dca", Number: 4, IP: net.IP{85, 12, 61, 40}},
{City: "dca", Number: 5, IP: net.IP{85, 12, 61, 50}},
{City: "dca", Number: 6, IP: net.IP{85, 12, 61, 60}},
{City: "dca", Number: 7, IP: net.IP{85, 12, 61, 70}},
{City: "dca", Number: 8, IP: net.IP{85, 12, 61, 80}},
{City: "dfw", Number: 1, IP: net.IP{23, 105, 32, 243}},
{City: "dfw", Number: 2, IP: net.IP{23, 105, 32, 244}},
{City: "dub", Number: 1, IP: net.IP{84, 247, 48, 227}},
{City: "dub", Number: 2, IP: net.IP{84, 247, 48, 234}},
{City: "dub", Number: 3, IP: net.IP{84, 247, 48, 241}},
{City: "dub", Number: 4, IP: net.IP{84, 247, 48, 248}},
{City: "eze", Number: 1, IP: net.IP{168, 205, 93, 211}},
{City: "eze", Number: 2, IP: net.IP{168, 205, 93, 217}},
{City: "fra", Number: 1, IP: net.IP{91, 148, 232, 10}},
{City: "fra", Number: 2, IP: net.IP{91, 148, 232, 20}},
{City: "fra", Number: 3, IP: net.IP{91, 148, 232, 30}},
{City: "fra", Number: 4, IP: net.IP{91, 148, 232, 40}},
{City: "fra", Number: 5, IP: net.IP{91, 148, 233, 7}},
{City: "fra", Number: 6, IP: net.IP{91, 148, 233, 8}},
{City: "fra", Number: 7, IP: net.IP{91, 148, 233, 9}},
{City: "fra", Number: 8, IP: net.IP{91, 148, 233, 10}},
{City: "gru", Number: 1, IP: net.IP{177, 54, 145, 193}},
{City: "gru", Number: 2, IP: net.IP{177, 54, 145, 197}},
{City: "hel", Number: 1, IP: net.IP{194, 34, 134, 219}},
{City: "hel", Number: 2, IP: net.IP{194, 34, 134, 227}},
{City: "hkg", Number: 1, IP: net.IP{209, 58, 185, 88}},
{City: "hkg", Number: 2, IP: net.IP{209, 58, 185, 97}},
{City: "hkg", Number: 3, IP: net.IP{209, 58, 185, 108}},
{City: "hkg", Number: 4, IP: net.IP{209, 58, 185, 120}},
{City: "icn", Number: 1, IP: net.IP{169, 56, 73, 146}},
{City: "icn", Number: 2, IP: net.IP{169, 56, 73, 153}},
{City: "iev", Number: 1, IP: net.IP{176, 103, 52, 40}},
{City: "iev", Number: 2, IP: net.IP{176, 103, 53, 40}},
{City: "ist", Number: 1, IP: net.IP{185, 84, 183, 3}},
{City: "ist", Number: 2, IP: net.IP{185, 84, 183, 4}},
{City: "jfk", Number: 1, IP: net.IP{217, 138, 208, 99}},
{City: "jfk", Number: 2, IP: net.IP{217, 138, 208, 106}},
{City: "jfk", Number: 3, IP: net.IP{217, 138, 208, 113}},
{City: "jfk", Number: 4, IP: net.IP{217, 138, 208, 120}},
{City: "jnb", Number: 1, IP: net.IP{172, 107, 93, 131}},
{City: "jnb", Number: 2, IP: net.IP{172, 107, 93, 137}},
{City: "lax", Number: 10, IP: net.IP{45, 152, 182, 234}},
{City: "lax", Number: 11, IP: net.IP{45, 152, 182, 241}},
{City: "lax", Number: 12, IP: net.IP{45, 152, 182, 248}},
{City: "lax", Number: 9, IP: net.IP{45, 152, 182, 227}},
{City: "lis", Number: 1, IP: net.IP{89, 26, 243, 153}},
{City: "lis", Number: 2, IP: net.IP{89, 26, 243, 154}},
{City: "lon", Number: 1, IP: net.IP{217, 138, 195, 163}},
{City: "lon", Number: 2, IP: net.IP{217, 138, 195, 170}},
{City: "lon", Number: 3, IP: net.IP{217, 138, 195, 177}},
{City: "lon", Number: 4, IP: net.IP{217, 138, 195, 184}},
{City: "mad", Number: 1, IP: net.IP{217, 138, 218, 131}},
{City: "man", Number: 1, IP: net.IP{217, 138, 196, 131}},
{City: "man", Number: 2, IP: net.IP{217, 138, 196, 138}},
{City: "man", Number: 3, IP: net.IP{217, 138, 196, 145}},
{City: "man", Number: 4, IP: net.IP{217, 138, 196, 152}},
{City: "mex", Number: 1, IP: net.IP{169, 57, 96, 52}},
{City: "mex", Number: 2, IP: net.IP{169, 57, 96, 57}},
{City: "mia", Number: 1, IP: net.IP{86, 106, 87, 131}},
{City: "mia", Number: 2, IP: net.IP{86, 106, 87, 138}},
{City: "mia", Number: 3, IP: net.IP{86, 106, 87, 145}},
{City: "mia", Number: 4, IP: net.IP{86, 106, 87, 152}},
{City: "mxp", Number: 1, IP: net.IP{89, 40, 182, 195}},
{City: "mxp", Number: 2, IP: net.IP{89, 40, 182, 201}},
{City: "nrt", Number: 1, IP: net.IP{217, 138, 252, 3}},
{City: "nrt", Number: 2, IP: net.IP{217, 138, 252, 10}},
{City: "nrt", Number: 3, IP: net.IP{217, 138, 252, 17}},
{City: "nrt", Number: 4, IP: net.IP{217, 138, 252, 24}},
{City: "ord", Number: 1, IP: net.IP{23, 108, 95, 129}},
{City: "ord", Number: 2, IP: net.IP{23, 108, 95, 167}},
{City: "osl", Number: 1, IP: net.IP{84, 247, 50, 115}},
{City: "osl", Number: 2, IP: net.IP{84, 247, 50, 119}},
{City: "osl", Number: 3, IP: net.IP{84, 247, 50, 123}},
{City: "otp", Number: 1, IP: net.IP{89, 46, 102, 179}},
{City: "otp", Number: 2, IP: net.IP{89, 46, 102, 185}},
{City: "phx", Number: 1, IP: net.IP{91, 148, 236, 10}},
{City: "phx", Number: 2, IP: net.IP{91, 148, 236, 20}},
{City: "phx", Number: 3, IP: net.IP{91, 148, 236, 30}},
{City: "phx", Number: 4, IP: net.IP{91, 148, 236, 40}},
{City: "phx", Number: 5, IP: net.IP{91, 148, 236, 50}},
{City: "phx", Number: 6, IP: net.IP{91, 148, 236, 60}},
{City: "phx", Number: 7, IP: net.IP{91, 148, 236, 70}},
{City: "phx", Number: 8, IP: net.IP{91, 148, 236, 80}},
{City: "prg", Number: 1, IP: net.IP{185, 216, 35, 99}},
{City: "prg", Number: 2, IP: net.IP{185, 216, 35, 105}},
{City: "rix", Number: 1, IP: net.IP{109, 248, 149, 35}},
{City: "rix", Number: 2, IP: net.IP{109, 248, 149, 40}},
{City: "rkv", Number: 1, IP: net.IP{82, 221, 131, 78}},
{City: "rkv", Number: 2, IP: net.IP{82, 221, 131, 127}},
{City: "sea", Number: 1, IP: net.IP{23, 81, 208, 96}},
{City: "sea", Number: 2, IP: net.IP{23, 81, 208, 104}},
{City: "sin", Number: 1, IP: net.IP{92, 119, 178, 131}},
{City: "sin", Number: 2, IP: net.IP{92, 119, 178, 138}},
{City: "sin", Number: 3, IP: net.IP{92, 119, 178, 145}},
{City: "sin", Number: 4, IP: net.IP{92, 119, 178, 152}},
{City: "sof", Number: 1, IP: net.IP{217, 138, 221, 163}},
{City: "sof", Number: 2, IP: net.IP{217, 138, 221, 169}},
{City: "stl", Number: 1, IP: net.IP{148, 72, 170, 145}},
{City: "stl", Number: 2, IP: net.IP{148, 72, 172, 82}},
{City: "syd", Number: 1, IP: net.IP{93, 115, 35, 35}},
{City: "syd", Number: 2, IP: net.IP{93, 115, 35, 42}},
{City: "syd", Number: 3, IP: net.IP{93, 115, 35, 49}},
{City: "syd", Number: 4, IP: net.IP{93, 115, 35, 56}},
{City: "vie", Number: 1, IP: net.IP{5, 253, 207, 227}},
{City: "vie", Number: 2, IP: net.IP{5, 253, 207, 234}},
{City: "vie", Number: 3, IP: net.IP{5, 253, 207, 241}},
{City: "vie", Number: 4, IP: net.IP{5, 253, 207, 248}},
{City: "vno", Number: 1, IP: net.IP{185, 64, 104, 176}},
{City: "vno", Number: 2, IP: net.IP{185, 64, 104, 180}},
{City: "waw", Number: 1, IP: net.IP{217, 138, 209, 163}},
{City: "waw", Number: 2, IP: net.IP{217, 138, 209, 164}},
{City: "waw", Number: 3, IP: net.IP{217, 138, 209, 165}},
{City: "waw", Number: 4, IP: net.IP{217, 138, 209, 166}},
{City: "yul", Number: 1, IP: net.IP{217, 138, 213, 67}},
{City: "yul", Number: 2, IP: net.IP{217, 138, 213, 74}},
{City: "yul", Number: 3, IP: net.IP{217, 138, 213, 81}},
{City: "yul", Number: 4, IP: net.IP{217, 138, 213, 88}},
{City: "yvr", Number: 1, IP: net.IP{71, 19, 248, 57}},
{City: "yvr", Number: 2, IP: net.IP{71, 19, 248, 113}},
{City: "yyz", Number: 3, IP: net.IP{199, 189, 27, 19}},
{City: "zrh", Number: 1, IP: net.IP{185, 156, 175, 195}},
{City: "zrh", Number: 2, IP: net.IP{185, 156, 175, 202}},
{City: "zrh", Number: 3, IP: net.IP{185, 156, 175, 209}},
{City: "zrh", Number: 4, IP: net.IP{185, 156, 175, 216}},
}
}

View File

@@ -31,6 +31,11 @@ func GetAllServers() (allServers models.AllServers) {
Timestamp: 1599323261, Timestamp: 1599323261,
Servers: PurevpnServers(), Servers: PurevpnServers(),
}, },
Privado: models.PrivadoServers{
Version: 1,
Timestamp: 1604546335,
Servers: PrivadoServers(),
},
Surfshark: models.SurfsharkServers{ Surfshark: models.SurfsharkServers{
Version: 1, Version: 1,
Timestamp: 1599957644, Timestamp: 1599957644,

View File

@@ -54,6 +54,11 @@ func Test_versions(t *testing.T) {
version: allServers.Pia.Version, version: allServers.Pia.Version,
digest: "f1e01afe", digest: "f1e01afe",
}, },
"Privado": {
model: models.PrivadoServer{},
version: allServers.Privado.Version,
digest: "d7f96824",
},
"Purevpn": { "Purevpn": {
model: models.PurevpnServer{}, model: models.PurevpnServer{},
version: allServers.Purevpn.Version, version: allServers.Purevpn.Version,
@@ -135,6 +140,11 @@ func Test_timestamps(t *testing.T) {
timestamp: allServers.Purevpn.Timestamp, timestamp: allServers.Purevpn.Timestamp,
digest: "cdf9b708", digest: "cdf9b708",
}, },
"Privado": {
servers: allServers.Privado.Servers,
timestamp: allServers.Privado.Timestamp,
digest: "3ccd3e0f",
},
"Surfshark": { "Surfshark": {
servers: allServers.Surfshark.Servers, servers: allServers.Surfshark.Servers,
timestamp: allServers.Surfshark.Timestamp, timestamp: allServers.Surfshark.Timestamp,

View File

@@ -2,9 +2,9 @@ package constants
const ( const (
// Announcement is a message announcement. // Announcement is a message announcement.
Announcement = "Port forwarding is working for PIA v4 servers" Announcement = "Support for Privado"
// AnnouncementExpiration is the expiration date of the announcement in format yyyy-mm-dd. // AnnouncementExpiration is the expiration date of the announcement in format yyyy-mm-dd.
AnnouncementExpiration = "2020-11-15" AnnouncementExpiration = "2020-11-25"
) )
const ( const (

View File

@@ -21,6 +21,8 @@ const (
Nordvpn models.VPNProvider = "nordvpn" Nordvpn models.VPNProvider = "nordvpn"
// PureVPN is a VPN provider. // PureVPN is a VPN provider.
Purevpn models.VPNProvider = "purevpn" Purevpn models.VPNProvider = "purevpn"
// Privado is a VPN provider.
Privado models.VPNProvider = "privado"
) )
const ( const (

View File

@@ -6,8 +6,10 @@ type OpenVPNConnection struct {
IP net.IP IP net.IP
Port uint16 Port uint16
Protocol NetworkProtocol Protocol NetworkProtocol
Hostname string // Privado for tls verification
} }
func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool { func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool {
return o.IP.Equal(other.IP) && o.Port == other.Port && o.Protocol == other.Protocol return o.IP.Equal(other.IP) && o.Port == other.Port && o.Protocol == other.Protocol &&
o.Hostname == other.Hostname
} }

View File

@@ -130,6 +130,11 @@ func (p *ProviderSettings) String() string {
"Countries: "+commaJoin(p.ServerSelection.Countries), "Countries: "+commaJoin(p.ServerSelection.Countries),
"Cities: "+commaJoin(p.ServerSelection.Cities), "Cities: "+commaJoin(p.ServerSelection.Cities),
) )
case "privado":
settingsList = append(settingsList,
"Cities: "+commaJoin(p.ServerSelection.Cities),
"Server numbers: "+commaJoin(numbers),
)
default: default:
settingsList = append(settingsList, settingsList = append(settingsList,
"<Missing String method, please implement me!>", "<Missing String method, please implement me!>",

View File

@@ -107,6 +107,17 @@ func (s *PurevpnServer) String() string {
s.Region, s.Country, s.City, goStringifyIPs(s.IPs)) s.Region, s.Country, s.City, goStringifyIPs(s.IPs))
} }
type PrivadoServer struct {
IP net.IP `json:"ip"`
City string `json:"city"`
Number uint16 `json:"number"`
}
func (s *PrivadoServer) String() string {
return fmt.Sprintf("{City: %q, Number: %d, IP: %s}",
s.City, s.Number, goStringifyIP(s.IP))
}
func goStringifyIP(ip net.IP) string { func goStringifyIP(ip net.IP) string {
s := fmt.Sprintf("%#v", ip) s := fmt.Sprintf("%#v", ip)
s = strings.TrimSuffix(strings.TrimPrefix(s, "net.IP{"), "}") s = strings.TrimSuffix(strings.TrimPrefix(s, "net.IP{"), "}")

View File

@@ -6,6 +6,7 @@ type AllServers struct {
Mullvad MullvadServers `json:"mullvad"` Mullvad MullvadServers `json:"mullvad"`
Nordvpn NordvpnServers `json:"nordvpn"` Nordvpn NordvpnServers `json:"nordvpn"`
Pia PiaServers `json:"pia"` Pia PiaServers `json:"pia"`
Privado PrivadoServers `json:"privado"`
Purevpn PurevpnServers `json:"purevpn"` Purevpn PurevpnServers `json:"purevpn"`
Surfshark SurfsharkServers `json:"surfshark"` Surfshark SurfsharkServers `json:"surfshark"`
Vyprvpn VyprvpnServers `json:"vyprvpn"` Vyprvpn VyprvpnServers `json:"vyprvpn"`
@@ -32,6 +33,11 @@ type PiaServers struct {
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
Servers []PIAServer `json:"servers"` Servers []PIAServer `json:"servers"`
} }
type PrivadoServers struct {
Version uint16 `json:"version"`
Timestamp int64 `json:"timestamp"`
Servers []PrivadoServer `json:"servers"`
}
type PurevpnServers struct { type PurevpnServers struct {
Version uint16 `json:"version"` Version uint16 `json:"version"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`

View File

@@ -91,6 +91,10 @@ type Reader interface {
GetNordvpnRegions() (regions []string, err error) GetNordvpnRegions() (regions []string, err error)
GetNordvpnNumbers() (numbers []uint16, err error) GetNordvpnNumbers() (numbers []uint16, err error)
// Privado getters
GetPrivadoCities() (regions []string, err error)
GetPrivadoNumbers() (numbers []uint16, err error)
// PureVPN getters // PureVPN getters
GetPurevpnRegions() (regions []string, err error) GetPurevpnRegions() (regions []string, err error)
GetPurevpnCountries() (countries []string, err error) GetPurevpnCountries() (countries []string, err error)
@@ -148,9 +152,9 @@ func (r *reader) GetVPNSP() (vpnServiceProvider models.VPNProvider, err error) {
s, err := r.envParams.GetValueIfInside( s, err := r.envParams.GetValueIfInside(
"VPNSP", "VPNSP",
[]string{ []string{
"pia", "private internet access", "private internet access old", "pia", "private internet access",
"mullvad", "windscribe", "surfshark", "cyberghost", "mullvad", "windscribe", "surfshark", "cyberghost",
"vyprvpn", "nordvpn", "purevpn", "vyprvpn", "nordvpn", "purevpn", "privado",
}, libparams.Default("private internet access")) }, libparams.Default("private internet access"))
if s == "pia" { if s == "pia" {
s = "private internet access" s = "private internet access"

View File

@@ -0,0 +1,37 @@
package params
import (
"fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants"
)
// GetPrivadoCities obtains the cities for the Privado server from the
// environment variable CITY.
func (r *reader) GetPrivadoCities() (regions []string, err error) {
return r.envParams.GetCSVInPossibilities("CITY", constants.PrivadoCityChoices())
}
// GetPrivadoNumbers obtains the server numbers (optional) for the Privado servers from the
// environment variable SERVER_NUMBER.
func (r *reader) GetPrivadoNumbers() (numbers []uint16, err error) {
possibilities := make([]string, 65537)
for i := range possibilities {
possibilities[i] = fmt.Sprintf("%d", i)
}
possibilities[65536] = ""
values, err := r.envParams.GetCSVInPossibilities("SERVER_NUMBER", possibilities)
if err != nil {
return nil, err
}
numbers = make([]uint16, len(values))
for i := range values {
n, err := strconv.Atoi(values[i])
if err != nil {
return nil, err
}
numbers[i] = uint16(n)
}
return numbers, nil
}

View File

@@ -2,4 +2,5 @@ package provider
const ( const (
aes256cbc = "aes-256-cbc" aes256cbc = "aes-256-cbc"
sha256 = "sha256"
) )

View File

@@ -68,7 +68,7 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity,
cipher = aes256cbc cipher = aes256cbc
} }
if len(auth) == 0 { if len(auth) == 0 {
auth = "SHA256" auth = sha256
} }
lines = []string{ lines = []string{
"client", "client",

View File

@@ -0,0 +1,144 @@
package provider
import (
"context"
"fmt"
"math/rand"
"net"
"net/http"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
)
type privado struct {
servers []models.PrivadoServer
randSource rand.Source
}
func newPrivado(servers []models.PrivadoServer, timeNow timeNowFunc) *privado {
return &privado{
servers: servers,
randSource: rand.NewSource(timeNow().UnixNano()),
}
}
func (s *privado) filterServers(cities []string, numbers []uint16) (servers []models.PrivadoServer) {
numbersStr := make([]string, len(numbers))
for i := range numbers {
numbersStr[i] = fmt.Sprintf("%d", numbers[i])
}
for _, server := range s.servers {
numberStr := fmt.Sprintf("%d", server.Number)
switch {
case
filterByPossibilities(server.City, cities),
filterByPossibilities(numberStr, numbersStr):
default:
servers = append(servers, server)
}
}
return servers
}
func makePrivadoHostname(city string, number uint16) string {
numberString := ""
const ten, hundred = 10, 100
switch {
case number < ten:
numberString = fmt.Sprintf("00%d", number)
case number < hundred:
numberString = fmt.Sprintf("0%d", number)
default:
numberString = fmt.Sprintf("%d", number)
}
return fmt.Sprintf("%s-%s.vpn.privado.io", city, numberString)
}
func (s *privado) GetOpenVPNConnection(selection models.ServerSelection) (
connection models.OpenVPNConnection, err error) {
var port uint16 = 1194
switch selection.Protocol {
case constants.UDP:
default:
return connection, fmt.Errorf("protocol %q is not supported by Privado", selection.Protocol)
}
if selection.TargetIP != nil {
return models.OpenVPNConnection{IP: selection.TargetIP, Port: port, Protocol: selection.Protocol}, nil
}
servers := s.filterServers(selection.Cities, selection.Numbers)
if len(servers) == 0 {
return connection, fmt.Errorf("no server found for cities %s and server numbers %v",
commaJoin(selection.Cities), selection.Numbers)
}
connections := make([]models.OpenVPNConnection, len(servers))
for i := range servers {
connection := models.OpenVPNConnection{
IP: servers[i].IP,
Port: port,
Protocol: selection.Protocol,
Hostname: makePrivadoHostname(servers[i].City, servers[i].Number),
}
connections = append(connections, connection)
}
return pickRandomConnection(connections, s.randSource), nil
}
func (s *privado) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool,
cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 {
cipher = aes256cbc
}
if len(auth) == 0 {
auth = sha256
}
lines = []string{
"client",
"dev tun",
"nobind",
"persist-key",
// Privado specific
"tls-cipher TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA",
fmt.Sprintf("verify-x509-name %s name", connection.Hostname),
// Added constant values
"auth-nocache",
"mute-replay-warnings",
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
"auth-retry nointeract",
"suppress-timestamps",
// Modified variables
fmt.Sprintf("verb %d", verbosity),
fmt.Sprintf("auth-user-pass %s", constants.OpenVPNAuthConf),
fmt.Sprintf("proto %s", connection.Protocol),
fmt.Sprintf("remote %s %d", connection.IP, connection.Port),
fmt.Sprintf("cipher %s", cipher),
fmt.Sprintf("auth %s", auth),
}
if !root {
lines = append(lines, "user nonrootuser")
}
lines = append(lines, []string{
"<ca>",
"-----BEGIN CERTIFICATE-----",
constants.PrivadoCertificate,
"-----END CERTIFICATE-----",
"</ca>",
}...)
return lines
}
func (s *privado) PortForward(ctx context.Context, client *http.Client,
fileManager files.FileManager, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,
syncState func(port uint16) (pfFilepath models.Filepath)) {
panic("port forwarding is not supported for privado")
}

View File

@@ -40,6 +40,8 @@ func New(provider models.VPNProvider, allServers models.AllServers, timeNow time
return newNordvpn(allServers.Nordvpn.Servers, timeNow) return newNordvpn(allServers.Nordvpn.Servers, timeNow)
case constants.Purevpn: case constants.Purevpn:
return newPurevpn(allServers.Purevpn.Servers, timeNow) return newPurevpn(allServers.Purevpn.Servers, timeNow)
case constants.Privado:
return newPrivado(allServers.Privado.Servers, timeNow)
default: default:
return nil // should never occur return nil // should never occur
} }

View File

@@ -68,6 +68,8 @@ func GetOpenVPNSettings(paramsReader params.Reader, vpnProvider models.VPNProvid
settings.Provider, err = GetNordvpnSettings(paramsReader) settings.Provider, err = GetNordvpnSettings(paramsReader)
case constants.Purevpn: case constants.Purevpn:
settings.Provider, err = GetPurevpnSettings(paramsReader) settings.Provider, err = GetPurevpnSettings(paramsReader)
case constants.Privado:
settings.Provider, err = GetPrivadoSettings(paramsReader)
default: default:
err = fmt.Errorf("VPN service provider %q is not valid", vpnProvider) err = fmt.Errorf("VPN service provider %q is not valid", vpnProvider)
} }

View File

@@ -232,3 +232,25 @@ func GetPurevpnSettings(paramsReader params.Reader) (settings models.ProviderSet
} }
return settings, nil return settings, nil
} }
// GetPrivadoSettings obtains Privado settings from environment variables using the params package.
func GetPrivadoSettings(paramsReader params.Reader) (settings models.ProviderSettings, err error) {
settings.Name = constants.Privado
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.Cities, err = paramsReader.GetPrivadoCities()
if err != nil {
return settings, err
}
settings.ServerSelection.Numbers, err = paramsReader.GetPrivadoNumbers()
if err != nil {
return settings, err
}
return settings, nil
}

View File

@@ -47,6 +47,12 @@ func (s *storage) mergeServers(hardcoded, persistent models.AllServers) (merged
merged.Pia = persistent.Pia merged.Pia = persistent.Pia
} }
} }
merged.Privado = hardcoded.Privado
if persistent.Privado.Timestamp > hardcoded.Privado.Timestamp {
s.logger.Info("Using Privado servers from file (%s more recent)",
getUnixTimeDifference(persistent.Privado.Timestamp, hardcoded.Privado.Timestamp))
merged.Privado = persistent.Privado
}
merged.Purevpn = hardcoded.Purevpn merged.Purevpn = hardcoded.Purevpn
if persistent.Purevpn.Timestamp > hardcoded.Purevpn.Timestamp { if persistent.Purevpn.Timestamp > hardcoded.Purevpn.Timestamp {
s.logger.Info("Using Purevpn servers from file (%s more recent)", s.logger.Info("Using Purevpn servers from file (%s more recent)",

View File

@@ -18,6 +18,7 @@ func countServers(allServers models.AllServers) int {
len(allServers.Mullvad.Servers) + len(allServers.Mullvad.Servers) +
len(allServers.Nordvpn.Servers) + len(allServers.Nordvpn.Servers) +
len(allServers.Pia.Servers) + len(allServers.Pia.Servers) +
len(allServers.Privado.Servers) +
len(allServers.Purevpn.Servers) + len(allServers.Purevpn.Servers) +
len(allServers.Surfshark.Servers) + len(allServers.Surfshark.Servers) +
len(allServers.Vyprvpn.Servers) + len(allServers.Vyprvpn.Servers) +

View File

@@ -5,6 +5,7 @@ type Options struct {
Mullvad bool Mullvad bool
Nordvpn bool Nordvpn bool
PIA bool PIA bool
Privado bool
Purevpn bool Purevpn bool
Surfshark bool Surfshark bool
Vyprvpn bool Vyprvpn bool

114
internal/updater/privado.go Normal file
View File

@@ -0,0 +1,114 @@
package updater
import (
"context"
"fmt"
"net"
"sort"
"strconv"
"strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/network"
)
func (u *updater) updatePrivado(ctx context.Context) (err error) {
servers, warnings, err := findPrivadoServersFromZip(ctx, u.client, u.lookupIP)
if u.options.CLI {
for _, warning := range warnings {
u.logger.Warn("Privado: %s", warning)
}
}
if err != nil {
return fmt.Errorf("cannot update Privado servers: %w", err)
}
if u.options.Stdout {
u.println(stringifyPrivadoServers(servers))
}
u.servers.Privado.Timestamp = u.timeNow().Unix()
u.servers.Privado.Servers = servers
return nil
}
func findPrivadoServersFromZip(ctx context.Context, client network.Client, lookupIP lookupIPFunc) (
servers []models.PrivadoServer, warnings []string, err error) {
const zipURL = "https://privado.io/apps/ovpn_configs.zip"
contents, err := fetchAndExtractFiles(ctx, client, zipURL)
if err != nil {
return nil, nil, err
}
for fileName, content := range contents {
if err := ctx.Err(); err != nil {
return nil, warnings, err
}
remoteLines := extractRemoteLinesFromOpenvpn(content)
if len(remoteLines) == 0 {
return nil, warnings, fmt.Errorf("cannot find any remote lines in %s", fileName)
}
hosts := extractHostnamesFromRemoteLines(remoteLines)
if len(hosts) == 0 {
return nil, warnings, fmt.Errorf("cannot find any hosts in %s", fileName)
} else if len(hosts) > 1 {
warning := fmt.Sprintf("more than one host in %q, only taking first one %q into account", fileName, hosts[0])
warnings = append(warnings, warning)
}
host := hosts[0]
if net.ParseIP(host) != nil {
warning := fmt.Sprintf("ignoring IP address host %q in %s", host, fileName)
warnings = append(warnings, warning)
continue
}
const repetition = 1
IPs, err := resolveRepeat(ctx, lookupIP, host, repetition)
switch {
case err != nil:
return nil, warnings, err
case len(IPs) == 0:
warning := fmt.Sprintf("no IP address found for host %q", host)
warnings = append(warnings, warning)
continue
case len(IPs) > 1:
warning := fmt.Sprintf("more than one IP address found for host %q", host)
warnings = append(warnings, warning)
}
subdomain := strings.TrimSuffix(host, ".vpn.privado.io")
parts := strings.Split(subdomain, "-")
const expectedParts = 2
if len(parts) != expectedParts {
warning := fmt.Sprintf("malformed subdomain %q: cannot find city and server number", subdomain)
warnings = append(warnings, warning)
continue
}
city, serverNumberString := parts[0], parts[1]
serverNumberInt, err := strconv.ParseInt(serverNumberString, 10, 16)
if err != nil {
warning := fmt.Sprintf("malformed server number %q: %s", serverNumberString, err)
warnings = append(warnings, warning)
continue
}
server := models.PrivadoServer{
City: city,
Number: uint16(serverNumberInt),
IP: IPs[0],
}
servers = append(servers, server)
}
sort.Slice(servers, func(i, j int) bool {
keyA := servers[i].City + fmt.Sprintf("%d", servers[i].Number)
keyB := servers[j].City + fmt.Sprintf("%d", servers[j].Number)
return keyA < keyB
})
return servers, warnings, nil
}
func stringifyPrivadoServers(servers []models.PrivadoServer) (s string) {
s = "func PrivadoServers() []models.PrivadoServer {\n"
s += " return []models.PrivadoServer{\n"
for _, server := range servers {
s += " " + server.String() + ",\n"
}
s += " }\n"
s += "}"
return s
}

View File

@@ -90,6 +90,16 @@ 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 {
u.logger.Error(err)
}
if ctx.Err() != nil {
return allServers, ctx.Err()
}
}
if u.options.Purevpn { if u.options.Purevpn {
u.logger.Info("updating PureVPN servers...") u.logger.Info("updating PureVPN servers...")
// TODO support servers offering only TCP or only UDP // TODO support servers offering only TCP or only UDP