Maint: refactor VPN configuration structure

- Paves the way for Wireguard
- VPN struct contains Type, Openvpn and Provider configurations
- OpenVPN specific options (e.g. client key) moved from Provider to Openvpn configuration struct
- Move Provider configuration from OpenVPN configuration to VPN
- HTTP control server returns only openvpn settings (not provider settings)
This commit is contained in:
Quentin McGaw (desktop)
2021-08-17 15:44:11 +00:00
parent a00de75f61
commit cc2235653a
39 changed files with 372 additions and 301 deletions

View File

@@ -318,7 +318,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings)
portForwardLogger := logger.NewChild(logging.Settings{Prefix: "port forwarding: "})
portForwardLooper := portforward.NewLoop(allSettings.OpenVPN.Provider.PortForwarding,
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
httpClient, firewallConf, portForwardLogger)
portForwardHandler, portForwardCtx, portForwardDone := goshutdown.NewGoRoutineHandler(
"port forwarding", goshutdown.GoRoutineSettings{Timeout: time.Second})
@@ -352,7 +352,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
tickersGroupHandler.Add(pubIPTickerHandler)
openvpnLogger := logger.NewChild(logging.Settings{Prefix: "openvpn: "})
openvpnLooper := openvpn.NewLoop(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
openvpnLooper := openvpn.NewLoop(allSettings.VPN.OpenVPN,
allSettings.VPN.Provider, nonRootUsername, puid, pgid, allServers,
ovpnConf, firewallConf, routingConf, portForwardLooper, publicIPLooper, unboundLooper,
openvpnLogger, httpClient, buildInfo, allSettings.VersionInformation)
openvpnHandler, openvpnCtx, openvpnDone := goshutdown.NewGoRoutineHandler(

View File

@@ -28,12 +28,12 @@ func (c *CLI) OpenvpnConfig(logger logging.Logger) error {
if err != nil {
return err
}
providerConf := provider.New(allSettings.OpenVPN.Provider.Name, allServers, time.Now)
connection, err := providerConf.GetOpenVPNConnection(allSettings.OpenVPN.Provider.ServerSelection)
providerConf := provider.New(allSettings.VPN.Provider.Name, allServers, time.Now)
connection, err := providerConf.GetOpenVPNConnection(allSettings.VPN.Provider.ServerSelection)
if err != nil {
return err
}
lines := providerConf.BuildConf(connection, "nonroortuser", allSettings.OpenVPN)
lines := providerConf.BuildConf(connection, "nonrootuser", allSettings.VPN.OpenVPN)
fmt.Println(strings.Join(lines, "\n"))
return nil
}

View File

@@ -17,14 +17,6 @@ func (settings *Provider) cyberghostLines() (lines []string) {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ExtraConfigOptions.ClientKey != "" {
lines = append(lines, lastIndent+"Client key is set")
}
if settings.ExtraConfigOptions.ClientCertificate != "" {
lines = append(lines, lastIndent+"Client certificate is set")
}
return lines
}
@@ -41,16 +33,6 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
return err
}
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
if err != nil {
return err
}
settings.ServerSelection.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP",
constants.CyberghostGroupChoices())
if err != nil {
@@ -69,3 +51,17 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
return nil
}
func (settings *OpenVPN) readCyberghost(r reader) (err error) {
settings.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ClientCrt, err = readClientCertificate(r)
if err != nil {
return err
}
return nil
}

View File

@@ -29,10 +29,6 @@ func (settings *Provider) mullvadLines() (lines []string) {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
if settings.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, lastIndent+"IPv6: enabled")
}
return lines
}
@@ -80,10 +76,5 @@ func (settings *Provider) readMullvad(r reader) (err error) {
return fmt.Errorf("environment variable OWNED: %w", err)
}
settings.ExtraConfigOptions.OpenVPNIPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
}
return nil
}

View File

