Merge branch 'master' into nftables

This commit is contained in:
Quentin McGaw
2026-08-06 20:13:34 +00:00
491 changed files with 22568 additions and 314322 deletions
+65 -46
View File
@@ -2,7 +2,6 @@ package main
import (
"context"
"errors"
"fmt"
"io/fs"
"net/http"
@@ -16,7 +15,10 @@ import (
_ "time/tzdata"
_ "github.com/breml/rootcerts"
"github.com/qdm12/dns/v2/pkg/doh"
dnsprovider "github.com/qdm12/dns/v2/pkg/provider"
"github.com/qdm12/gluetun/internal/alpine"
"github.com/qdm12/gluetun/internal/boringpoll"
"github.com/qdm12/gluetun/internal/cli"
"github.com/qdm12/gluetun/internal/command"
"github.com/qdm12/gluetun/internal/configuration/settings"
@@ -39,8 +41,8 @@ import (
"github.com/qdm12/gluetun/internal/routing"
"github.com/qdm12/gluetun/internal/server"
"github.com/qdm12/gluetun/internal/shadowsocks"
"github.com/qdm12/gluetun/internal/socks5"
"github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/gluetun/internal/tun"
updater "github.com/qdm12/gluetun/internal/updater/loop"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
@@ -77,7 +79,6 @@ func main() {
logger := log.New(log.SetLevel(log.LevelInfo))
args := os.Args
tun := tun.New()
netLinkDebugLogger := logger.New(log.SetComponent("netlink"))
netLinker := netlink.New(netLinkDebugLogger)
cli := cli.New()
@@ -97,7 +98,7 @@ func main() {
errorCh := make(chan error)
go func() {
errorCh <- _main(ctx, buildInfo, args, logger, reader, tun, netLinker, cmder, cli)
errorCh <- _main(ctx, buildInfo, args, logger, reader, netLinker, cmder, cli)
}()
// Wait for OS signal or run error
@@ -139,12 +140,10 @@ func main() {
}
}
var errCommandUnknown = errors.New("command is unknown")
//nolint:gocognit,gocyclo,maintidx
func _main(ctx context.Context, buildInfo models.BuildInformation,
args []string, logger log.LoggerInterface, reader *reader.Reader,
tun Tun, netLinker netLinker, cmder RunStarter,
netLinker netLinker, cmder RunStarter,
cli clier,
) error {
if len(args) > 1 { // cli operation
@@ -162,13 +161,13 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
case "genkey":
return cli.GenKey(args[2:])
default:
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
return fmt.Errorf("command is unknown: %s", args[1])
}
}
defer fmt.Println(gluetunLogo)
announcementExp, err := time.Parse(time.RFC3339, "2026-04-01T00:00:00Z")
announcementExp, err := time.Parse(time.RFC3339, "2026-06-30T00:00:00Z")
if err != nil {
return err
}
@@ -179,7 +178,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Version: buildInfo.Version,
Commit: buildInfo.Commit,
Created: buildInfo.Created,
Announcement: "All control server routes are now private by default",
Announcement: "Your servers data files are now migrated to /gluetun/servers/",
AnnounceExp: announcementExp,
// Sponsor information
PaypalUser: "qmcgaw",
@@ -207,9 +206,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
netLinker.PatchLoggerLevel(logLevel)
routingLogger := logger.New(log.SetComponent("routing"))
if *allSettings.Firewall.Debug { // To remove in v4
routingLogger.Patch(log.SetLevel(log.LevelDebug))
}
routingConf := routing.New(netLinker, routingLogger)
defaultRoutes, err := routingConf.DefaultRoutes()
@@ -222,12 +218,12 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err
}
iptablesLogLevel, _ := log.ParseLevel(allSettings.Firewall.Iptables.LogLevel)
iptablesLogger := logger.New(log.SetComponent("iptables"), log.SetLevel(iptablesLogLevel))
firewallLogger := logger.New(log.SetComponent("firewall"))
if *allSettings.Firewall.Debug { // To remove in v4
firewallLogger.Patch(log.SetLevel(log.LevelDebug))
}
firewallConf, err := firewall.NewConfig(ctx, allSettings.Firewall.Implementation,
firewallLogger, cmder, defaultRoutes, localNetworks)
firewallLogger, iptablesLogger, cmder, defaultRoutes, localNetworks)
if err != nil {
return err
}
@@ -245,15 +241,19 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
// TODO run this in a loop or in openvpn to reload from file without restarting
storageLogger := logger.New(log.SetComponent("storage"))
storage, err := storage.New(storageLogger, *allSettings.Storage.Filepath)
storage, err := storage.New(storageLogger, *allSettings.Storage.ServersEnabled,
allSettings.Storage.ServersPath, allSettings.Storage.LegacyServersFilepath)
if err != nil {
return err
}
ipv6Supported, err := netLinker.IsIPv6Supported()
ipv6SupportLevel, err := netLinker.FindIPv6SupportLevel(ctx,
allSettings.IPv6.CheckAddresses, firewallConf)
if err != nil {
return fmt.Errorf("checking for IPv6 support: %w", err)
}
ipv6Supported := ipv6SupportLevel == netlink.IPv6Supported ||
ipv6SupportLevel == netlink.IPv6Internet
err = allSettings.Validate(storage, ipv6Supported, logger)
if err != nil {
@@ -340,19 +340,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return fmt.Errorf("adding local rules: %w", err)
}
const tunDevice = "/dev/net/tun"
err = tun.Check(tunDevice)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("checking TUN device: %w (see the Wiki errors/tun page)", err)
}
logger.Info(err.Error() + "; creating it...")
err = tun.Create(tunDevice)
if err != nil {
return fmt.Errorf("creating tun device: %w", err)
}
}
for _, port := range allSettings.Firewall.InputPorts {
for _, defaultRoute := range defaultRoutes {
err = firewallConf.SetAllowedPort(ctx, port, defaultRoute.NetInterface)
@@ -399,7 +386,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
dnsLogger := logger.New(log.SetComponent("dns"))
dnsLooper, err := dns.NewLoop(allSettings.DNS, httpClient,
dnsLogger)
dnsLogger, localNetworksToPrefixes(localNetworks))
if err != nil {
return fmt.Errorf("creating DNS loop: %w", err)
}
@@ -425,6 +412,18 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return fmt.Errorf("starting public ip loop: %w", err)
}
socks5Loop := socks5.NewLoop(socks5.Settings{
Enabled: *allSettings.Socks5.Enabled,
Username: *allSettings.Socks5.Username,
Password: *allSettings.Socks5.Password,
Address: allSettings.Socks5.ListeningAddress,
Logger: logger.New(log.SetComponent("socks5")),
})
socks5RunError, err := socks5Loop.Start(ctx)
if err != nil {
return fmt.Errorf("starting SOCKS5 server loop: %w", err)
}
healthLogger := logger.New(log.SetComponent("healthcheck"))
healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger)
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
@@ -432,20 +431,31 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
go healthcheckServer.Run(healthServerCtx, healthServerDone)
healthChecker := healthcheck.NewChecker(healthLogger)
// Note: we use a separate DoH dialer for the VPN servers data updater, separate from the
// main DNS local server to make sure no request is blocked by filters.
dohDialer, err := doh.New(doh.Settings{
UpstreamResolvers: []dnsprovider.Provider{dnsprovider.Cloudflare(), dnsprovider.Google()},
})
if err != nil {
return fmt.Errorf("creating updater DoH dialer: %w", err)
}
updaterLogger := logger.New(log.SetComponent("updater"))
unzipper := unzip.New(httpClient)
parallelResolver := resolver.NewParallelResolver(allSettings.Updater.DNSAddress)
parallelResolver := resolver.NewParallelResolver(dohDialer)
openvpnFileExtractor := extract.New()
providers := provider.NewProviders(storage, time.Now, updaterLogger,
httpClient, unzipper, parallelResolver, publicIPLooper.Fetcher(),
openvpnFileExtractor, allSettings.Updater)
boringPollLogger := logger.New(log.SetComponent("boring poll"))
boringPoll := boringpoll.New(httpClient, boringPollLogger, allSettings.BoringPoll)
vpnLogger := logger.New(log.SetComponent("vpn"))
vpnLooper := vpn.NewLoop(allSettings.VPN, ipv6Supported, allSettings.Firewall.VPNInputPorts,
providers, storage, allSettings.Health, healthChecker, healthcheckServer, ovpnConf, netLinker, firewallConf,
routingConf, portForwardLooper, cmder, publicIPLooper, dnsLooper, vpnLogger, httpClient,
buildInfo, *allSettings.Version.Enabled)
vpnLooper := vpn.NewLoop(allSettings.VPN, ipv6SupportLevel, allSettings.Firewall.VPNInputPorts,
providers, storage, boringPoll, allSettings.Health, healthChecker, healthcheckServer,
ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper, cmder, publicIPLooper,
dnsLooper, vpnLogger, httpClient, buildInfo, *allSettings.Version.Enabled)
vpnHandler, vpnCtx, vpnDone := goshutdown.NewGoRoutineHandler(
"vpn", goroutine.OptionTimeout(time.Second))
go vpnLooper.Run(vpnCtx, vpnDone)
@@ -483,7 +493,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
httpServer, err := server.New(httpServerCtx, allSettings.ControlServer,
logger.New(log.SetComponent("http server")),
buildInfo, vpnLooper, portForwardLooper, dnsLooper, updaterLooper, publicIPLooper,
storage, ipv6Supported)
storage, ipv6SupportLevel.IsSupported())
if err != nil {
return fmt.Errorf("setting up control server: %w", err)
}
@@ -509,7 +519,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
String() string
Stop() error
}{
portForwardLooper, publicIPLooper,
portForwardLooper, publicIPLooper, socks5Loop,
}
for _, stopper := range stoppers {
err := stopper.Stop()
@@ -521,6 +531,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
logger.Errorf("port forwarding loop crashed: %s", err)
case err := <-publicIPRunError:
logger.Errorf("public IP loop crashed: %s", err)
case err := <-socks5RunError:
logger.Errorf("SOCKS5 server loop crashed: %s", err)
}
return orderHandler.Shutdown(context.Background())
@@ -553,13 +565,23 @@ func printVersions(ctx context.Context, logger infoer,
return nil
}
func localNetworksToPrefixes(localNetworks []routing.LocalNetwork) (prefixes []netip.Prefix) {
prefixes = make([]netip.Prefix, len(localNetworks))
for i, localNetwork := range localNetworks {
prefixes[i] = localNetwork.IPNet
}
return prefixes
}
type netLinker interface {
Addresser
Router
Ruler
Linker
IsWireguardSupported() (ok bool, err error)
IsIPv6Supported() (ok bool, err error)
FindIPv6SupportLevel(ctx context.Context,
checkAddresses []netip.AddrPort, firewall netlink.Firewall,
) (level netlink.IPv6SupportLevel, err error)
FlushConntrack() error
PatchLoggerLevel(level log.Level)
}
@@ -603,15 +625,12 @@ type clier interface {
GenKey(args []string) error
}
type Tun interface {
Check(tunDevice string) error
Create(tunDevice string) error
}
type RunStarter interface {
Run(cmd *exec.Cmd) (output string, err error)
Start(cmd *exec.Cmd) (stdoutLines, stderrLines <-chan string,
waitError <-chan error, err error)
RunAndLog(ctx context.Context, commandString string,
logger command.Logger) (err error)
}
const gluetunLogo = ` @@@