Windscribe support (#114)
This commit is contained in:
@@ -38,7 +38,7 @@ ENV VPNSP=pia \
|
||||
TZ= \
|
||||
# PIA only
|
||||
PASSWORD= \
|
||||
REGION="CA Montreal" \
|
||||
REGION="Austria" \
|
||||
PIA_ENCRYPTION=strong \
|
||||
OPENVPN_CIPHER= \
|
||||
OPENVPN_AUTH= \
|
||||
@@ -48,6 +48,7 @@ ENV VPNSP=pia \
|
||||
COUNTRY=Sweden \
|
||||
CITY= \
|
||||
ISP= \
|
||||
# Mullvad and Windscribe only
|
||||
PORT= \
|
||||
# DNS over TLS
|
||||
DOT=on \
|
||||
|
||||
31
README.md
31
README.md
@@ -1,8 +1,8 @@
|
||||
# Private Internet Access Client
|
||||
# Gluetun VPN client
|
||||
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access or Mullvad VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and Tinyproxy*
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access, Mullvad and Windscribe VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and Tinyproxy*
|
||||
|
||||
**ANNOUNCEMENT**: *Support for [Mullvad](http://mullvad.net)*
|
||||
**ANNOUNCEMENT**: *Support for [Windscribe](https://windscribe.com/)*
|
||||
|
||||
<img height="200" src="title.svg?sanitize=true">
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
## Features
|
||||
|
||||
- Based on Alpine 3.11 for a small Docker image below 50MB
|
||||
- Supports **Private Internet Access** and **Mullvad** servers
|
||||
- Supports **Private Internet Access**, **Mullvad** and **Windscribe** servers
|
||||
- DNS over TLS baked in with service provider(s) of your choice
|
||||
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses
|
||||
- Choose the vpn network protocol, `udp` or `tcp`
|
||||
@@ -42,7 +42,7 @@
|
||||
- Built in HTTP proxy (Tinyproxy, tunnels TCP)
|
||||
- [Connect other containers to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
|
||||
- [Connect LAN devices to it](https://github.com/qdm12/private-internet-access-docker#connect-to-it)
|
||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, ppc64le and even that s390x 🎆
|
||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7 🎆
|
||||
|
||||
### Private Internet Access
|
||||
|
||||
@@ -55,6 +55,10 @@
|
||||
- Pick the [country, city and ISP](https://mullvad.net/en/servers/#openvpn)
|
||||
- Pick the port to use (i.e. `53` (udp) or `80` (tcp))
|
||||
|
||||
### Windscribe
|
||||
|
||||
- Pick the [region](https://windscribe.com/status)
|
||||
|
||||
### Extra niche features
|
||||
|
||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||
@@ -81,6 +85,8 @@
|
||||
- If `VPNSP=mullvad` and `PORT=53`, allow outbound UDP 53 to the corresponding VPN server IPs, which you can fine in [the mapping of Mullvad servers](https://github.com/qdm12/private-internet-access-docker/blob/master/internal/constants/mullvad.go#L64-L667)
|
||||
- If `VPNSP=mullvad` and `PORT=80`, allow outbound TCP 80 to the corresponding VPN server IPs, which you can fine in [the mapping of Mullvad servers](https://github.com/qdm12/private-internet-access-docker/blob/master/internal/constants/mullvad.go#L64-L667)
|
||||
- If `VPNSP=mullvad` and `PORT=443`, allow outbound TCP 443 to the corresponding VPN server IPs, which you can fine in [the mapping of Mullvad servers](https://github.com/qdm12/private-internet-access-docker/blob/master/internal/constants/mullvad.go#L64-L667)
|
||||
- If `VPNSP=windscribe` and `PROTOCOL=udp`: allow outbound UDP 443 to the corresponding VPN server IPs
|
||||
- If `VPNSP=windscribe` and `PROTOCOL=tcp`: allow outbound TCP 1194 to the corresponding VPN server IPs
|
||||
- If `SHADOWSOCKS=on`, allow inbound TCP 8388 and UDP 8388 from your LAN
|
||||
- If `TINYPROXY=on`, allow inbound TCP 8888 from your LAN
|
||||
|
||||
@@ -124,16 +130,16 @@ docker run --rm --network=container:pia alpine:3.11 wget -qO- https://ipinfo.io
|
||||
|
||||
| Environment variable | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `VPNSP` | `pia` | VPN Service Provider, one of `pia`, `mullvad` |
|
||||
| `REGION` | `CA Montreal` | (PIA only) one of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) |
|
||||
| `VPNSP` | `pia` | VPN Service Provider, one of `pia`, `mullvad` or `windscribe` |
|
||||
| `REGION` | `Austria` | (PIA & Windscribe only) one of the [PIA regions](https://www.privateinternetaccess.com/pages/network/) or one of the [Windscribe regions](https://windscribe.com/status) |
|
||||
| `COUNTRY` | `Sweden` | (Mullvad only) one of the [Mullvad countries](https://mullvad.net/en/servers/#openvpn) |
|
||||
| `CITY` | | (Mullvad only, *optional*) one of the [Mullvad cities](https://mullvad.net/en/servers/#openvpn) |
|
||||
| `ISP` | | (Mullvad only, *optional*) one of the [Mullvad ISP](https://mullvad.net/en/servers/#openvpn) |
|
||||
| `PORT` | | (Mullvad only, *optional*) For TCP, `80` or `443`, or `53` for UDP. Leave blank for default Mullvad server port |
|
||||
| `PORT` | | (Mullvad and Windscribe only, *optional*) **Mullvad**: For TCP, `80` or `443`, or `53` for UDP. Leave blank for default Mullvad server port; **Windscribe** see [this list of ports](https://windscribe.com/getconfig/openvpn) |
|
||||
| `PROTOCOL` | `udp` | `tcp` or `udp` |
|
||||
| `PIA_ENCRYPTION` | `strong` | (PIA only) `normal` or `strong` or `custom` |
|
||||
| `USER` | | PIA username **or** Mullvad user ID |
|
||||
| `PASSWORD` | | Your PIA password |
|
||||
| `ENCRYPTION` | `strong` | (PIA only) `normal` or `strong` |
|
||||
| `USER` | | PIA username **or** Mullvad user ID **or** Windscribe username |
|
||||
| `PASSWORD` | | Your PIA password **or** Windscribe password |
|
||||
| `DOT` | `on` | `on` or `off`, to activate DNS over TLS to 1.1.1.1 |
|
||||
| `DOT_PROVIDERS` | `cloudflare` | Comma delimited list of DNS over TLS providers from `cloudflare`, `google`, `quad9`, `quadrant`, `cleanbrowsing`, `securedns`, `libredns` |
|
||||
| `DOT_CACHING` | `on` | Unbound caching feature, `on` or `off` |
|
||||
@@ -161,7 +167,7 @@ docker run --rm --network=container:pia alpine:3.11 wget -qO- https://ipinfo.io
|
||||
| `TZ` | | Specify a timezone to use i.e. `Europe/London` |
|
||||
| `OPENVPN_VERBOSITY` | `1` | Openvpn verbosity level from 0 to 6 |
|
||||
| `OPENVPN_ROOT` | `no` | Run OpenVPN as root, `yes` or `no` |
|
||||
| `OPENVPN_TARGET_IP` | | Specify a target VPN server IP address to use, valid for Mullvad and Private Internet Access |
|
||||
| `OPENVPN_TARGET_IP` | | (Optional) Specify a target VPN server IP address to use, valid for Mullvad and Private Internet Access |
|
||||
| `OPENVPN_CIPHER` | | Specify a custom cipher to use, use at your own risk. It will also set `ncp-disable` if using AES GCM for PIA |
|
||||
| `OPENVPN_AUTH` | | Specify a custom auth algorithm to use (i.e. `sha256`) *for pia only* |
|
||||
|
||||
@@ -456,7 +462,6 @@ Thanks for all the contributions, whether small or not so small!
|
||||
|
||||
<details><summary>Expand me</summary><p>
|
||||
|
||||
- Support Windscribe
|
||||
- Gotify support for notificactions
|
||||
- Periodic update of malicious block lists with Unbound restart
|
||||
- Improve healthcheck
|
||||
|
||||
10
cmd/main.go
10
cmd/main.go
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/qdm12/private-internet-access-docker/internal/shadowsocks"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/splash"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/tinyproxy"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/windscribe"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -56,6 +57,7 @@ func main() {
|
||||
firewallConf := firewall.NewConfigurator(logger, fileManager)
|
||||
piaConf := pia.NewConfigurator(client, fileManager, firewallConf, logger)
|
||||
mullvadConf := mullvad.NewConfigurator(fileManager, logger)
|
||||
windscribeConf := windscribe.NewConfigurator(fileManager)
|
||||
tinyProxyConf := tinyproxy.NewConfigurator(fileManager, logger)
|
||||
shadowsocksConf := shadowsocks.NewConfigurator(fileManager, logger)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -86,6 +88,9 @@ func main() {
|
||||
case "mullvad":
|
||||
openVPNUser = allSettings.Mullvad.User
|
||||
openVPNPassword = "m"
|
||||
case "windscribe":
|
||||
openVPNUser = allSettings.Windscribe.User
|
||||
openVPNPassword = allSettings.Windscribe.Password
|
||||
}
|
||||
err = ovpnConf.WriteAuthFile(openVPNUser, openVPNPassword, uid, gid)
|
||||
e.FatalOnError(err)
|
||||
@@ -139,6 +144,11 @@ func main() {
|
||||
e.FatalOnError(err)
|
||||
err = mullvadConf.BuildConf(connections, allSettings.OpenVPN.Verbosity, uid, gid, allSettings.OpenVPN.Root, allSettings.OpenVPN.Cipher)
|
||||
e.FatalOnError(err)
|
||||
case "windscribe":
|
||||
connections, err = windscribeConf.GetOpenVPNConnections(allSettings.Windscribe.Region, allSettings.OpenVPN.NetworkProtocol, allSettings.Windscribe.Port, allSettings.OpenVPN.TargetIP)
|
||||
e.FatalOnError(err)
|
||||
err = windscribeConf.BuildConf(connections, allSettings.OpenVPN.Verbosity, uid, gid, allSettings.OpenVPN.Root, allSettings.OpenVPN.Cipher, allSettings.OpenVPN.Auth)
|
||||
e.FatalOnError(err)
|
||||
}
|
||||
|
||||
defaultInterface, defaultGateway, defaultSubnet, err := firewallConf.GetDefaultRoute()
|
||||
|
||||
@@ -1,57 +1,58 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
pia:
|
||||
image: qmcgaw/private-internet-access
|
||||
container_name: pia
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
network_mode: bridge
|
||||
init: true
|
||||
ports:
|
||||
- 8888:8888/tcp
|
||||
- 8388:8388/tcp
|
||||
- 8388:8388/udp
|
||||
# command:
|
||||
environment:
|
||||
# More variables are available, see the readme table
|
||||
- VPNSP=pia
|
||||
- USER=js89ds7
|
||||
- PROTOCOL=udp
|
||||
- OPENVPN_VERBOSITY=1
|
||||
- OPENVPN_ROOT=no
|
||||
- OPENVPN_TARGET_IP=
|
||||
- TZ=
|
||||
|
||||
# PIA only
|
||||
- REGION=CA Montreal
|
||||
- PASSWORD=8fd9s239G
|
||||
- PIA_ENCRYPTION=strong
|
||||
- PORT_FORWARDING=off
|
||||
- OPENVPN_CIPHER=
|
||||
- OPENVPN_AUTH=
|
||||
|
||||
# Mullvad only
|
||||
- COUNTRY=Sweden
|
||||
- CITY=
|
||||
- ISP=
|
||||
- PORT=
|
||||
|
||||
# DNS over TLS
|
||||
- DOT=on
|
||||
- DOT_PROVIDERS=cloudflare
|
||||
- DOT_IPV6=on
|
||||
- DOT_VERBOSITY=1
|
||||
- BLOCK_MALICIOUS=on
|
||||
- BLOCK_SURVEILLANCE=off
|
||||
- BLOCK_ADS=off
|
||||
- UNBLOCK=
|
||||
# Firewall
|
||||
- EXTRA_SUBNETS=
|
||||
# Shadowsocks
|
||||
- SHADOWSOCKS=off
|
||||
- SHADOWSOCKS_PASSWORD=
|
||||
# Tinyproxy
|
||||
- TINYPROXY=off
|
||||
- TINYPROXY_USER=
|
||||
- TINYPROXY_PASSWORD=
|
||||
restart: always
|
||||
version: "3.7"
|
||||
services:
|
||||
pia:
|
||||
image: qmcgaw/private-internet-access
|
||||
container_name: pia
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
network_mode: bridge
|
||||
init: true
|
||||
ports:
|
||||
- 8888:8888/tcp
|
||||
- 8388:8388/tcp
|
||||
- 8388:8388/udp
|
||||
# command:
|
||||
environment:
|
||||
# More variables are available, see the readme table
|
||||
- VPNSP=pia
|
||||
- USER=js89ds7
|
||||
- PROTOCOL=udp
|
||||
- OPENVPN_VERBOSITY=1
|
||||
- OPENVPN_ROOT=no
|
||||
- OPENVPN_TARGET_IP=
|
||||
- TZ=
|
||||
|
||||
# PIA and Windscribe only
|
||||
- REGION=Austria
|
||||
- PASSWORD=8fd9s239G
|
||||
|
||||
# PIA only
|
||||
- PASSWORD=8fd9s239G
|
||||
- PIA_ENCRYPTION=strong
|
||||
- PORT_FORWARDING=off
|
||||
|
||||
# Mullvad only
|
||||
- COUNTRY=Sweden
|
||||
- CITY=
|
||||
- ISP=
|
||||
- PORT=
|
||||
|
||||
# DNS over TLS
|
||||
- DOT=on
|
||||
- DOT_PROVIDERS=cloudflare
|
||||
- DOT_IPV6=on
|
||||
- DOT_VERBOSITY=1
|
||||
- BLOCK_MALICIOUS=on
|
||||
- BLOCK_SURVEILLANCE=off
|
||||
- BLOCK_ADS=off
|
||||
- UNBLOCK=
|
||||
# Firewall
|
||||
- EXTRA_SUBNETS=
|
||||
# Shadowsocks
|
||||
- SHADOWSOCKS=off
|
||||
- SHADOWSOCKS_PASSWORD=
|
||||
# Tinyproxy
|
||||
- TINYPROXY=off
|
||||
- TINYPROXY_USER=
|
||||
- TINYPROXY_PASSWORD=
|
||||
restart: always
|
||||
|
||||
@@ -2,9 +2,9 @@ package constants
|
||||
|
||||
const (
|
||||
// Announcement is a message announcement
|
||||
Announcement = "Support for Mullvad (a bit unstable)"
|
||||
Announcement = "Support for Windscribe"
|
||||
// AnnouncementExpiration is the expiration date of the announcement in format yyyy-mm-dd
|
||||
AnnouncementExpiration = "2020-03-15"
|
||||
AnnouncementExpiration = "2020-04-15"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
310
internal/constants/windscribe.go
Normal file
310
internal/constants/windscribe.go
Normal file
@@ -0,0 +1,310 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"github.com/qdm12/private-internet-access-docker/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
WindscribeCertificate = "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"
|
||||
WindscribeOpenvpnStaticKeyV1 = "5801926a57ac2ce27e3dfd1dd6ef82042d82bd4f3f0021296f57734f6f1ea714a6623845541c4b0c3dea0a050fe6746cb66dfab14cda27e5ae09d7c155aa554f399fa4a863f0e8c1af787e5c602a801d3a2ec41e395a978d56729457fe6102d7d9e9119aa83643210b33c678f9d4109e3154ac9c759e490cb309b319cf708cae83ddadc3060a7a26564d1a24411cd552fe6620ea16b755697a4fc5e6e9d0cfc0c5c4a1874685429046a424c026db672e4c2c492898052ba59128d46200b40f880027a8b6610a4d559bdc9346d33a0a6b08e75c7fd43192b162bfd0aef0c716b31584827693f676f9a5047123466f0654eade34972586b31c6ce7e395f4b478cb"
|
||||
)
|
||||
|
||||
func WindscribeRegionChoices() (choices []string) {
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range WindscribeServers() {
|
||||
uniqueChoices[string(server.Region)] = struct{}{}
|
||||
}
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func WindscribeServers() []models.WindscribeServer {
|
||||
return []models.WindscribeServer{
|
||||
{
|
||||
Region: models.WindscribeRegion("albania"),
|
||||
Subdomain: "al",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("argentina"),
|
||||
Subdomain: "ar",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("argentina"),
|
||||
Subdomain: "ar",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("australia"),
|
||||
Subdomain: "au",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("austria"),
|
||||
Subdomain: "at",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("azerbaijan"),
|
||||
Subdomain: "az",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("belgium"),
|
||||
Subdomain: "be",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("bosnia"),
|
||||
Subdomain: "ba",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("brazil"),
|
||||
Subdomain: "br",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("bulgaria"),
|
||||
Subdomain: "bg",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("canada east"),
|
||||
Subdomain: "ca",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("canada west"),
|
||||
Subdomain: "ca-west",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("colombia"),
|
||||
Subdomain: "co",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("croatia"),
|
||||
Subdomain: "hr",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("cyprus"),
|
||||
Subdomain: "cy",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("czech republic"),
|
||||
Subdomain: "cz",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("denmark"),
|
||||
Subdomain: "dk",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("estonia"),
|
||||
Subdomain: "ee",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("egypt"),
|
||||
Subdomain: "eg",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("fake antarctica"),
|
||||
Subdomain: "aq",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("finland"),
|
||||
Subdomain: "fi",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("france"),
|
||||
Subdomain: "fr",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("georgia"),
|
||||
Subdomain: "ge",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("germany"),
|
||||
Subdomain: "de",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("greece"),
|
||||
Subdomain: "gr",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("hong kong"),
|
||||
Subdomain: "hk",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("hungary"),
|
||||
Subdomain: "hu",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("iceland"),
|
||||
Subdomain: "is",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("india"),
|
||||
Subdomain: "in",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("indonesia"),
|
||||
Subdomain: "id",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("ireland"),
|
||||
Subdomain: "ie",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("israel"),
|
||||
Subdomain: "il",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("italy"),
|
||||
Subdomain: "it",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("japan"),
|
||||
Subdomain: "jp",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("latvia"),
|
||||
Subdomain: "lv",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("lithuania"),
|
||||
Subdomain: "lt",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("macedonia"),
|
||||
Subdomain: "mk",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("malaysia"),
|
||||
Subdomain: "my",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("mexico"),
|
||||
Subdomain: "mx",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("moldova"),
|
||||
Subdomain: "md",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("netherlands"),
|
||||
Subdomain: "nl",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("new zealand"),
|
||||
Subdomain: "nz",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("norway"),
|
||||
Subdomain: "no",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("philippines"),
|
||||
Subdomain: "ph",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("poland"),
|
||||
Subdomain: "pl",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("portugal"),
|
||||
Subdomain: "pt",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("romania"),
|
||||
Subdomain: "ro",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("russia"),
|
||||
Subdomain: "ru",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("serbia"),
|
||||
Subdomain: "rs",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("singapore"),
|
||||
Subdomain: "sg",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("slovakia"),
|
||||
Subdomain: "sk",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("slovenia"),
|
||||
Subdomain: "si",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("south africa"),
|
||||
Subdomain: "za",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("south korea"),
|
||||
Subdomain: "kr",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("spain"),
|
||||
Subdomain: "es",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("sweden"),
|
||||
Subdomain: "se",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("switzerland"),
|
||||
Subdomain: "ch",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("thailand"),
|
||||
Subdomain: "th",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("tunisia"),
|
||||
Subdomain: "tn",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("turkey"),
|
||||
Subdomain: "tr",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("ukraine"),
|
||||
Subdomain: "ua",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("united arab emirates"),
|
||||
Subdomain: "ae",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("united kingdom"),
|
||||
Subdomain: "uk",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("us central"),
|
||||
Subdomain: "us-central",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("us east"),
|
||||
Subdomain: "us-east",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("us west"),
|
||||
Subdomain: "us-west",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("vietnam"),
|
||||
Subdomain: "vn",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("windflix ca"),
|
||||
Subdomain: "wf-ca",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("windflix jp"),
|
||||
Subdomain: "wf-jp",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("windflix uk"),
|
||||
Subdomain: "wf-uk",
|
||||
},
|
||||
{
|
||||
Region: models.WindscribeRegion("windflix us"),
|
||||
Subdomain: "wf-us",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ type (
|
||||
MullvadCity string
|
||||
// MullvadProvider is used as the Internet service provider for a Mullvad server
|
||||
MullvadProvider string
|
||||
// WindscribeCity is used as the region for a Windscribe server
|
||||
WindscribeRegion string
|
||||
// URL is an HTTP(s) URL address
|
||||
URL string
|
||||
// Filepath is a local filesytem file path
|
||||
|
||||
6
internal/models/windscribe.go
Normal file
6
internal/models/windscribe.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
type WindscribeServer struct {
|
||||
Region WindscribeRegion
|
||||
Subdomain string
|
||||
}
|
||||
@@ -53,6 +53,10 @@ type ParamsReader interface {
|
||||
GetMullvadISP() (country models.MullvadProvider, err error)
|
||||
GetMullvadPort() (port uint16, err error)
|
||||
|
||||
// Windscribe getters
|
||||
GetWindscribeRegion() (country models.WindscribeRegion, err error)
|
||||
GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error)
|
||||
|
||||
// Shadowsocks getters
|
||||
GetShadowSocks() (activated bool, err error)
|
||||
GetShadowSocksLog() (activated bool, err error)
|
||||
@@ -92,6 +96,6 @@ func NewParamsReader(logger logging.Logger) ParamsReader {
|
||||
|
||||
// GetVPNSP obtains the VPN service provider to use from the environment variable VPNSP
|
||||
func (p *paramsReader) GetVPNSP() (vpnServiceProvider string, err error) {
|
||||
s, err := p.envParams.GetValueIfInside("VPNSP", []string{"pia", "mullvad"})
|
||||
s, err := p.envParams.GetValueIfInside("VPNSP", []string{"pia", "mullvad", "windscribe"})
|
||||
return s, err
|
||||
}
|
||||
|
||||
45
internal/params/windscribe.go
Normal file
45
internal/params/windscribe.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package params
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
libparams "github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/constants"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/models"
|
||||
)
|
||||
|
||||
// GetWindscribeRegion obtains the region for the Windscribe server from the
|
||||
// environment variable REGION
|
||||
func (p *paramsReader) GetWindscribeRegion() (country models.WindscribeRegion, err error) {
|
||||
choices := append(constants.WindscribeRegionChoices())
|
||||
s, err := p.envParams.GetValueIfInside("REGION", choices)
|
||||
return models.WindscribeRegion(strings.ToLower(s)), err
|
||||
}
|
||||
|
||||
// GetMullvadPort obtains the port to reach the Mullvad server on from the
|
||||
// environment variable PORT
|
||||
func (p *paramsReader) GetWindscribePort(protocol models.NetworkProtocol) (port uint16, err error) {
|
||||
n, err := p.envParams.GetEnvIntRange("PORT", 0, 65535, libparams.Default("0"))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
switch protocol {
|
||||
case constants.TCP:
|
||||
switch n {
|
||||
case 21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783:
|
||||
default:
|
||||
return 0, fmt.Errorf("port %d is not valid for protocol %s", n, protocol)
|
||||
}
|
||||
case constants.UDP:
|
||||
switch n {
|
||||
case 53, 80, 123, 443, 1194, 54783:
|
||||
default:
|
||||
return 0, fmt.Errorf("port %d is not valid for protocol %s", n, protocol)
|
||||
}
|
||||
}
|
||||
return uint16(n), nil
|
||||
}
|
||||
@@ -13,6 +13,7 @@ type Settings struct {
|
||||
OpenVPN OpenVPN
|
||||
PIA PIA
|
||||
Mullvad Mullvad
|
||||
Windscribe Windscribe
|
||||
DNS DNS
|
||||
Firewall Firewall
|
||||
TinyProxy TinyProxy
|
||||
@@ -26,6 +27,8 @@ func (s *Settings) String() string {
|
||||
vpnServiceProvider = s.PIA.String()
|
||||
case "mullvad":
|
||||
vpnServiceProvider = s.Mullvad.String()
|
||||
case "windscribe":
|
||||
vpnServiceProvider = s.Windscribe.String()
|
||||
}
|
||||
return strings.Join([]string{
|
||||
"Settings summary below:",
|
||||
@@ -75,8 +78,23 @@ func GetAllSettings(params params.ParamsReader) (settings Settings, err error) {
|
||||
return settings, fmt.Errorf("auth algorithm %q is not supported by Mullvad (not using auth at all)", settings.OpenVPN.Auth)
|
||||
}
|
||||
settings.Mullvad, err = GetMullvadSettings(params)
|
||||
case "windscribe":
|
||||
switch settings.OpenVPN.Cipher {
|
||||
case "", "aes-256-cbc", "aes-256-gcm": // TODO check inside params getters
|
||||
default:
|
||||
return settings, fmt.Errorf("cipher %q is not supported by Windscribe", settings.OpenVPN.Cipher)
|
||||
}
|
||||
switch settings.OpenVPN.Auth {
|
||||
case "", "sha512":
|
||||
default:
|
||||
return settings, fmt.Errorf("auth algorithm %q is not supported by Windscribe", settings.OpenVPN.Auth)
|
||||
}
|
||||
settings.Windscribe, err = GetWindscribeSettings(params, settings.OpenVPN.NetworkProtocol)
|
||||
default:
|
||||
return settings, fmt.Errorf("VPN service provider %q is not valid", settings.VPNSP)
|
||||
err = fmt.Errorf("VPN service provider %q is not valid", settings.VPNSP)
|
||||
}
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
if err != nil {
|
||||
return settings, err
|
||||
|
||||
49
internal/settings/windscribe.go
Normal file
49
internal/settings/windscribe.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/private-internet-access-docker/internal/models"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/params"
|
||||
)
|
||||
|
||||
// Windscribe contains the settings to connect to a Windscribe server
|
||||
type Windscribe struct {
|
||||
User string
|
||||
Password string
|
||||
Region models.WindscribeRegion
|
||||
Port uint16
|
||||
}
|
||||
|
||||
func (w *Windscribe) String() string {
|
||||
settingsList := []string{
|
||||
"Windscribe settings:",
|
||||
"User: [redacted]",
|
||||
"Password: [redacted]",
|
||||
"Region: " + string(w.Region),
|
||||
"Custom port: " + fmt.Sprintf("%d", w.Port),
|
||||
}
|
||||
return strings.Join(settingsList, "\n |--")
|
||||
}
|
||||
|
||||
// GetWindscribeSettings obtains Windscribe settings from environment variables using the params package.
|
||||
func GetWindscribeSettings(params params.ParamsReader, protocol models.NetworkProtocol) (settings Windscribe, err error) {
|
||||
settings.User, err = params.GetUser()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
settings.Password, err = params.GetPassword()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
settings.Region, err = params.GetWindscribeRegion()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
settings.Port, err = params.GetWindscribePort(protocol)
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
118
internal/windscribe/conf.go
Normal file
118
internal/windscribe/conf.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package windscribe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/golibs/files"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/constants"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/models"
|
||||
)
|
||||
|
||||
func (c *configurator) GetOpenVPNConnections(region models.WindscribeRegion, protocol models.NetworkProtocol, customPort uint16, targetIP net.IP) (connections []models.OpenVPNConnection, err error) {
|
||||
var subdomain string
|
||||
for _, server := range constants.WindscribeServers() {
|
||||
if server.Region == region {
|
||||
subdomain = server.Subdomain
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(subdomain) == 0 {
|
||||
return nil, fmt.Errorf("no server found for region %q", region)
|
||||
}
|
||||
hostname := subdomain + ".windscribe.com"
|
||||
IPs, err := c.lookupIP(hostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if targetIP != nil {
|
||||
found := false
|
||||
for i := range IPs {
|
||||
if IPs[i].Equal(targetIP) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, fmt.Errorf("target IP address %q not found from IP addresses resolved from %s", targetIP, hostname)
|
||||
}
|
||||
IPs = []net.IP{targetIP}
|
||||
}
|
||||
var port uint16
|
||||
switch {
|
||||
case customPort > 0:
|
||||
port = customPort
|
||||
case protocol == constants.TCP:
|
||||
port = 1194
|
||||
case protocol == constants.UDP:
|
||||
port = 443
|
||||
default:
|
||||
return nil, fmt.Errorf("protocol %q is unknown", protocol)
|
||||
}
|
||||
for _, IP := range IPs {
|
||||
connections = append(connections, models.OpenVPNConnection{IP: IP, Port: port, Protocol: protocol})
|
||||
}
|
||||
return connections, nil
|
||||
}
|
||||
|
||||
func (c *configurator) BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string) (err error) {
|
||||
if len(cipher) == 0 {
|
||||
cipher = "AES-256-CBC"
|
||||
}
|
||||
if len(auth) == 0 {
|
||||
auth = "sha512"
|
||||
}
|
||||
lines := []string{
|
||||
"client",
|
||||
"dev tun",
|
||||
"nobind",
|
||||
"persist-key",
|
||||
"persist-tun",
|
||||
|
||||
// Windscribe specific
|
||||
"resolv-retry infinite",
|
||||
"comp-lzo",
|
||||
"remote-cert-tls server",
|
||||
"key-direction 1",
|
||||
|
||||
// Added constant values
|
||||
"auth-nocache",
|
||||
"mute-replay-warnings",
|
||||
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
|
||||
"auth-retry nointeract",
|
||||
"remote-random",
|
||||
|
||||
// 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),
|
||||
fmt.Sprintf("auth %s", auth),
|
||||
}
|
||||
if strings.HasSuffix(cipher, "-gcm") {
|
||||
lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM")
|
||||
}
|
||||
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{
|
||||
"<ca>",
|
||||
"-----BEGIN CERTIFICATE-----",
|
||||
constants.WindscribeCertificate,
|
||||
"-----END CERTIFICATE-----",
|
||||
"</ca>",
|
||||
}...)
|
||||
lines = append(lines, []string{
|
||||
"<tls-auth>",
|
||||
"-----BEGIN OpenVPN Static key V1-----",
|
||||
constants.WindscribeOpenvpnStaticKeyV1,
|
||||
"-----END OpenVPN Static key V1-----",
|
||||
"</tls-auth>",
|
||||
"",
|
||||
}...)
|
||||
return c.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines, files.Ownership(uid, gid), files.Permissions(0400))
|
||||
}
|
||||
24
internal/windscribe/windscribe.go
Normal file
24
internal/windscribe/windscribe.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package windscribe
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/golibs/files"
|
||||
"github.com/qdm12/private-internet-access-docker/internal/models"
|
||||
)
|
||||
|
||||
// Configurator contains methods to download, read and modify the openvpn configuration to connect as a client
|
||||
type Configurator interface {
|
||||
GetOpenVPNConnections(region models.WindscribeRegion, protocol models.NetworkProtocol, customPort uint16, targetIP net.IP) (connections []models.OpenVPNConnection, err error)
|
||||
BuildConf(connections []models.OpenVPNConnection, verbosity, uid, gid int, root bool, cipher, auth string) (err error)
|
||||
}
|
||||
|
||||
type configurator struct {
|
||||
fileManager files.FileManager
|
||||
lookupIP func(host string) ([]net.IP, error)
|
||||
}
|
||||
|
||||
// NewConfigurator returns a new Configurator object
|
||||
func NewConfigurator(fileManager files.FileManager) Configurator {
|
||||
return &configurator{fileManager, net.LookupIP}
|
||||
}
|
||||
Reference in New Issue
Block a user