Compare commits

...

6 Commits

Author SHA1 Message Date
Quentin McGaw 7f22fb3276 fix(protonvpn): support port 51820 for UDP OpenVPN 2026-02-11 14:13:34 +00:00
Quentin McGaw 6909a0c123 fix(healthcheck): prevent race condition and fix #3096 (#3123) 2026-02-11 14:12:20 +00:00
Quentin McGaw 3e1f48932a fix(openvpn): only log openvpn version corresponding to OPENVPN_VERSION 2026-02-11 14:12:08 +00:00
Chris Duck 50744852c5 fix(protonvpn): update OpenVPN settings (#3120) 2026-02-11 14:11:57 +00:00
Quentin McGaw 09e52bc685 fix(httpproxy): remove info log when no Proxy-Authorization header is present 2026-02-11 14:11:46 +00:00
Quentin McGaw 857fe425ec fix(wireguard): fix detection of kernelspace wireguard 2026-02-11 14:11:36 +00:00
14 changed files with 50 additions and 71 deletions
+7 -3
View File
@@ -22,6 +22,7 @@ import (
"github.com/qdm12/gluetun/internal/configuration/sources/files"
"github.com/qdm12/gluetun/internal/configuration/sources/secrets"
"github.com/qdm12/gluetun/internal/constants"
copenvpn "github.com/qdm12/gluetun/internal/constants/openvpn"
"github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/healthcheck"
@@ -269,11 +270,14 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
ovpnConf := openvpn.New(
logger.New(log.SetComponent("openvpn configurator")),
cmder, puid, pgid)
ovpnVersion := ovpnConf.Version26
if allSettings.VPN.OpenVPN.Version == copenvpn.Openvpn25 {
ovpnVersion = ovpnConf.Version25
}
err = printVersions(ctx, logger, []printVersionElement{
{name: "Alpine", getVersion: alpineConf.Version},
{name: "OpenVPN 2.5", getVersion: ovpnConf.Version25},
{name: "OpenVPN 2.6", getVersion: ovpnConf.Version26},
{name: "OpenVPN", getVersion: ovpnVersion},
{name: "IPtables", getVersion: firewallConf.Version},
})
if err != nil {
@@ -549,7 +553,7 @@ type netLinker interface {
Router
Ruler
Linker
IsWireguardSupported() (ok bool, err error)
IsWireguardSupported() bool
IsIPv6Supported() (ok bool, err error)
PatchLoggerLevel(level log.Level)
}
@@ -104,7 +104,7 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) {
allowedUDP = []uint16{53, 1194, 1197, 1198, 8080, 9201}
case providers.Protonvpn:
allowedTCP = []uint16{443, 5995, 8443}
allowedUDP = []uint16{80, 443, 1194, 4569, 5060}
allowedUDP = []uint16{80, 443, 1194, 4569, 5060, 51820}
case providers.SlickVPN:
allowedTCP = []uint16{443, 8080, 8888}
allowedUDP = []uint16{443, 8080, 8888}
+10 -2
View File
@@ -106,14 +106,22 @@ func (c *Checker) Start(ctx context.Context) (runError <-chan error, err error)
if err != nil {
err = fmt.Errorf("small periodic check: %w", err)
}
runErrorCh <- err
select {
case <-ctx.Done():
continue
case runErrorCh <- err:
}
smallCheckTimer.Reset(smallCheckPeriod)
case <-fullCheckTimer.C:
err := c.fullPeriodicCheck(ctx)
if err != nil {
err = fmt.Errorf("full periodic check: %w", err)
}
runErrorCh <- err
select {
case <-ctx.Done():
continue
case runErrorCh <- err:
}
fullCheckTimer.Reset(fullCheckPeriod)
}
}
-1
View File
@@ -13,7 +13,6 @@ func (h *handler) isAuthorized(responseWriter http.ResponseWriter, request *http
}
basicAuth := request.Header.Get("Proxy-Authorization")
if basicAuth == "" {
h.logger.Info("Proxy-Authorization header not found from " + request.RemoteAddr)
responseWriter.Header().Set("Proxy-Authenticate", `Basic realm="Access to Gluetun over HTTP"`)
responseWriter.WriteHeader(http.StatusProxyAuthRequired)
return false
+11 -1
View File
@@ -1,8 +1,18 @@
package netlink
import "net/netip"
import (
"net/netip"
"github.com/qdm12/log"
)
func makeNetipPrefix(n byte) netip.Prefix {
const bits = 24
return netip.PrefixFrom(netip.AddrFrom4([4]byte{n, n, n, 0}), bits)
}
type noopLogger struct{}
func (l *noopLogger) Debug(_ string) {}
func (l *noopLogger) Debugf(_ string, _ ...any) {}
func (l *noopLogger) Patch(_ ...log.Option) {}
+9 -26
View File
@@ -3,52 +3,35 @@
package netlink
import (
"fmt"
"github.com/qdm12/gluetun/internal/mod"
"github.com/vishvananda/netlink"
)
func (n *NetLink) IsWireguardSupported() (ok bool, err error) {
func (n *NetLink) IsWireguardSupported() bool {
// Check for Wireguard family without loading the wireguard module.
// Some kernels have the wireguard module built-in, and don't have a
// modules directory, such as WSL2 kernels.
ok, err = hasWireguardFamily()
if err != nil {
return false, fmt.Errorf("checking for wireguard family: %w", err)
}
ok := hasWireguardFamily()
if ok {
return true, nil
return true
}
// Try loading the wireguard module, since some systems do not load
// it after a boot. If this fails, wireguard is assumed to not be supported.
n.debugLogger.Debugf("wireguard family not found, trying to load wireguard kernel module")
err = mod.Probe("wireguard")
err := mod.Probe("wireguard")
if err != nil {
n.debugLogger.Debugf("failed loading wireguard kernel module: %s", err)
return false, nil
return false
}
n.debugLogger.Debugf("wireguard kernel module loaded successfully")
// Re-check if the Wireguard family is now available, after loading
// the wireguard kernel module.
ok, err = hasWireguardFamily()
if err != nil {
return false, fmt.Errorf("checking for wireguard family: %w", err)
}
return ok, nil
return hasWireguardFamily()
}
func hasWireguardFamily() (ok bool, err error) {
families, err := netlink.GenlFamilyList()
if err != nil {
return false, fmt.Errorf("listing gen 1 families: %w", err)
}
for _, family := range families {
if family.Name == "wireguard" {
return true, nil
}
}
return false, nil
func hasWireguardFamily() bool {
_, err := netlink.GenlFamilyGet("wireguard")
return err == nil
}
+5 -7
View File
@@ -4,17 +4,15 @@ package netlink
import (
"testing"
"github.com/stretchr/testify/require"
)
func Test_NetLink_IsWireguardSupported(t *testing.T) {
t.Skip() // TODO unskip once the data race problem with netlink.GenlFamilyList() is fixed
t.Parallel()
netLink := &NetLink{}
ok, err := netLink.IsWireguardSupported()
require.NoError(t, err)
netLink := &NetLink{
debugLogger: &noopLogger{},
}
ok := netLink.IsWireguardSupported()
if ok { // cannot assert since this depends on kernel
t.Log("wireguard is supported")
} else {
+2 -3
View File
@@ -15,7 +15,6 @@ func (p *Provider) OpenVPNConfig(connection models.Connection,
RemoteCertTLS: true,
AuthUserPass: true,
Ciphers: []string{
openvpn.AES256cbc,
openvpn.AES256gcm,
},
Auth: openvpn.SHA512,
@@ -23,8 +22,8 @@ func (p *Provider) OpenVPNConfig(connection models.Connection,
TunMTUExtra: 32,
RenegDisabled: true,
KeyDirection: "1",
CAs: []string{"MIIFozCCA4ugAwIBAgIBATANBgkqhkiG9w0BAQ0FADBAMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMUHJvdG9uVlBOIEFHMRowGAYDVQQDExFQcm90b25WUE4gUm9vdCBDQTAeFw0xNzAyMTUxNDM4MDBaFw0yNzAyMTUxNDM4MDBaMEAxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxQcm90b25WUE4gQUcxGjAYBgNVBAMTEVByb3RvblZQTiBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt+BsSsZg7+AuqTq7vDbPzfygtl9f8fLJqO4amsyOXlI7pquL5IsEZhpWyJIIvYybqS4s1/T7BbvHPLVEwlrq8A5DBIXcfuXrBbKoYkmpICGc2u1KYVGOZ9A+PH9z4Tr6OXFfXRnsbZToie8t2Xjv/dZDdUDAqeW89I/mXg3k5x08m2nfGCQDm4gCanN1r5MT7ge56z0MkY3FFGCOqRwspIEUzu1ZqGSTkG1eQiOYIrdOF5cc7n2APyvBIcfvp/W3cpTOEmEBJ7/14RnXnHo0fcx61Inx/6ZxzKkW8BMdGGQF3tF6u2M0FjVN0lLH9S0ul1TgoOS56yEJ34hrJSRTqHuar3t/xdCbKFZjyXFZFNsXVvgJu34CNLrHHTGJj9jiUfFnxWQYMo9UNUd4a3PPG1HnbG7LAjlvj5JlJ5aqO5gshdnqb9uIQeR2CdzcCJgklwRGCyDT1pm7eoivWV19YBd81vKulLzgPavu3kRRe83yl29It2hwQ9FMs5w6ZV/X6ciTKo3etkX9nBD9ZzJPsGQsBUy7CzO1jK4W01+u3ItmQS+1s4xtcFxdFY8o/q1zoqBlxpe5MQIWN6QalryiET74gMHE/S5WrPlsq/gehxsdgc6GDUXG4dk8vn6OUMa6wb5wRO3VXGEc67IYm4mDFTYiPvLaFOxtndlUWuCruKcCAwEAAaOBpzCBpDAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBSDkIaYhLVZTwyLNTetNB2qV0gkVDBoBgNVHSMEYTBfgBSDkIaYhLVZTwyLNTetNB2qV0gkVKFEpEIwQDELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFByb3RvblZQTiBBRzEaMBgGA1UEAxMRUHJvdG9uVlBOIFJvb3QgQ0GCAQEwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQCYr7LpvnfZXBCxVIVc2ea1fjxQ6vkTj0zMhtFs3qfeXpMRf+g1NAh4vv1UIwLsczilMt87SjpJ25pZPyS3O+/VlI9ceZMvtGXdMGfXhTDp//zRoL1cbzSHee9tQlmEm1tKFxB0wfWd/inGRjZxpJCTQh8oc7CTziHZufS+Jkfpc4Rasr31fl7mHhJahF1j/ka/OOWmFbiHBNjzmNWPQInJm+0ygFqij5qs51OEvubR8yh5Mdq4TNuWhFuTxpqoJ87VKaSOx/Aefca44Etwcj4gHb7LThidw/kyzysZiWjyrbfX/31RX7QanKiMk2RDtgZaWi/lMfsl5O+6E2lJ1vo4xv9pW8225B5XeAeXHCfjV/vrrCFqeCprNF6a3Tn/LX6VNy3jbeC+167QagBOaoDA01XPOx7OdhsbGd7cJ5VkgyycZgLnT9zrChgwjx59JQosFEG1DsaAgHfpEl/N3YPJh68N7fwN41Cjzsk39v6iZdfuet/sP7oiP5/gLmA/CIPNhdIYxaojbLjFPkftVjVPn49RqwqzJJPRN8BOyb94yhQ7KO4F3IcLT/y/dsWitY0ZH4lCnAVV/v2YjWAWS3OWyC8BFx/Jmc3WDK/yPwECUcPgHIeXiRjHnJt0Zcm23O2Q3RphpU+1SO3XixsXpOVOYP6rJIXW9bMZA1gTTlpi7A=="}, //nolint:lll
TLSAuth: "6acef03f62675b4b1bbd03e53b187727423cea742242106cb2916a8a4c8297563d22c7e5cef430b1103c6f66eb1fc5b375a672f158e2e2e936c3faa48b035a6de17beaac23b5f03b10b868d53d03521d8ba115059da777a60cbfd7b2c9c5747278a15b8f6e68a3ef7fd583ec9f398c8bd4735dab40cbd1e3c62a822e97489186c30a0b48c7c38ea32ceb056d3fa5a710e10ccc7a0ddb363b08c3d2777a3395e10c0b6080f56309192ab5aacd4b45f55da61fc77af39bd81a19218a79762c33862df55785075f37d8c71dc8a42097ee43344739a0dd48d03025b0450cf1fb5e8caeb893d9a96d1f15519bb3c4dcb40ee316672ea16c012664f8a9f11255518deb", //nolint:lll
CAs: []string{"MIIFnTCCA4WgAwIBAgIUCI574SM3Lyh47GyNl0WAOYrqb5QwDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCQ0gxHzAdBgNVBAoMFlByb3RvbiBUZWNobm9sb2dpZXMgQUcxEjAQBgNVBAsMCVByb3RvblZQTjEaMBgGA1UEAwwRUHJvdG9uVlBOIFJvb3QgQ0EwHhcNMTkxMDE3MDgwNjQxWhcNMzkxMDEyMDgwNjQxWjBeMQswCQYDVQQGEwJDSDEfMB0GA1UECgwWUHJvdG9uIFRlY2hub2xvZ2llcyBBRzESMBAGA1UECwwJUHJvdG9uVlBOMRowGAYDVQQDDBFQcm90b25WUE4gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMkUT7zMUS5C+NjQ7YoGpVFlfbN9HFgG4JiKfHB8QxnPPRgyTi0zVOAj1ImsRilauY8Ddm5dQtd8qcApoz6oCx5cFiiSQG2uyhS/59Zl5wqIkw1o+CgwZgeWkq04lcrxhhfPgJZRFjrYVezy/Z2Ssd18s3/FFNQ+2iV1KC2Kz8eSPr50u+l9vEKsKiNGkJTdlWjoDKZM2C15i/h8Smi+PdJlx7WMTtYoVC1Fzq0raCPDQl18kspu11b6d8ECPWghKcDIIKuA0r0nGqF1GvH1AmbC/xUaNrKgz9AfioZLMP/l22tVG3KKM1ku0eYHX7NzNHgkM2JKnBBannImQQBGTAcvvUlnfF3AHx4vzx7HahpBz8ebThx2uv+vzu8lCVEcKjQObGwLbAONJN2enug8hwSSZQv7tz7onDQWlYh0El5fnkrEQGbukNnSyOqTwfobvBllIPzBqdO38eZFA0YTlH9plYjIjPjGl931lFAA3G9t0x7nxAauLXN5QVp1yoF1tzXc5kN0SFAasM9VtVEOSMaGHLKhF+IMyVX8h5IuIRC8u5O672r7cHS+Dtx87LjxypqNhmbf1TWyLJSoh0qYhMr+BbO7+N6zKRIZPI5bMXc8Be2pQwbSA4ZrDvSjFC9yDXmSuZTyVo6Bqi/KCUZeaXKof68oNxVYeGowNeQdg/znAgMBAAGjUzBRMB0GA1UdDgQWBBR44WtTuEKCaPPUltYEHZoyhJo+4TAfBgNVHSMEGDAWgBR44WtTuEKCaPPUltYEHZoyhJo+4TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBBmzCQlHxOJ6izys3TVpaze+rUkA9GejgsB2DZXIcm4Lj/SNzQsPlZRu4S0IZV253dbE1DoWlHanw5lnXwx8iU82X7jdm/5uZOwj2NqSqTbTn0WLAC6khEKKe5bPTf18UOcwN82Le3AnkwcNAaBO5/TzFQVgnVedXr2g6rmpp9gdedeEl9acB7xqfYfkrmijqYMm+xeG2rXaanch3HjweMDuZdT/Ub5G6oir0KowftlA1ytjXRg+X+yWymTpF/zGLYfSodWWjMKhpzZtRJZ+9B0pWXUyY7SuCj5T5SMIAux3NQQ46wSbHRolIlwh7zD7kBgkyLe7ByLvGFKa2Vw4PuWjqYwrRbFjb2+EKAwPu6VTWz/QQTU8oJewGFipw94Bi61zuaPvF1qZCHgYhVojRy6KcqncX2Hx9hjfVxspBZDrVH6uofCmd99GmVu+qizybWQTrPaubfc/a2jJIbXc2bRQjYj/qmjE3hTlmO3k7VEP6i8CLhEl+dX75aZw9StkqjdpIApYwX6XNDqVuGzfeTXXclk4N4aDPwPFM/Yo/eKnvlNlKbljWdMYkfx8r37aOHpchH34cv0Jb5Im+1H07ywnshXNfUhRazOpubJRHnbjDuBwWS1/Vwp5AJ+QHsPXhJdl3qHc1szJZVJb3VyAWvG/bWApKfFuZX18tiI4N0EA=="}, //nolint:lll
TLSCrypt: "6acef03f62675b4b1bbd03e53b187727423cea742242106cb2916a8a4c8297563d22c7e5cef430b1103c6f66eb1fc5b375a672f158e2e2e936c3faa48b035a6de17beaac23b5f03b10b868d53d03521d8ba115059da777a60cbfd7b2c9c5747278a15b8f6e68a3ef7fd583ec9f398c8bd4735dab40cbd1e3c62a822e97489186c30a0b48c7c38ea32ceb056d3fa5a710e10ccc7a0ddb363b08c3d2777a3395e10c0b6080f56309192ab5aacd4b45f55da61fc77af39bd81a19218a79762c33862df55785075f37d8c71dc8a42097ee43344739a0dd48d03025b0450cf1fb5e8caeb893d9a96d1f15519bb3c4dcb40ee316672ea16c012664f8a9f11255518deb", //nolint:lll
UDPLines: []string{
"fast-io",
},
-15
View File
@@ -63,21 +63,6 @@ func (mr *MockNetLinkerMockRecorder) AddrReplace(arg0, arg1 interface{}) *gomock
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrReplace", reflect.TypeOf((*MockNetLinker)(nil).AddrReplace), arg0, arg1)
}
// IsWireguardSupported mocks base method.
func (m *MockNetLinker) IsWireguardSupported() (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IsWireguardSupported")
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// IsWireguardSupported indicates an expected call of IsWireguardSupported.
func (mr *MockNetLinkerMockRecorder) IsWireguardSupported() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWireguardSupported", reflect.TypeOf((*MockNetLinker)(nil).IsWireguardSupported))
}
// LinkAdd mocks base method.
func (m *MockNetLinker) LinkAdd(arg0 netlink.Link) (int, error) {
m.ctrl.T.Helper()
-1
View File
@@ -12,7 +12,6 @@ type NetLinker interface {
Router
Ruler
Linker
IsWireguardSupported() (ok bool, err error)
}
type Addresser interface {
+1 -1
View File
@@ -61,7 +61,7 @@ type NetLinker interface {
Router
Ruler
Linker
IsWireguardSupported() (ok bool, err error)
IsWireguardSupported() bool
}
type Router interface {
+1 -1
View File
@@ -9,7 +9,7 @@ type NetLinker interface {
Router
Ruler
Linker
IsWireguardSupported() (ok bool, err error)
IsWireguardSupported() bool
}
type Router interface {
+2 -3
View File
@@ -49,12 +49,11 @@ func (mr *MockNetLinkerMockRecorder) AddrReplace(arg0, arg1 interface{}) *gomock
}
// IsWireguardSupported mocks base method.
func (m *MockNetLinker) IsWireguardSupported() (bool, error) {
func (m *MockNetLinker) IsWireguardSupported() bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IsWireguardSupported")
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
return ret0
}
// IsWireguardSupported indicates an expected call of IsWireguardSupported.
+1 -6
View File
@@ -16,7 +16,6 @@ import (
)
var (
ErrDetectKernel = errors.New("cannot detect Kernel support")
ErrCreateTun = errors.New("cannot create TUN device")
ErrAddLink = errors.New("cannot add Wireguard link")
ErrFindLink = errors.New("cannot find link")
@@ -35,11 +34,7 @@ var (
// See https://git.zx2c4.com/wireguard-go/tree/main.go
func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan<- struct{}) {
kernelSupported, err := w.netlink.IsWireguardSupported()
if err != nil {
waitError <- fmt.Errorf("%w: %s", ErrDetectKernel, err)
return
}
kernelSupported := w.netlink.IsWireguardSupported()
setupFunction := setupUserSpace
switch w.settings.Implementation {