@@ -1,7 +1,6 @@
package configuration
import (
"errors"
"fmt"
"strconv"
"strings"
@@ -20,9 +19,12 @@ type OpenVPN struct {
Root bool `json:"run_as_root"`
Cipher string `json:"cipher"`
Auth string `json:"auth"`
Provider Provider `json:"provider"`
Config string `json:"custom_config"`
Version string `json:"version"`
ClientCrt string `json:"-"` // Cyberghost
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
EncPreset string `json:"encryption_preset"` // PIA
IPv6 bool `json:"ipv6"` // Mullvad
}
func (settings *OpenVPN) String() string {
@@ -55,48 +57,32 @@ func (settings *OpenVPN) lines() (lines []string) {
lines = append(lines, indent+lastIndent+"Custom configuration: "+settings.Config)
}
if settings.Provider.Name == "" {
lines = append(lines, indent+lastIndent+"Provider: custom configuration")
} else {
lines = append(lines, indent+lastIndent+"Provider:")
for _, line := range settings.Provider.lines() {
lines = append(lines, indent+indent+line)
if settings.ClientKey != "" {
lines = append(lines, indent+lastIndent+"Client key is set")
}
if settings.ClientCrt != "" {
lines = append(lines, indent+lastIndent+"Client certificate is set")
}
if settings.IPv6 {
lines = append(lines, indent+lastIndent+"IPv6: enabled")
}
if settings.EncPreset != "" { // PIA only
lines = append(lines, indent+lastIndent+"Encryption preset: "+settings.EncPreset)
}
return lines
}
var (
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
)
func (settings *OpenVPN) read(r reader) (err error) {
vpnsp, err := r.env.Inside("VPNSP", []string{
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"},
params.Default("private internet access"))
if err != nil {
return fmt.Errorf("environment variable VPNSP: %w", err)
}
if vpnsp == "pia" { // retro compatibility
vpnsp = "private internet access"
}
settings.Provider.Name = vpnsp
func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
settings.Config, err = r.env.Get("OPENVPN_CUSTOM_CONFIG", params.CaseSensitiveValue())
if err != nil {
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
}
customConfig := settings.Config != ""
if customConfig {
settings.Provider.Name = ""
}
credentialsRequired := !customConfig && settings.Provider.Name != constants.VPNUnlimited
credentialsRequired := settings.Config == "" && serviceProvider != constants.VPNUnlimited
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
if err != nil {
@@ -105,7 +91,7 @@ func (settings *OpenVPN) read(r reader) (err error) {
// Remove spaces in user ID to simplify user's life, thanks @JeordyR
settings.User = strings.ReplaceAll(settings.User, " ", "")
if settings.Provider.Name == constants.Mullvad {
if serviceProvider == constants.Mullvad {
settings.Password = "m"
} else {
settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", credentialsRequired, []string{"PASSWORD"})
@@ -155,50 +141,23 @@ func (settings *OpenVPN) read(r reader) (err error) {
return fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err)
}
settings.MSSFix = uint16(mssFix)
return settings.readProvider(r)
settings.IPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
}
func (settings *OpenVPN) readProvider(r reader) error {
var readProvider func(r reader) error
switch settings.Provider.Name {
case "": // custom config
readProvider = func(r reader) error { return nil }
switch serviceProvider {
case constants.Cyberghost:
readProvider = settings.Provider.readCyberghost
case constants.Fastestvpn:
readProvider = settings.Provider.readFastestvpn
case constants.HideMyAss:
readProvider = settings.Provider.readHideMyAss
case constants.Ipvanish:
readProvider = settings.Provider.readIpvanish
case constants.Ivpn:
readProvider = settings.Provider.readIvpn
case constants.Mullvad:
readProvider = settings.Provider.readMullvad
case constants.Nordvpn:
readProvider = settings.Provider.readNordvpn
case constants.Privado:
readProvider = settings.Provider.readPrivado
err = settings.readCyberghost(r)
case constants.PrivateInternetAccess:
readProvider = settings.Provider.readPrivateInternetAccess
case constants.Privatevpn:
readProvider = settings.Provider.readPrivatevpn
case constants.Protonvpn:
readProvider = settings.Provider.readProtonvpn
case constants.Purevpn:
readProvider = settings.Provider.readPurevpn
case constants.Surfshark:
readProvider = settings.Provider.readSurfshark
case constants.Torguard:
readProvider = settings.Provider.readTorguard
err = settings.readPrivateInternetAccess(r)
case constants.VPNUnlimited:
readProvider = settings.Provider.readVPNUnlimited
case constants.Vyprvpn:
readProvider = settings.Provider.readVyprvpn
case constants.Windscribe:
readProvider = settings.Provider.readWindscribe
default:
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
err = settings.readVPNUnlimited(r)
}
return readProvider(r)
if err != nil {
return err
}
return nil
}

View File

@@ -13,9 +13,6 @@ func Test_OpenVPN_JSON(t *testing.T) {
in := OpenVPN{
Root: true,
Flags: []string{},
Provider: Provider{
Name: "name",
},
}
data, err := json.MarshalIndent(in, "", " ")
require.NoError(t, err)
@@ -28,35 +25,10 @@ func Test_OpenVPN_JSON(t *testing.T) {
"run_as_root": true,
"cipher": "",
"auth": "",
"provider": {
"name": "name",
"server_selection": {
"tcp": false,
"regions": null,
"groups": null,
"countries": null,
"cities": null,
"hostnames": null,
"names": null,
"isps": null,
"owned": false,
"custom_port": 0,
"numbers": null,
"encryption_preset": "",
"free_only": false,
"stream_only": false
},
"extra_config": {
"encryption_preset": "",
"openvpn_ipv6": false
},
"port_forwarding": {
"enabled": false,
"filepath": ""
}
},
"custom_config": "",
"version": ""
"version": "",
"encryption_preset": "",
"ipv6": false
}`, string(data))
var out OpenVPN
err = json.Unmarshal(data, &out)

View File

@@ -21,8 +21,6 @@ func (settings *Provider) privateinternetaccessLines() (lines []string) {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
}
lines = append(lines, lastIndent+"Encryption preset: "+settings.ServerSelection.EncryptionPreset)
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
@@ -50,17 +48,6 @@ func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
return err
}
encryptionPreset, err := r.env.Inside("PIA_ENCRYPTION",
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
params.Default(constants.PIACertificateStrong),
)
if err != nil {
return fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
}
settings.ServerSelection.EncryptionPreset = encryptionPreset
settings.ExtraConfigOptions.EncryptionPreset = encryptionPreset
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices())
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
@@ -81,6 +68,11 @@ func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
return fmt.Errorf("environment variable PORT: %w", err)
}
settings.ServerSelection.EncryptionPreset, err = getPIAEncryptionPreset(r)
if err != nil {
return err
}
settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off"))
if err != nil {
return fmt.Errorf("environment variable PORT_FORWARDING: %w", err)
@@ -96,3 +88,20 @@ func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
return nil
}
func (settings *OpenVPN) readPrivateInternetAccess(r reader) (err error) {
settings.EncPreset, err = getPIAEncryptionPreset(r)
return err
}
func getPIAEncryptionPreset(r reader) (encryptionPreset string, err error) {
encryptionPreset, err = r.env.Inside("PIA_ENCRYPTION",
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
params.Default(constants.PIACertificateStrong),
)
if err != nil {
return "", fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
}
return encryptionPreset, nil
}

View File

@@ -1,6 +1,7 @@
package configuration
import (
"errors"
"fmt"
"net"
"strings"
@@ -13,7 +14,6 @@ import (
type Provider struct {
Name string `json:"name"`
ServerSelection ServerSelection `json:"server_selection"`
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
PortForwarding PortForwarding `json:"port_forwarding"`
}
@@ -76,6 +76,79 @@ func (settings *Provider) lines() (lines []string) {
return lines
}
var (
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
)
func (settings *Provider) read(r reader) error {
err := settings.readVPNServiceProvider(r)
if err != nil {
return err
}
var readProvider func(r reader) error
switch settings.Name {
case "": // custom config
readProvider = func(r reader) error { return nil }
case constants.Cyberghost:
readProvider = settings.readCyberghost
case constants.Fastestvpn:
readProvider = settings.readFastestvpn
case constants.HideMyAss:
readProvider = settings.readHideMyAss
case constants.Ipvanish:
readProvider = settings.readIpvanish
case constants.Ivpn:
readProvider = settings.readIvpn
case constants.Mullvad:
readProvider = settings.readMullvad
case constants.Nordvpn:
readProvider = settings.readNordvpn
case constants.Privado:
readProvider = settings.readPrivado
case constants.PrivateInternetAccess:
readProvider = settings.readPrivateInternetAccess
case constants.Privatevpn:
readProvider = settings.readPrivatevpn
case constants.Protonvpn:
readProvider = settings.readProtonvpn
case constants.Purevpn:
readProvider = settings.readPurevpn
case constants.Surfshark:
readProvider = settings.readSurfshark
case constants.Torguard:
readProvider = settings.readTorguard
case constants.VPNUnlimited:
readProvider = settings.readVPNUnlimited
case constants.Vyprvpn:
readProvider = settings.readVyprvpn
case constants.Windscribe:
readProvider = settings.readWindscribe
default:
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Name)
}
return readProvider(r)
}
func (settings *Provider) readVPNServiceProvider(r reader) (err error) {
allowedVPNServiceProviders := []string{
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"}
vpnsp, err := r.env.Inside("VPNSP", allowedVPNServiceProviders,
params.Default("private internet access"))
if err != nil {
return fmt.Errorf("environment variable VPNSP: %w", err)
}
if vpnsp == "pia" { // retro compatibility
vpnsp = "private internet access"
}
settings.Name = vpnsp
return nil
}
func commaJoin(slice []string) string {
return strings.Join(slice, ", ")
}

View File

@@ -27,18 +27,12 @@ func Test_Provider_lines(t *testing.T) {
Groups: []string{"group"},
Regions: []string{"a", "El country"},
},
ExtraConfigOptions: ExtraConfigOptions{
ClientKey: "a",
ClientCertificate: "a",
},
},
lines: []string{
"|--Cyberghost settings:",
" |--Network protocol: udp",
" |--Server groups: group",
" |--Regions: a, El country",
" |--Client key is set",
" |--Client certificate is set",
},
},
"fastestvpn": {
@@ -116,9 +110,6 @@ func Test_Provider_lines(t *testing.T) {
ISPs: []string{"e", "f"},
CustomPort: 1,
},
ExtraConfigOptions: ExtraConfigOptions{
OpenVPNIPv6: true,
},
},
lines: []string{
"|--Mullvad settings:",
@@ -127,7 +118,6 @@ func Test_Provider_lines(t *testing.T) {
" |--Cities: c, d",
" |--ISPs: e, f",
" |--Custom port: 1",
" |--IPv6: enabled",
},
},
"nordvpn": {
@@ -201,7 +191,6 @@ func Test_Provider_lines(t *testing.T) {
Name: constants.PrivateInternetAccess,
ServerSelection: ServerSelection{
Regions: []string{"a", "b"},
EncryptionPreset: constants.PIAEncryptionPresetStrong,
CustomPort: 1,
},
PortForwarding: PortForwarding{
@@ -213,7 +202,6 @@ func Test_Provider_lines(t *testing.T) {
"|--Private Internet Access settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Encryption preset: strong",
" |--Custom port: 1",
" |--Port forwarding:",
" |--File path: /here",
@@ -276,9 +264,6 @@ func Test_Provider_lines(t *testing.T) {
FreeOnly: true,
StreamOnly: true,
},
ExtraConfigOptions: ExtraConfigOptions{
ClientKey: "a",
},
},
lines: []string{
"|--Vpn Unlimited settings:",
@@ -288,7 +273,6 @@ func Test_Provider_lines(t *testing.T) {
" |--Hostnames: e, f",
" |--Free servers only",
" |--Stream servers only",
" |--Client key is set",
},
},
"vyprvpn": {

View File

@@ -33,7 +33,7 @@ type ServerSelection struct { //nolint:maligned
// NordVPN
Numbers []uint16 `json:"numbers"`
// PIA
// PIA - needed to get the port number
EncryptionPreset string `json:"encryption_preset"`
// ProtonVPN
@@ -43,13 +43,6 @@ type ServerSelection struct { //nolint:maligned
StreamOnly bool `json:"stream_only"`
}
type ExtraConfigOptions struct {
ClientCertificate string `json:"-"` // Cyberghost
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
EncryptionPreset string `json:"encryption_preset"` // PIA
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
}
// PortForwarding contains settings for port forwarding.
type PortForwarding struct {
Enabled bool `json:"enabled"`

View File

@@ -11,7 +11,7 @@ import (
// Settings contains all settings for the program to run.
type Settings struct {
OpenVPN OpenVPN
VPN VPN
System System
DNS DNS
Firewall Firewall
@@ -30,7 +30,7 @@ func (settings *Settings) String() string {
func (settings *Settings) lines() (lines []string) {
lines = append(lines, "Settings summary below:")
lines = append(lines, settings.OpenVPN.lines()...)
lines = append(lines, settings.VPN.lines()...)
lines = append(lines, settings.DNS.lines()...)
lines = append(lines, settings.Firewall.lines()...)
lines = append(lines, settings.System.lines()...)
@@ -47,7 +47,7 @@ func (settings *Settings) lines() (lines []string) {
}
var (
ErrOpenvpn = errors.New("cannot read Openvpn settings")
ErrVPN = errors.New("cannot read VPN settings")
ErrSystem = errors.New("cannot read System settings")
ErrDNS = errors.New("cannot read DNS settings")
ErrFirewall = errors.New("cannot read firewall settings")
@@ -69,8 +69,8 @@ func (settings *Settings) Read(env params.Env, logger logging.Logger) (err error
return fmt.Errorf("environment variable VERSION_INFORMATION: %w", err)
}
if err := settings.OpenVPN.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrOpenvpn, err)
if err := settings.VPN.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrVPN, err)
}
if err := settings.System.read(r); err != nil {

View File

@@ -16,19 +16,23 @@ func Test_Settings_lines(t *testing.T) {
}{
"default settings": {
settings: Settings{
OpenVPN: OpenVPN{
Version: constants.Openvpn25,
VPN: VPN{
Type: constants.OpenVPN,
Provider: Provider{
Name: constants.Mullvad,
},
OpenVPN: OpenVPN{
Version: constants.Openvpn25,
},
},
},
lines: []string{
"Settings summary below:",
"|--VPN:",
" |--Type: openvpn",
" |--OpenVPN:",
" |--Version: 2.5",
" |--Verbosity level: 0",
" |--Provider:",
" |--Mullvad settings:",
" |--Network protocol: udp",
"|--DNS:",

View File

@@ -0,0 +1,71 @@
package configuration
import (
"errors"
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
type VPN struct {
Type string `json:"type"`
OpenVPN OpenVPN `json:"openvpn"`
Provider Provider `json:"provider"`
}
func (settings *VPN) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *VPN) lines() (lines []string) {
lines = append(lines, lastIndent+"VPN:")
lines = append(lines, indent+lastIndent+"Type: "+settings.Type)
for _, line := range settings.OpenVPN.lines() {
lines = append(lines, indent+line)
}
for _, line := range settings.Provider.lines() {
lines = append(lines, indent+line)
}
return lines
}
var (
errReadProviderSettings = errors.New("cannot read provider settings")
errReadOpenVPNSettings = errors.New("cannot read OpenVPN settings")
)
func (settings *VPN) read(r reader) (err error) {
vpnType, err := r.env.Inside("VPN_TYPE",
[]string{constants.OpenVPN}, params.Default(constants.OpenVPN))
if err != nil {
return fmt.Errorf("environment variable VPN_TYPE: %w", err)
}
settings.Type = vpnType
if !settings.isOpenVPNCustomConfig(r.env) {
if err := settings.Provider.read(r); err != nil {
return fmt.Errorf("%w: %s", errReadProviderSettings, err)
}
}
err = settings.OpenVPN.read(r, settings.Provider.Name)
if err != nil {
return fmt.Errorf("%w: %s", errReadOpenVPNSettings, err)
}
return nil
}
func (settings VPN) isOpenVPNCustomConfig(env params.Env) (ok bool) {
if settings.Type != constants.OpenVPN {
return false
}
s, err := env.Get("OPENVPN_CUSTOM_CONFIG")
return err == nil && s != ""
}

View File

@@ -28,10 +28,6 @@ func (settings *Provider) vpnUnlimitedLines() (lines []string) {
lines = append(lines, lastIndent+"Stream servers only")
}
if settings.ExtraConfigOptions.ClientKey != "" {
lines = append(lines, lastIndent+"Client key is set")
}
return lines
}
@@ -48,16 +44,6 @@ func (settings *Provider) readVPNUnlimited(r reader) (err error) {
return err
}
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
@@ -85,3 +71,17 @@ func (settings *Provider) readVPNUnlimited(r reader) (err error) {
return nil
}
func (settings *OpenVPN) readVPNUnlimited(r reader) (err error) {
settings.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ClientCrt, err = readClientCertificate(r)
if err != nil {
return err
}
return nil
}

View File

@@ -1,5 +1,9 @@
package constants
const (
OpenVPN = "openvpn"
)
const (
// Cyberghost is a VPN provider.
Cyberghost = "cyberghost"

View File

@@ -68,7 +68,7 @@ func modifyCustomConfig(lines []string, username string,
len(settings.Cipher) > 0 && strings.HasPrefix(line, "data-ciphers"),
len(settings.Auth) > 0 && strings.HasPrefix(line, "auth "),
settings.MSSFix > 0 && strings.HasPrefix(line, "mssfix "),
!settings.Provider.ExtraConfigOptions.OpenVPNIPv6 && strings.HasPrefix(line, "tun-ipv6"):
!settings.IPv6 && strings.HasPrefix(line, "tun-ipv6"):
default:
modified = append(modified, line)
}
@@ -93,7 +93,7 @@ func modifyCustomConfig(lines []string, username string,
if settings.MSSFix > 0 {
modified = append(modified, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if !settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if !settings.IPv6 {
modified = append(modified, `pull-filter ignore "route-ipv6"`)
modified = append(modified, `pull-filter ignore "ifconfig-ipv6"`)
}

View File

@@ -67,8 +67,10 @@ const (
defaultBackoffTime = 15 * time.Second
)
func NewLoop(settings configuration.OpenVPN, username string,
puid, pgid int, allServers models.AllServers, conf Configurator,
func NewLoop(openVPNSettings configuration.OpenVPN,
providerSettings configuration.Provider,
username string, puid, pgid int,
allServers models.AllServers, conf Configurator,
fw firewallConfigurer, routing routing.VPNGetter,
portForward portforward.StartStopper,
publicip publicip.Looper, dnsLooper dns.Looper,
@@ -80,7 +82,7 @@ func NewLoop(settings configuration.OpenVPN, username string,
stopped := make(chan struct{})
statusManager := loopstate.New(constants.Stopped, start, running, stop, stopped)
state := state.New(statusManager, settings, allServers)
state := state.New(statusManager, openVPNSettings, providerSettings, allServers)
return &Loop{
statusManager: statusManager,

View File

@@ -23,23 +23,23 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
}
for ctx.Err() == nil {
settings, allServers := l.state.GetSettingsAndServers()
openVPNSettings, providerSettings, allServers := l.state.GetSettingsAndServers()
providerConf := provider.New(settings.Provider.Name, allServers, time.Now)
providerConf := provider.New(providerSettings.Name, allServers, time.Now)
var connection models.OpenVPNConnection
var lines []string
var err error
if settings.Config == "" {
connection, err = providerConf.GetOpenVPNConnection(settings.Provider.ServerSelection)
if openVPNSettings.Config == "" {
connection, err = providerConf.GetOpenVPNConnection(providerSettings.ServerSelection)
if err != nil {
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
continue
}
lines = providerConf.BuildConf(connection, l.username, settings)
lines = providerConf.BuildConf(connection, l.username, openVPNSettings)
} else {
lines, connection, err = l.processCustomConfig(settings)
lines, connection, err = l.processCustomConfig(openVPNSettings)
if err != nil {
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
@@ -53,9 +53,9 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
continue
}
if settings.User != "" {
if openVPNSettings.User != "" {
err := l.conf.WriteAuthFile(
settings.User, settings.Password, l.puid, l.pgid)
openVPNSettings.User, openVPNSettings.Password, l.puid, l.pgid)
if err != nil {
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
@@ -72,7 +72,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
openvpnCtx, openvpnCancel := context.WithCancel(context.Background())
stdoutLines, stderrLines, waitError, err := l.conf.Start(
openvpnCtx, settings.Version, settings.Flags)
openvpnCtx, openVPNSettings.Version, openVPNSettings.Flags)
if err != nil {
openvpnCancel()
l.signalOrSetStatus(constants.Crashed)
@@ -96,12 +96,12 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
for stayHere {
select {
case <-l.startPFCh:
l.startPortForwarding(ctx, settings.Provider.PortForwarding.Enabled,
l.startPortForwarding(ctx, providerSettings.PortForwarding.Enabled,
providerConf, connection.Hostname)
case <-ctx.Done():
const pfTimeout = 100 * time.Millisecond
l.stopPortForwarding(context.Background(),
settings.Provider.PortForwarding.Enabled, pfTimeout)
providerSettings.PortForwarding.Enabled, pfTimeout)
openvpnCancel()
<-waitError
close(waitError)
@@ -110,7 +110,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
case <-l.stop:
l.userTrigger = true
l.logger.Info("stopping")
l.stopPortForwarding(ctx, settings.Provider.PortForwarding.Enabled, 0)
l.stopPortForwarding(ctx, providerSettings.PortForwarding.Enabled, 0)
openvpnCancel()
<-waitError
// do not close waitError or the waitError
@@ -127,7 +127,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
l.statusManager.Lock() // prevent SetStatus from running in parallel
l.stopPortForwarding(ctx, settings.Provider.PortForwarding.Enabled, 0)
l.stopPortForwarding(ctx, providerSettings.PortForwarding.Enabled, 0)
openvpnCancel()
l.statusManager.SetStatus(constants.Crashed)
l.logAndWait(ctx, err)

View File

@@ -9,11 +9,13 @@ import (
type SettingsGetSetter = state.SettingsGetSetter
func (l *Loop) GetSettings() (settings configuration.OpenVPN) {
func (l *Loop) GetSettings() (
openvpn configuration.OpenVPN, provider configuration.Provider) {
return l.state.GetSettings()
}
func (l *Loop) SetSettings(ctx context.Context, settings configuration.OpenVPN) (
func (l *Loop) SetSettings(ctx context.Context,
openvpn configuration.OpenVPN, provider configuration.Provider) (
outcome string) {
return l.state.SetSettings(ctx, settings)
return l.state.SetSettings(ctx, openvpn, provider)
}

View File

@@ -0,0 +1,43 @@
package state
import (
"context"
"reflect"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
)
type SettingsGetSetter interface {
GetSettings() (openvpn configuration.OpenVPN,
provider configuration.Provider)
SetSettings(ctx context.Context, openvpn configuration.OpenVPN,
provider configuration.Provider) (outcome string)
}
func (s *State) GetSettings() (openvpn configuration.OpenVPN,
provider configuration.Provider) {
s.settingsMu.RLock()
openvpn = s.openvpn
provider = s.provider
s.settingsMu.RUnlock()
return openvpn, provider
}
func (s *State) SetSettings(ctx context.Context,
openvpn configuration.OpenVPN, provider configuration.Provider) (
outcome string) {
s.settingsMu.Lock()
settingsUnchanged := reflect.DeepEqual(s.openvpn, openvpn) &&
reflect.DeepEqual(s.provider, provider)
if settingsUnchanged {
s.settingsMu.Unlock()
return "settings left unchanged"
}
s.openvpn = openvpn
s.provider = provider
s.settingsMu.Unlock()
_, _ = s.statusApplier.ApplyStatus(ctx, constants.Stopped)
outcome, _ = s.statusApplier.ApplyStatus(ctx, constants.Running)
return outcome
}

View File

@@ -1,36 +0,0 @@
package state
import (
"context"
"reflect"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
)
type SettingsGetSetter interface {
GetSettings() (settings configuration.OpenVPN)
SetSettings(ctx context.Context, settings configuration.OpenVPN) (
outcome string)
}
func (s *State) GetSettings() (settings configuration.OpenVPN) {
s.settingsMu.RLock()
defer s.settingsMu.RUnlock()
return s.settings
}
func (s *State) SetSettings(ctx context.Context, settings configuration.OpenVPN) (
outcome string) {
s.settingsMu.Lock()
settingsUnchanged := reflect.DeepEqual(s.settings, settings)
if settingsUnchanged {
s.settingsMu.Unlock()
return "settings left unchanged"
}
s.settings = settings
s.settingsMu.Unlock()
_, _ = s.statusApplier.ApplyStatus(ctx, constants.Stopped)
outcome, _ = s.statusApplier.ApplyStatus(ctx, constants.Running)
return outcome
}

View File

@@ -13,16 +13,17 @@ var _ Manager = (*State)(nil)
type Manager interface {
SettingsGetSetter
ServersGetterSetter
GetSettingsAndServers() (settings configuration.OpenVPN,
allServers models.AllServers)
GetSettingsAndServers() (openvpn configuration.OpenVPN,
provider configuration.Provider, allServers models.AllServers)
}
func New(statusApplier loopstate.Applier,
settings configuration.OpenVPN,
openvpn configuration.OpenVPN, provider configuration.Provider,
allServers models.AllServers) *State {
return &State{
statusApplier: statusApplier,
settings: settings,
openvpn: openvpn,
provider: provider,
allServers: allServers,
}
}
@@ -30,20 +31,22 @@ func New(statusApplier loopstate.Applier,
type State struct {
statusApplier loopstate.Applier
settings configuration.OpenVPN
openvpn configuration.OpenVPN
provider configuration.Provider
settingsMu sync.RWMutex
allServers models.AllServers
allServersMu sync.RWMutex
}
func (s *State) GetSettingsAndServers() (settings configuration.OpenVPN,
allServers models.AllServers) {
func (s *State) GetSettingsAndServers() (openvpn configuration.OpenVPN,
provider configuration.Provider, allServers models.AllServers) {
s.settingsMu.RLock()
s.allServersMu.RLock()
settings = s.settings
openvpn = s.openvpn
provider = s.provider
allServers = s.allServers
s.settingsMu.RUnlock()
s.allServersMu.RUnlock()
return settings, allServers
return openvpn, provider, allServers
}

View File

@@ -71,7 +71,7 @@ func (c *Cyberghost) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
@@ -81,9 +81,9 @@ func (c *Cyberghost) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, utils.WrapOpenvpnCA(
constants.CyberghostCertificate)...)
lines = append(lines, utils.WrapOpenvpnCert(
settings.Provider.ExtraConfigOptions.ClientCertificate)...)
settings.ClientCrt)...)
lines = append(lines, utils.WrapOpenvpnKey(
settings.Provider.ExtraConfigOptions.ClientKey)...)
settings.ClientKey)...)
lines = append(lines, "")

View File

@@ -62,7 +62,7 @@ func (f *Fastestvpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -59,7 +59,7 @@ func (h *HideMyAss) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -57,7 +57,7 @@ func (i *Ipvanish) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -63,7 +63,7 @@ func (i *Ivpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -56,7 +56,7 @@ func (m *Mullvad) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "fast-io")
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
@@ -71,7 +71,7 @@ func (m *Mullvad) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -67,7 +67,7 @@ func (n *Nordvpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -58,7 +58,7 @@ func (p *Privado) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -12,7 +12,7 @@ import (
func (p *PIA) BuildConf(connection models.OpenVPNConnection,
username string, settings configuration.OpenVPN) (lines []string) {
var defaultCipher, defaultAuth, X509CRL, certificate string
switch settings.Provider.ExtraConfigOptions.EncryptionPreset {
switch settings.EncPreset {
case constants.PIAEncryptionPresetNormal:
defaultCipher = constants.AES128cbc
defaultAuth = constants.SHA1
@@ -81,7 +81,7 @@ func (p *PIA) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -59,7 +59,7 @@ func (p *Privatevpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -66,7 +66,7 @@ func (p *Protonvpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -66,7 +66,7 @@ func (p *Purevpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -64,7 +64,7 @@ func (s *Surfshark) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -70,7 +70,7 @@ func (t *Torguard) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "fast-io")
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -55,7 +55,7 @@ func (p *Provider) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
@@ -65,9 +65,9 @@ func (p *Provider) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, utils.WrapOpenvpnCA(
constants.VPNUnlimitedCertificateAuthority)...)
lines = append(lines, utils.WrapOpenvpnCert(
settings.Provider.ExtraConfigOptions.ClientCertificate)...)
settings.ClientCrt)...)
lines = append(lines, utils.WrapOpenvpnKey(
settings.Provider.ExtraConfigOptions.ClientKey)...)
settings.ClientKey)...)
lines = append(lines, "")

View File

@@ -67,7 +67,7 @@ func (w *Windscribe) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
if settings.IPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)

View File

@@ -96,7 +96,7 @@ func (h *openvpnHandler) setStatus(w http.ResponseWriter, r *http.Request) {
}
func (h *openvpnHandler) getSettings(w http.ResponseWriter) {
settings := h.looper.GetSettings()
settings, _ := h.looper.GetSettings()
settings.User = "redacted"
settings.Password = "redacted"
encoder := json.NewEncoder(w)