Windscribe support (#114)

This commit is contained in:
Quentin McGaw
2020-03-29 16:42:06 -04:00
committed by GitHub
parent 643745d33e
commit 76cea56864
14 changed files with 668 additions and 75 deletions

View File

@@ -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 \

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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 (

View 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",
},
}
}

View File

@@ -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

View File

@@ -0,0 +1,6 @@
package models
type WindscribeServer struct {
Region WindscribeRegion
Subdomain string
}

View File

@@ -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
}

View 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
}

View File

@@ -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

View 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
View 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))
}

View 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}
}