15
README.md
15
README.md
@@ -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.
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
206
internal/constants/privado.go
Normal file
206
internal/constants/privado.go
Normal 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}},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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!>",
|
||||||
|
|||||||
@@ -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{"), "}")
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
37
internal/params/privado.go
Normal file
37
internal/params/privado.go
Normal 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
|
||||||
|
}
|
||||||
@@ -2,4 +2,5 @@ package provider
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
aes256cbc = "aes-256-cbc"
|
aes256cbc = "aes-256-cbc"
|
||||||
|
sha256 = "sha256"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
144
internal/provider/privado.go
Normal file
144
internal/provider/privado.go
Normal 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")
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)",
|
||||||
|
|||||||
@@ -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) +
|
||||||
|
|||||||
@@ -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
114
internal/updater/privado.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user