VPNSP value custom for OpenVPN custom config files (#621)

- Retro-compatibility: `OPENVPN_CUSTOM_CONFIG` set implies `VPNSP=custom`
- Change: `up` and `down` options are not filtered out
- Change: `OPENVPN_INTERFACE` overrides the network interface defined in the configuration file
- Change: `PORT` overrides any port found in the configuration file
- Feat: config file is read when building the OpenVPN configuration, so it's effectively reloaded on VPN restarts
- Feat: extract values from custom file at start to log out valid settings
- Maint: `internal/openvpn/extract` package instead of `internal/openvpn/custom` package
- Maint: All providers' `BuildConf` method return an error
- Maint: rename `CustomConfig` to `ConfFile` in Settings structures
This commit is contained in:
Quentin McGaw
2021-09-13 11:30:14 -04:00
committed by GitHub
parent 11af6c10f1
commit f807f756eb
43 changed files with 328 additions and 296 deletions

View File

@@ -0,0 +1,38 @@
package custom
import (
"errors"
"fmt"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/utils"
)
var (
ErrVPNTypeNotSupported = errors.New("VPN type not supported for custom provider")
ErrExtractConnection = errors.New("cannot extract connection")
)
// GetConnection gets the connection from the OpenVPN configuration file.
func (p *Provider) GetConnection(selection configuration.ServerSelection) (
connection models.Connection, err error) {
if selection.VPN != constants.OpenVPN {
return connection, fmt.Errorf("%w: %s", ErrVPNTypeNotSupported, selection.VPN)
}
_, connection, err = p.extractor.Data(selection.OpenVPN.ConfFile)
if err != nil {
return connection, fmt.Errorf("%w: %s", ErrExtractConnection, err)
}
connection.Port = getPort(connection.Port, selection)
return connection, nil
}
// Port found is overridden by custom port set with `PORT` or `WIREGUARD_PORT`.
func getPort(foundPort uint16, selection configuration.ServerSelection) (port uint16) {
return utils.GetPort(selection, foundPort, foundPort, foundPort)
}

View File

@@ -0,0 +1,101 @@
package custom
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/utils"
)
var ErrExtractData = errors.New("failed extracting information from custom configuration file")
func (p *Provider) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string, err error) {
lines, _, err = p.extractor.Data(settings.ConfFile)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrExtractData, err)
}
lines = modifyConfig(lines, connection, settings)
return lines, nil
}
func modifyConfig(lines []string, connection models.Connection,
settings configuration.OpenVPN) (modified []string) {
// Remove some lines
for _, line := range lines {
switch {
case
line == "",
strings.HasPrefix(line, "verb "),
strings.HasPrefix(line, "auth-user-pass "),
strings.HasPrefix(line, "user "),
strings.HasPrefix(line, "proto "),
strings.HasPrefix(line, "remote "),
strings.HasPrefix(line, "dev "),
settings.Cipher != "" && strings.HasPrefix(line, "cipher "),
settings.Cipher != "" && strings.HasPrefix(line, "data-ciphers "),
settings.Auth != "" && strings.HasPrefix(line, "auth "),
settings.MSSFix > 0 && strings.HasPrefix(line, "mssfix "),
!settings.IPv6 && strings.HasPrefix(line, "tun-ipv6"):
default:
modified = append(modified, line)
}
}
// Add values
modified = append(modified, connection.OpenVPNProtoLine())
modified = append(modified, connection.OpenVPNRemoteLine())
modified = append(modified, "dev "+settings.Interface)
modified = append(modified, "mute-replay-warnings")
modified = append(modified, "auth-nocache")
modified = append(modified, "pull-filter ignore \"auth-token\"") // prevent auth failed loop
modified = append(modified, "auth-retry nointeract")
modified = append(modified, "suppress-timestamps")
if settings.User != "" {
modified = append(modified, "auth-user-pass "+constants.OpenVPNAuthConf)
}
modified = append(modified, "verb "+strconv.Itoa(settings.Verbosity))
if settings.Cipher != "" {
modified = append(modified, utils.CipherLines(settings.Cipher, settings.Version)...)
}
if settings.Auth != "" {
modified = append(modified, "auth "+settings.Auth)
}
if settings.MSSFix > 0 {
modified = append(modified, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if !settings.IPv6 {
modified = append(modified, `pull-filter ignore "route-ipv6"`)
modified = append(modified, `pull-filter ignore "ifconfig-ipv6"`)
}
if !settings.Root {
modified = append(modified, "user "+settings.ProcUser)
}
modified = append(modified, "") // trailing line
return uniqueLines(modified)
}
func uniqueLines(lines []string) (unique []string) {
seen := make(map[string]struct{}, len(lines))
unique = make([]string, 0, len(lines))
for _, line := range lines {
_, ok := seen[line]
if ok {
continue
}
seen[line] = struct{}{}
unique = append(unique, line)
}
return unique
}

View File

@@ -0,0 +1,82 @@
package custom
import (
"net"
"testing"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
)
func Test_modifyConfig(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
lines []string
settings configuration.OpenVPN
connection models.Connection
modified []string
}{
"mixed": {
lines: []string{
"up bla",
"proto tcp",
"remote 5.5.5.5",
"cipher bla",
"",
"tun-ipv6",
"keep me here",
"auth bla",
},
settings: configuration.OpenVPN{
User: "user",
Cipher: "cipher",
Auth: "auth",
MSSFix: 1000,
ProcUser: "procuser",
Interface: "tun3",
},
connection: models.Connection{
IP: net.IPv4(1, 2, 3, 4),
Port: 1194,
Protocol: constants.UDP,
},
modified: []string{
"up bla",
"keep me here",
"proto udp",
"remote 1.2.3.4 1194",
"dev tun3",
"mute-replay-warnings",
"auth-nocache",
"pull-filter ignore \"auth-token\"",
"auth-retry nointeract",
"suppress-timestamps",
"auth-user-pass /etc/openvpn/auth.conf",
"verb 0",
"data-ciphers-fallback cipher",
"data-ciphers cipher",
"auth auth",
"mssfix 1000",
"pull-filter ignore \"route-ipv6\"",
"pull-filter ignore \"ifconfig-ipv6\"",
"user procuser",
"",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
modified := modifyConfig(testCase.lines,
testCase.connection, testCase.settings)
assert.Equal(t, testCase.modified, modified)
})
}
}

View File

@@ -0,0 +1,19 @@
package custom
import (
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/openvpn/extract"
"github.com/qdm12/gluetun/internal/provider/utils"
)
type Provider struct {
extractor extract.Interface
utils.NoPortForwarder
}
func New() *Provider {
return &Provider{
extractor: extract.New(),
NoPortForwarder: utils.NewNoPortForwarding(constants.Custom),
}
}

View File

@@ -11,7 +11,7 @@ import (
)
func (c *Cyberghost) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -87,5 +87,5 @@ func (c *Cyberghost) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (f *Fastestvpn) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -76,5 +76,5 @@ func (f *Fastestvpn) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (h *HideMyAss) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -75,5 +75,5 @@ func (h *HideMyAss) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (i *Ipvanish) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -68,5 +68,5 @@ func (i *Ipvanish) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -11,7 +11,7 @@ import (
)
func (i *Ivpn) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -77,5 +77,5 @@ func (i *Ivpn) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (m *Mullvad) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -83,5 +83,5 @@ func (m *Mullvad) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (n *Nordvpn) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -81,5 +81,5 @@ func (n *Nordvpn) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (p *Privado) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -70,5 +70,5 @@ func (p *Privado) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (p *PIA) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
var defaultCipher, defaultAuth, X509CRL, certificate string
switch settings.EncPreset {
case constants.PIAEncryptionPresetNormal:
@@ -93,5 +93,5 @@ func (p *PIA) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (p *Privatevpn) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES128gcm
}
@@ -73,5 +73,5 @@ func (p *Privatevpn) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (p *Protonvpn) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -80,5 +80,5 @@ func (p *Protonvpn) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/custom"
"github.com/qdm12/gluetun/internal/provider/cyberghost"
"github.com/qdm12/gluetun/internal/provider/fastestvpn"
"github.com/qdm12/gluetun/internal/provider/hidemyass"
@@ -34,7 +35,7 @@ import (
// Provider contains methods to read and modify the openvpn configuration to connect as a client.
type Provider interface {
GetConnection(selection configuration.ServerSelection) (connection models.Connection, err error)
BuildConf(connection models.Connection, settings configuration.OpenVPN) (lines []string)
BuildConf(connection models.Connection, settings configuration.OpenVPN) (lines []string, err error)
PortForwarder
}
@@ -50,6 +51,8 @@ type PortForwarder interface {
func New(provider string, allServers models.AllServers, timeNow func() time.Time) Provider {
randSource := rand.NewSource(timeNow().UnixNano())
switch provider {
case constants.Custom:
return custom.New()
case constants.Cyberghost:
return cyberghost.New(allServers.Cyberghost.Servers, randSource)
case constants.Fastestvpn:

View File

@@ -10,7 +10,7 @@ import (
)
func (p *Purevpn) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256gcm
}
@@ -84,5 +84,5 @@ func (p *Purevpn) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (s *Surfshark) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256gcm
}
@@ -78,5 +78,5 @@ func (s *Surfshark) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (t *Torguard) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256gcm
}
@@ -84,5 +84,5 @@ func (t *Torguard) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (p *Provider) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
lines = []string{
"client",
"dev " + settings.Interface,
@@ -71,5 +71,5 @@ func (p *Provider) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -10,7 +10,7 @@ import (
)
func (v *Vyprvpn) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -65,5 +65,5 @@ func (v *Vyprvpn) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}

View File

@@ -11,7 +11,7 @@ import (
)
func (w *Windscribe) BuildConf(connection models.Connection,
settings configuration.OpenVPN) (lines []string) {
settings configuration.OpenVPN) (lines []string, err error) {
if settings.Cipher == "" {
settings.Cipher = constants.AES256cbc
}
@@ -81,5 +81,5 @@ func (w *Windscribe) BuildConf(connection models.Connection,
lines = append(lines, "")
return lines
return lines, nil
}