Compare commits

..

1 Commits

Author SHA1 Message Date
Quentin McGaw
5f3301f3a3 Use Openvpn 2.4 only 2024-11-08 16:41:37 +00:00
26 changed files with 76 additions and 298 deletions

View File

@@ -85,7 +85,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
OPENVPN_PASSWORD= \
OPENVPN_USER_SECRETFILE=/run/secrets/openvpn_user \
OPENVPN_PASSWORD_SECRETFILE=/run/secrets/openvpn_password \
OPENVPN_VERSION=2.6 \
OPENVPN_VERSION=2.4 \
OPENVPN_VERBOSITY=1 \
OPENVPN_FLAGS= \
OPENVPN_CIPHERS= \
@@ -224,6 +224,9 @@ EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=3 CMD /gluetun-entrypoint healthcheck
ARG TARGETPLATFORM
RUN apk add --no-cache --update -l wget && \
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn\~2.4 && \
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.16/main" openssl\~1.1 && \
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.17/main" openvpn\~2.5 && \
mv /usr/sbin/openvpn /usr/sbin/openvpn2.5 && \
apk del openvpn && \

View File

@@ -270,6 +270,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
err = printVersions(ctx, logger, []printVersionElement{
{name: "Alpine", getVersion: alpineConf.Version},
{name: "OpenVPN 2.4", getVersion: ovpnConf.Version24},
{name: "OpenVPN 2.5", getVersion: ovpnConf.Version25},
{name: "OpenVPN 2.6", getVersion: ovpnConf.Version26},
{name: "IPtables", getVersion: firewallConf.Version},

4
go.mod
View File

@@ -8,7 +8,7 @@ require (
github.com/golang/mock v1.6.0
github.com/klauspost/compress v1.17.11
github.com/klauspost/pgzip v1.2.6
github.com/pelletier/go-toml/v2 v2.2.3
github.com/pelletier/go-toml/v2 v2.2.2
github.com/qdm12/dns/v2 v2.0.0-rc8
github.com/qdm12/gosettings v0.4.3
github.com/qdm12/goshutdown v0.3.0
@@ -22,7 +22,7 @@ require (
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
golang.org/x/net v0.30.0
golang.org/x/sys v0.27.0
golang.org/x/sys v0.26.0
golang.org/x/text v0.19.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6

17
go.sum
View File

@@ -4,6 +4,7 @@ github.com/breml/rootcerts v0.2.18 h1:KjZaNT7AX/akUjzpStuwTMQs42YHlPyc6NmdwShVba
github.com/breml/rootcerts v0.2.18/go.mod h1:S/PKh+4d1HUn4HQovEB8hPJZO6pUZYrIhmXBhsegfXw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
@@ -41,8 +42,8 @@ github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE9
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
@@ -73,6 +74,13 @@ github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
@@ -112,8 +120,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -142,6 +150,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=

View File

@@ -19,7 +19,7 @@ import (
// OpenVPN contains settings to configure the OpenVPN client.
type OpenVPN struct {
// Version is the OpenVPN version to run.
// It can only be "2.5" or "2.6".
// It can only be "2.4".
Version string `json:"version"`
// User is the OpenVPN authentication username.
// It cannot be nil in the internal state if OpenVPN is used.
@@ -90,7 +90,7 @@ var ivpnAccountID = regexp.MustCompile(`^(i|ivpn)\-[a-zA-Z0-9]{4}\-[a-zA-Z0-9]{4
func (o OpenVPN) validate(vpnProvider string) (err error) {
// Validate version
validVersions := []string{openvpn.Openvpn25, openvpn.Openvpn26}
validVersions := []string{openvpn.Openvpn24}
if err = validate.IsOneOf(o.Version, validVersions...); err != nil {
return fmt.Errorf("%w: %w", ErrOpenVPNVersionIsNotValid, err)
}
@@ -289,7 +289,7 @@ func (o *OpenVPN) overrideWith(other OpenVPN) {
}
func (o *OpenVPN) setDefaults(vpnProvider string) {
o.Version = gosettings.DefaultComparable(o.Version, openvpn.Openvpn26)
o.Version = gosettings.DefaultComparable(o.Version, openvpn.Openvpn24)
o.User = gosettings.DefaultPointer(o.User, "")
if vpnProvider == providers.Mullvad {
o.Password = gosettings.DefaultPointer(o.Password, "m")

View File

@@ -4,9 +4,7 @@ import (
"fmt"
"net/netip"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
"github.com/qdm12/gluetun/internal/constants/providers"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/pprof"
"github.com/qdm12/gosettings/reader"
@@ -162,18 +160,6 @@ func (s Settings) Warnings() (warnings []string) {
" so this will likely not work anymore. See https://github.com/qdm12/gluetun/issues/1498.")
}
if helpers.IsOneOf(s.VPN.Provider.Name, providers.SlickVPN) &&
s.VPN.Type == vpn.OpenVPN {
warnings = append(warnings, "OpenVPN 2.5 and 2.6 use OpenSSL 3 "+
"which prohibits the usage of weak security in today's standards. "+
s.VPN.Provider.Name+" uses weak security which is out "+
"of Gluetun's control so the only workaround is to allow such weaknesses "+
`using the OpenVPN option tls-cipher "DEFAULT:@SECLEVEL=0". `+
"You might want to reach to your provider so they upgrade their certificates. "+
"Once this is done, you will have to let the Gluetun maintainers know "+
"by creating an issue, attaching the new certificate and we will update Gluetun.")
}
// TODO remove in v4
if s.DNS.ServerAddress.Unmap().Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
warnings = append(warnings, "DNS address is set to "+s.DNS.ServerAddress.String()+

View File

@@ -30,7 +30,7 @@ func Test_Settings_String(t *testing.T) {
| | ├── Protocol: UDP
| | └── Private Internet Access encryption preset: strong
| └── OpenVPN settings:
| ├── OpenVPN version: 2.6
| ├── OpenVPN version: 2.4
| ├── User: [not set]
| ├── Password: [not set]
| ├── Private Internet Access encryption preset: strong

View File

@@ -1,6 +1,5 @@
package openvpn
const (
Openvpn25 = "2.5"
Openvpn26 = "2.6"
Openvpn24 = "2.4"
)

View File

@@ -32,11 +32,6 @@ type Route struct {
Type int
}
func (r Route) String() string {
return fmt.Sprintf("{link %d, dst %s, src %s, gw %s, priority %d, family %d, table %d, type %d}",
r.LinkIndex, r.Dst, r.Src, r.Gw, r.Priority, r.Family, r.Table, r.Type)
}
type Rule struct {
Priority int
Family int

View File

@@ -13,8 +13,7 @@ import (
var ErrVersionUnknown = errors.New("OpenVPN version is unknown")
const (
binOpenvpn25 = "openvpn2.5"
binOpenvpn26 = "openvpn2.6"
binOpenvpn24 = "openvpn2.4"
)
func start(ctx context.Context, starter CmdStarter, version string, flags []string) (
@@ -22,10 +21,8 @@ func start(ctx context.Context, starter CmdStarter, version string, flags []stri
) {
var bin string
switch version {
case openvpn.Openvpn25:
bin = binOpenvpn25
case openvpn.Openvpn26:
bin = binOpenvpn26
case openvpn.Openvpn24:
bin = binOpenvpn24
default:
return nil, nil, nil, fmt.Errorf("%w: %s", ErrVersionUnknown, version)
}

View File

@@ -8,12 +8,8 @@ import (
"strings"
)
func (c *Configurator) Version25(ctx context.Context) (version string, err error) {
return c.version(ctx, binOpenvpn25)
}
func (c *Configurator) Version26(ctx context.Context) (version string, err error) {
return c.version(ctx, binOpenvpn26)
func (c *Configurator) Version24(ctx context.Context) (version string, err error) {
return c.version(ctx, binOpenvpn24)
}
var ErrVersionTooShort = errors.New("version output is too short")

View File

@@ -28,6 +28,8 @@ func (p *Provider) OpenVPNConfig(connection models.Connection,
}
switch settings.Version {
case openvpn.Openvpn24:
providerSettings.Ciphers = []string{openvpn.AES256cbc}
case openvpn.Openvpn25, openvpn.Openvpn26:
providerSettings.Ciphers = []string{
openvpn.AES256gcm, openvpn.AES256cbc, openvpn.AES192gcm,

View File

@@ -64,8 +64,8 @@ func Test_modifyConfig(t *testing.T) {
"suppress-timestamps",
"auth-user-pass /etc/openvpn/auth.conf",
"verb 0",
"data-ciphers-fallback cipher",
"data-ciphers cipher",
"cipher cipher", //nolint:dupword
"ncp-ciphers cipher",
"auth sha512",
"mssfix 1000",
"pull-filter ignore \"route-ipv6\"",

View File

@@ -31,11 +31,5 @@ func (p *Provider) OpenVPNConfig(connection models.Connection,
},
}
// SlickVPN's certificate is sha1WithRSAEncryption and sha1 is now
// rejected by openssl 3.x.x which is used by OpenVPN >= 2.5.
// We lower the security level to 3 to allow this algorithm,
// see https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html
providerSettings.TLSCipher = "DEFAULT:@SECLEVEL=0"
return utils.OpenVPNConfig(providerSettings, connection, settings, ipv6Supported)
}

View File

@@ -10,7 +10,7 @@ func CipherLines(ciphers []string) (lines []string) {
}
return []string{
"data-ciphers-fallback " + ciphers[0],
"data-ciphers " + strings.Join(ciphers, ":"),
"cipher " + ciphers[0],
"ncp-ciphers " + strings.Join(ciphers, ":"),
}
}

View File

@@ -16,16 +16,16 @@ func Test_CipherLines(t *testing.T) {
"empty version": {
ciphers: []string{"AES"},
lines: []string{
"data-ciphers-fallback AES",
"data-ciphers AES",
"cipher AES",
"ncp-ciphers AES",
},
},
"2.5": {
"2.4": {
ciphers: []string{"AES", "CBC"},
version: "2.5",
version: "2.4",
lines: []string{
"data-ciphers-fallback AES",
"data-ciphers AES:CBC",
"cipher AES",
"ncp-ciphers AES:CBC",
},
},
}

View File

@@ -3,11 +3,7 @@ package api
import (
"errors"
"fmt"
"maps"
"net/http"
"net/url"
"regexp"
"slices"
"strings"
)
@@ -20,8 +16,6 @@ const (
IP2Location Provider = "ip2location"
)
const echoipPrefix = "echoip#"
type NameToken struct {
Name string
Token string
@@ -36,19 +30,15 @@ func New(nameTokenPairs []NameToken, client *http.Client) (
if err != nil {
return nil, fmt.Errorf("parsing API name: %w", err)
}
switch {
case provider == Cloudflare:
switch provider {
case Cloudflare:
fetchers[i] = newCloudflare(client)
case provider == IfConfigCo:
const ifConfigCoURL = "https://ifconfig.co"
fetchers[i] = newEchoip(client, ifConfigCoURL)
case provider == IPInfo:
case IfConfigCo:
fetchers[i] = newIfConfigCo(client)
case IPInfo:
fetchers[i] = newIPInfo(client, nameTokenPair.Token)
case provider == IP2Location:
case IP2Location:
fetchers[i] = newIP2Location(client, nameTokenPair.Token)
case strings.HasPrefix(string(provider), echoipPrefix):
url := strings.TrimPrefix(string(provider), echoipPrefix)
fetchers[i] = newEchoip(client, url)
default:
panic("provider not valid: " + provider)
}
@@ -56,88 +46,20 @@ func New(nameTokenPairs []NameToken, client *http.Client) (
return fetchers, nil
}
var regexEchoipURL = regexp.MustCompile(`^http(s|):\/\/.+$`)
var ErrProviderNotValid = errors.New("API name is not valid")
func ParseProvider(s string) (provider Provider, err error) {
possibleProviders := []Provider{
Cloudflare,
IfConfigCo,
IP2Location,
IPInfo,
switch strings.ToLower(s) {
case "cloudflare":
return Cloudflare, nil
case string(IfConfigCo):
return IfConfigCo, nil
case "ipinfo":
return IPInfo, nil
case "ip2location":
return IP2Location, nil
default:
return "", fmt.Errorf(`%w: %q can only be "cloudflare", "ifconfigco", "ip2location" or "ipinfo"`,
ErrProviderNotValid, s)
}
stringToProvider := make(map[string]Provider, len(possibleProviders))
for _, provider := range possibleProviders {
stringToProvider[string(provider)] = provider
}
provider, ok := stringToProvider[strings.ToLower(s)]
if ok {
return provider, nil
}
customPrefixToURLRegex := map[string]*regexp.Regexp{
echoipPrefix: regexEchoipURL,
}
for prefix, urlRegex := range customPrefixToURLRegex {
match, err := checkCustomURL(s, prefix, urlRegex)
if !match {
continue
} else if err != nil {
return "", err
}
return Provider(s), nil
}
providerStrings := make([]string, 0, len(stringToProvider)+len(customPrefixToURLRegex))
for _, providerString := range slices.Sorted(maps.Keys(stringToProvider)) {
providerStrings = append(providerStrings, `"`+providerString+`"`)
}
for _, prefix := range slices.Sorted(maps.Keys(customPrefixToURLRegex)) {
providerStrings = append(providerStrings, "a custom "+prefix+" url")
}
return "", fmt.Errorf(`%w: %q can only be %s`,
ErrProviderNotValid, s, orStrings(providerStrings))
}
var ErrCustomURLNotValid = errors.New("custom URL is not valid")
func checkCustomURL(s, prefix string, regex *regexp.Regexp) (match bool, err error) {
if !strings.HasPrefix(s, prefix) {
return false, nil
}
s = strings.TrimPrefix(s, prefix)
_, err = url.Parse(s)
if err != nil {
return true, fmt.Errorf("%s %w: %w", prefix, ErrCustomURLNotValid, err)
}
if regex.MatchString(s) {
return true, nil
}
return true, fmt.Errorf("%s %w: %q does not match regular expression: %s",
prefix, ErrCustomURLNotValid, s, regex)
}
func orStrings(strings []string) (result string) {
return joinStrings(strings, "or")
}
func joinStrings(strings []string, lastJoin string) (result string) {
if len(strings) == 0 {
return ""
}
result = strings[0]
for i := 1; i < len(strings); i++ {
if i < len(strings)-1 {
result += ", " + strings[i]
} else {
result += " " + lastJoin + " " + strings[i]
}
}
return result
}

View File

@@ -1,68 +0,0 @@
package api
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_ParseProvider(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
s string
provider Provider
errWrapped error
errMessage string
}{
"empty": {
errWrapped: ErrProviderNotValid,
errMessage: `API name is not valid: "" can only be ` +
`"cloudflare", "ifconfigco", "ip2location", "ipinfo" or a custom echoip# url`,
},
"invalid": {
s: "xyz",
errWrapped: ErrProviderNotValid,
errMessage: `API name is not valid: "xyz" can only be ` +
`"cloudflare", "ifconfigco", "ip2location", "ipinfo" or a custom echoip# url`,
},
"ipinfo": {
s: "ipinfo",
provider: IPInfo,
},
"IpInfo": {
s: "IpInfo",
provider: IPInfo,
},
"echoip_url_empty": {
s: "echoip#",
errWrapped: ErrCustomURLNotValid,
errMessage: `echoip# custom URL is not valid: "" ` +
`does not match regular expression: ^http(s|):\/\/.+$`,
},
"echoip_url_invalid": {
s: "echoip#postgres://localhost:3451",
errWrapped: ErrCustomURLNotValid,
errMessage: `echoip# custom URL is not valid: "postgres://localhost:3451" ` +
`does not match regular expression: ^http(s|):\/\/.+$`,
},
"echoip_url_valid": {
s: "echoip#http://localhost:3451",
provider: Provider("echoip#http://localhost:3451"),
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()
provider, err := ParseProvider(testCase.s)
assert.Equal(t, testCase.provider, provider)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
assert.EqualError(t, err, testCase.errMessage)
}
})
}
}

View File

@@ -6,45 +6,39 @@ import (
"fmt"
"net/http"
"net/netip"
"strings"
"github.com/qdm12/gluetun/internal/models"
)
type echoip struct {
type ifConfigCo struct {
client *http.Client
url string
}
func newEchoip(client *http.Client, url string) *echoip {
return &echoip{
func newIfConfigCo(client *http.Client) *ifConfigCo {
return &ifConfigCo{
client: client,
url: url,
}
}
func (e *echoip) String() string {
s := e.url
s = strings.TrimPrefix(s, "http://")
s = strings.TrimPrefix(s, "https://")
return s
func (i *ifConfigCo) String() string {
return string(IfConfigCo)
}
func (e *echoip) CanFetchAnyIP() bool {
func (i *ifConfigCo) CanFetchAnyIP() bool {
return true
}
func (e *echoip) Token() string {
func (i *ifConfigCo) Token() string {
return ""
}
// FetchInfo obtains information on the ip address provided
// using the echoip API at the url given. If the ip is the zero value,
// using the ifconfig.co/json API. If the ip is the zero value,
// the public IP address of the machine is used as the IP.
func (e *echoip) FetchInfo(ctx context.Context, ip netip.Addr) (
func (i *ifConfigCo) FetchInfo(ctx context.Context, ip netip.Addr) (
result models.PublicIP, err error,
) {
url := e.url + "/json"
url := "https://ifconfig.co/json"
if ip.IsValid() {
url += "?ip=" + ip.String()
}
@@ -54,7 +48,7 @@ func (e *echoip) FetchInfo(ctx context.Context, ip netip.Addr) (
return result, err
}
response, err := e.client.Do(request)
response, err := i.client.Do(request)
if err != nil {
return result, err
}

View File

@@ -67,7 +67,6 @@ type NetLinker interface {
type Router interface {
RouteList(family int) (routes []netlink.Route, err error)
RouteAdd(route netlink.Route) error
RouteReplace(route netlink.Route) error
}
type Ruler interface {

View File

@@ -38,7 +38,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
l.openvpnConf, providerConf, settings, l.ipv6Supported, l.starter, subLogger)
} else { // Wireguard
vpnInterface = settings.Wireguard.Interface
vpnRunner, serverName, canPortForward, err = setupWireguard(ctx, l.netLinker, l.routing, l.fw,
vpnRunner, serverName, canPortForward, err = setupWireguard(ctx, l.netLinker, l.fw,
providerConf, settings, l.ipv6Supported, subLogger)
}
if err != nil {

View File

@@ -13,7 +13,7 @@ import (
// setupWireguard sets Wireguard up using the configurators and settings given.
// It returns a serverName for port forwarding (PIA) and an error if it fails.
func setupWireguard(ctx context.Context, netlinker NetLinker, routing Routing,
func setupWireguard(ctx context.Context, netlinker NetLinker,
fw Firewall, providerConf provider.Provider,
settings settings.VPN, ipv6Supported bool, logger wireguard.Logger) (
wireguarder *wireguard.Wireguard, serverName string, canPortForward bool, err error,
@@ -29,7 +29,7 @@ func setupWireguard(ctx context.Context, netlinker NetLinker, routing Routing,
logger.Debug("Wireguard client private key: " + gosettings.ObfuscateKey(wireguardSettings.PrivateKey))
logger.Debug("Wireguard pre-shared key: " + gosettings.ObfuscateKey(wireguardSettings.PreSharedKey))
wireguarder, err = wireguard.New(wireguardSettings, netlinker, routing, logger)
wireguarder, err = wireguard.New(wireguardSettings, netlinker, logger)
if err != nil {
return nil, "", false, fmt.Errorf("creating Wireguard: %w", err)
}

View File

@@ -4,11 +4,10 @@ type Wireguard struct {
logger Logger
settings Settings
netlink NetLinker
routing Routing
}
func New(settings Settings, netlink NetLinker,
routing Routing, logger Logger,
logger Logger,
) (w *Wireguard, err error) {
settings.SetDefaults()
if err := settings.Check(); err != nil {
@@ -19,6 +18,5 @@ func New(settings Settings, netlink NetLinker,
logger: logger,
settings: settings,
netlink: netlink,
routing: routing,
}, nil
}

View File

@@ -1,7 +0,0 @@
package wireguard
import "net/netip"
type Routing interface {
VPNLocalGatewayIP(vpnInterface string) (gateway netip.Addr, err error)
}

View File

@@ -1,8 +1,6 @@
package wireguard
import (
"github.com/qdm12/gluetun/internal/netlink"
)
import "github.com/qdm12/gluetun/internal/netlink"
//go:generate mockgen -destination=netlinker_mock_test.go -package wireguard . NetLinker
@@ -17,7 +15,6 @@ type NetLinker interface {
type Router interface {
RouteList(family int) (routes []netlink.Route, err error)
RouteAdd(route netlink.Route) error
RouteReplace(route netlink.Route) error
}
type Ruler interface {

View File

@@ -1,7 +1,6 @@
package wireguard
import (
"errors"
"fmt"
"net/netip"
"strings"
@@ -30,10 +29,6 @@ func (w *Wireguard) addRoutes(link netlink.Link, destinations []netip.Prefix,
return nil
}
var (
ErrDefaultRouteNotFound = errors.New("default route not found")
)
func (w *Wireguard) addRoute(link netlink.Link, dst netip.Prefix,
firewallMark uint32,
) (err error) {
@@ -50,39 +45,5 @@ func (w *Wireguard) addRoute(link netlink.Link, dst netip.Prefix,
link.Name, dst, firewallMark, err)
}
vpnGatewayIP, err := w.routing.VPNLocalGatewayIP(link.Name)
if err != nil {
return fmt.Errorf("getting VPN gateway IP: %w", err)
}
routes, err := w.netlink.RouteList(netlink.FamilyV4)
if err != nil {
return fmt.Errorf("listing routes: %w", err)
}
var defaultRoute netlink.Route
var defaultRouteFound bool
for _, route = range routes {
if !route.Dst.IsValid() || route.Dst.Addr().IsUnspecified() {
defaultRoute = route
defaultRouteFound = true
break
}
}
if !defaultRouteFound {
return fmt.Errorf("%w: in %d routes", ErrDefaultRouteNotFound, len(routes))
}
// Equivalent replacement to:
// ip route replace default via <vpn-gateway> dev tun0
defaultRoute.Gw = vpnGatewayIP
defaultRoute.LinkIndex = link.Index
err = w.netlink.RouteReplace(defaultRoute)
if err != nil {
return fmt.Errorf("replacing default route: %w", err)
}
return err
}