chore(internal/providers): simplify OpenVPN config building

This commit is contained in:
Quentin McGaw
2022-04-25 07:57:45 +00:00
parent 4bde50fb3a
commit 7ff14a356c
27 changed files with 596 additions and 1438 deletions

View File

@@ -1,5 +1,254 @@
package utils
import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/openvpn/parse"
)
type OpenVPNProviderSettings struct {
Ping int
RemoteCertTLS bool
Ciphers []string
Auth string
CA string
CRLVerify string
Cert string
Key string
RSAKey string
TLSAuth string
TLSCrypt string
MssFix uint16
FastIO bool
AuthUserPass bool
AuthToken bool
Fragment uint16
SndBuf uint32
RcvBuf uint32
// VerifyX509Name can be set to a custom name to verify against.
// Note VerifyX509Type has to be set for it to be verified.
// If it is left unset, the code will deduce a name to verify against
// using the connection hostname and according to VerifyX509Type.
VerifyX509Name string
// VerifyX509Type can be "name-prefix", "name"
VerifyX509Type string
TLSCipher string
TunMTU uint16
TunMTUExtra uint16
RenegDisabled bool
RenegSec uint16
KeyDirection string
ExtraLines []string
UDPLines []string
IPv6Lines []string
}
//nolint:gocognit,gocyclo
func OpenVPNConfig(provider OpenVPNProviderSettings,
connection models.Connection,
settings settings.OpenVPN) []string {
var lines openvpnConfigLines
lines.add("client")
lines.add("nobind")
lines.add("tls-exit") // exit OpenVPN on a TLS error
lines.add("auth-nocache") // do not cache auth credentials
lines.add("mute-replay-warnings") // these are often ignored by some VPN providers
lines.add("auth-retry", "nointeract") // retry authenticating without interaction
lines.add("suppress-timestamps") // do not log timestamps, the Gluetun logger takes care of it
lines.add("dev", settings.Interface)
lines.add("verb", fmt.Sprint(*settings.Verbosity))
lines.add("auth-user-pass", constants.OpenVPNAuthConf)
lines.add("proto", connection.Protocol)
lines.add("remote", connection.IP.String(), fmt.Sprint(connection.Port))
if !provider.AuthToken {
lines.add("pull-filter", "ignore", `"auth-token"`) // prevent auth failed loops
}
if provider.KeyDirection != "" {
lines.add("key-direction", provider.KeyDirection)
}
if provider.Ping > 0 {
lines.add("ping", fmt.Sprint(provider.Ping))
}
if provider.RenegDisabled {
lines.add("reneg-sec", "0")
} else if provider.RenegSec > 0 {
lines.add("reneg-sec", fmt.Sprint(provider.RenegSec))
}
if provider.RemoteCertTLS {
// equivalent to older 'ns-cert-type' option
lines.add("remote-cert-tls server")
}
x509Type := provider.VerifyX509Type
if x509Type != "" {
x509Name := provider.VerifyX509Name
if x509Name == "" {
// find name from connection hostname depending on type
switch x509Type {
case "name":
x509Name = connection.Hostname
case "name-prefix":
x509Name = strings.Split(connection.Hostname, ".")[0]
default:
panic(fmt.Sprintf("verify-x509-name type not supported: %q", x509Type))
}
}
lines.add("verify-x509-name", x509Name, x509Type)
}
if provider.TLSCipher != "" {
lines.add("tls-cipher", provider.TLSCipher)
}
if provider.FastIO {
lines.add("fast-io")
}
ciphers := defaultStringSlice(settings.Ciphers, provider.Ciphers)
cipherLines := CipherLines(ciphers, settings.Version)
lines.addLines(cipherLines)
auth := defaultString(*settings.Auth, provider.Auth)
if auth != "" {
lines.add("auth", auth)
}
if provider.TunMTU > 0 {
lines.add("tun-mtu", fmt.Sprint(provider.TunMTU))
}
if provider.TunMTUExtra > 0 {
lines.add("tun-mtu-extra", fmt.Sprint(provider.TunMTUExtra))
}
mssFix := defaultUint16(*settings.MSSFix, provider.MssFix)
if mssFix > 0 {
lines.add("mssfix", fmt.Sprint(mssFix))
}
if provider.SndBuf > 0 {
lines.add("sndbuf", fmt.Sprint(provider.SndBuf))
}
if provider.RcvBuf > 0 {
lines.add("rcvbuf", fmt.Sprint(provider.RcvBuf))
}
if connection.Protocol == constants.UDP {
lines.add("explicit-exit-notify")
}
if settings.ProcessUser != "root" {
lines.add("user", settings.ProcessUser)
lines.add("persist-tun")
lines.add("persist-key")
}
if *settings.IPv6 {
lines.add("tun-ipv6")
} else {
lines.add("pull-filter", "ignore", `"route-ipv6"`)
lines.add("pull-filter", "ignore", `"ifconfig-ipv6"`)
lines.addLines(provider.IPv6Lines)
}
if provider.CA != "" {
lines.addLines(WrapOpenvpnCA(provider.CA))
}
if provider.CRLVerify != "" {
lines.addLines(WrapOpenvpnCRLVerify(provider.CRLVerify))
}
if provider.Cert != "" {
lines.addLines(WrapOpenvpnCert(provider.Cert))
}
if provider.Key != "" {
lines.addLines(WrapOpenvpnKey(provider.Key))
}
if provider.RSAKey != "" {
lines.addLines(WrapOpenvpnRSAKey(provider.RSAKey))
}
if provider.TLSAuth != "" {
lines.addLines(WrapOpenvpnTLSAuth(provider.TLSAuth))
}
if provider.TLSCrypt != "" {
lines.addLines(WrapOpenvpnTLSCrypt(provider.TLSCrypt))
}
if *settings.ClientCrt != "" {
certData, err := parse.ExtractCert([]byte(*settings.ClientCrt))
panicOnError(err, "cannot extract client crt")
lines.addLines(WrapOpenvpnCert(certData))
}
if *settings.ClientKey != "" {
keyData, err := parse.ExtractPrivateKey([]byte(*settings.ClientKey))
panicOnError(err, "cannot extract client private key")
lines.addLines(WrapOpenvpnKey(keyData))
}
lines.addLines(provider.ExtraLines)
// Add a trailing empty line
lines.add("")
return lines
}
type openvpnConfigLines []string
func (o *openvpnConfigLines) add(words ...string) {
*o = append(*o, strings.Join(words, " "))
}
func (o *openvpnConfigLines) addLines(lines []string) {
for _, line := range lines {
o.add(line)
}
}
func defaultString(value, defaultValue string) string {
if value == "" {
return defaultValue
}
return value
}
func defaultUint16(value, defaultValue uint16) uint16 {
if value == 0 {
return defaultValue
}
return value
}
func defaultStringSlice(value, defaultValue []string) (
result []string) {
if len(value) > 0 {
result = make([]string, len(value))
copy(result, value)
return result
}
result = make([]string, len(defaultValue))
copy(result, defaultValue)
return result
}
func panicOnError(err error, context string) {
if err == nil {
return
}
panicMessage := fmt.Sprintf("%s: %s", context, err)
panic(panicMessage)
}
func WrapOpenvpnCA(certificate string) (lines []string) {
return []string{
"<ca>",