package utils import ( "fmt" "strings" "github.com/qdm12/gluetun/internal/configuration/settings" "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants/openvpn" "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", openvpn.AuthConf) 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{ "", "-----BEGIN CERTIFICATE-----", certificate, "-----END CERTIFICATE-----", "", } } func WrapOpenvpnCert(clientCertificate string) (lines []string) { return []string{ "", "-----BEGIN CERTIFICATE-----", clientCertificate, "-----END CERTIFICATE-----", "", } } func WrapOpenvpnCRLVerify(x509CRL string) (lines []string) { return []string{ "", "-----BEGIN X509 CRL-----", x509CRL, "-----END X509 CRL-----", "", } } func WrapOpenvpnKey(clientKey string) (lines []string) { return []string{ "", "-----BEGIN PRIVATE KEY-----", clientKey, "-----END PRIVATE KEY-----", "", } } func WrapOpenvpnRSAKey(rsaPrivateKey string) (lines []string) { return []string{ "", "-----BEGIN RSA PRIVATE KEY-----", rsaPrivateKey, "-----END RSA PRIVATE KEY-----", "", } } func WrapOpenvpnTLSAuth(staticKeyV1 string) (lines []string) { return []string{ "", "-----BEGIN OpenVPN Static key V1-----", staticKeyV1, "-----END OpenVPN Static key V1-----", "", } } func WrapOpenvpnTLSCrypt(staticKeyV1 string) (lines []string) { return []string{ "", "-----BEGIN OpenVPN Static key V1-----", staticKeyV1, "-----END OpenVPN Static key V1-----", "", } }