-- Support Windscribe
- Gotify support for notificactions
- Periodic update of malicious block lists with Unbound restart
- Improve healthcheck
diff --git a/cmd/main.go b/cmd/main.go
index d8bcc504..7cc718d6 100644
--- a/cmd/main.go
+++ b/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()
diff --git a/docker-compose.yml b/docker-compose.yml
index fa89efdd..225ecf32 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -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
diff --git a/internal/constants/splash.go b/internal/constants/splash.go
index 44b799fb..ea95515c 100644
--- a/internal/constants/splash.go
+++ b/internal/constants/splash.go
@@ -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 (
diff --git a/internal/constants/windscribe.go b/internal/constants/windscribe.go
new file mode 100644
index 00000000..e6f17f9c
--- /dev/null
+++ b/internal/constants/windscribe.go
@@ -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",
+ },
+ }
+}
diff --git a/internal/models/alias.go b/internal/models/alias.go
index 08d87d8d..d7b46e9e 100644
--- a/internal/models/alias.go
+++ b/internal/models/alias.go
@@ -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
diff --git a/internal/models/windscribe.go b/internal/models/windscribe.go
new file mode 100644
index 00000000..2247ab7d
--- /dev/null
+++ b/internal/models/windscribe.go
@@ -0,0 +1,6 @@
+package models
+
+type WindscribeServer struct {
+ Region WindscribeRegion
+ Subdomain string
+}
diff --git a/internal/params/params.go b/internal/params/params.go
index 350e3859..4250ef11 100644
--- a/internal/params/params.go
+++ b/internal/params/params.go
@@ -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
}
diff --git a/internal/params/windscribe.go b/internal/params/windscribe.go
new file mode 100644
index 00000000..76fa1e41
--- /dev/null
+++ b/internal/params/windscribe.go
@@ -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
+}
diff --git a/internal/settings/settings.go b/internal/settings/settings.go
index 00b4a079..aeca8c38 100644
--- a/internal/settings/settings.go
+++ b/internal/settings/settings.go
@@ -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
diff --git a/internal/settings/windscribe.go b/internal/settings/windscribe.go
new file mode 100644
index 00000000..a5c58c2b
--- /dev/null
+++ b/internal/settings/windscribe.go
@@ -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
+}
diff --git a/internal/windscribe/conf.go b/internal/windscribe/conf.go
new file mode 100644
index 00000000..306ed93e
--- /dev/null
+++ b/internal/windscribe/conf.go
@@ -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{
+ "