mirror of
https://github.com/qdm12/gluetun.git
synced 2025-12-10 10:45:38 -06:00
- `OPENVPN_ENCRYPTED_KEY` environment variable - `OPENVPN_ENCRYPTED_KEY_SECRETFILE` environment variable - `OPENVPN_KEY_PASSPHRASE` environment variable - `OPENVPN_KEY_PASSPHRASE_SECRETFILE` environment variable - `PREMIUM_ONLY` environment variable - OpenVPN user and password not required for vpnsecure provider
344 lines
8.2 KiB
Go
344 lines
8.2 KiB
Go
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/extract"
|
|
)
|
|
|
|
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.Fragment > 0 {
|
|
lines.add("fragment", fmt.Sprint(provider.Fragment))
|
|
}
|
|
|
|
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")
|
|
lines.addLines(provider.UDPLines)
|
|
}
|
|
|
|
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.EncryptedKey != "" {
|
|
lines.add("askpass", openvpn.AskPassPath)
|
|
keyData, err := extract.PEM([]byte(*settings.EncryptedKey))
|
|
panicOnError(err, "cannot extract PEM encrypted key")
|
|
lines.addLines(WrapOpenvpnEncryptedKey(keyData))
|
|
}
|
|
|
|
if *settings.Cert != "" {
|
|
certData, err := extract.PEM([]byte(*settings.Cert))
|
|
panicOnError(err, "cannot extract OpenVPN certificate")
|
|
lines.addLines(WrapOpenvpnCert(certData))
|
|
}
|
|
|
|
if *settings.Key != "" {
|
|
keyData, err := extract.PEM([]byte(*settings.Key))
|
|
panicOnError(err, "cannot extract OpenVPN 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>",
|
|
"-----BEGIN CERTIFICATE-----",
|
|
certificate,
|
|
"-----END CERTIFICATE-----",
|
|
"</ca>",
|
|
}
|
|
}
|
|
|
|
func WrapOpenvpnCert(clientCertificate string) (lines []string) {
|
|
return []string{
|
|
"<cert>",
|
|
"-----BEGIN CERTIFICATE-----",
|
|
clientCertificate,
|
|
"-----END CERTIFICATE-----",
|
|
"</cert>",
|
|
}
|
|
}
|
|
|
|
func WrapOpenvpnCRLVerify(x509CRL string) (lines []string) {
|
|
return []string{
|
|
"<crl-verify>",
|
|
"-----BEGIN X509 CRL-----",
|
|
x509CRL,
|
|
"-----END X509 CRL-----",
|
|
"</crl-verify>",
|
|
}
|
|
}
|
|
|
|
func WrapOpenvpnKey(clientKey string) (lines []string) {
|
|
return []string{
|
|
"<key>",
|
|
"-----BEGIN PRIVATE KEY-----",
|
|
clientKey,
|
|
"-----END PRIVATE KEY-----",
|
|
"</key>",
|
|
}
|
|
}
|
|
|
|
func WrapOpenvpnEncryptedKey(encryptedKey string) (lines []string) {
|
|
return []string{
|
|
"<key>",
|
|
"-----BEGIN ENCRYPTED PRIVATE KEY-----",
|
|
encryptedKey,
|
|
"-----END ENCRYPTED PRIVATE KEY-----",
|
|
"</key>",
|
|
}
|
|
}
|
|
|
|
func WrapOpenvpnRSAKey(rsaPrivateKey string) (lines []string) {
|
|
return []string{
|
|
"<key>",
|
|
"-----BEGIN RSA PRIVATE KEY-----",
|
|
rsaPrivateKey,
|
|
"-----END RSA PRIVATE KEY-----",
|
|
"</key>",
|
|
}
|
|
}
|
|
|
|
func WrapOpenvpnTLSAuth(staticKeyV1 string) (lines []string) {
|
|
return []string{
|
|
"<tls-auth>",
|
|
"-----BEGIN OpenVPN Static key V1-----",
|
|
staticKeyV1,
|
|
"-----END OpenVPN Static key V1-----",
|
|
"</tls-auth>",
|
|
}
|
|
}
|
|
|
|
func WrapOpenvpnTLSCrypt(staticKeyV1 string) (lines []string) {
|
|
return []string{
|
|
"<tls-crypt>",
|
|
"-----BEGIN OpenVPN Static key V1-----",
|
|
staticKeyV1,
|
|
"-----END OpenVPN Static key V1-----",
|
|
"</tls-crypt>",
|
|
}
|
|
}
|