diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index 9ec9e82d..f82d40f9 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -16,6 +16,8 @@ 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" @@ -433,10 +435,18 @@ 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(), diff --git a/internal/cli/update.go b/internal/cli/update.go index eaad2d48..85a1a211 100644 --- a/internal/cli/update.go +++ b/internal/cli/update.go @@ -10,6 +10,8 @@ import ( "strings" "time" + "github.com/qdm12/dns/v2/pkg/doh" + dnsprovider "github.com/qdm12/dns/v2/pkg/provider" "github.com/qdm12/gluetun/internal/configuration/settings" "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants/providers" @@ -38,12 +40,12 @@ type UpdaterLogger interface { func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) error { options := settings.Updater{} var endUserMode, maintainerMode, updateAll bool - var csvProviders, ipToken, protonUsername, protonEmail, protonPassword string + var dnsServer, csvProviders, ipToken, protonUsername, protonEmail, protonPassword string flagSet := flag.NewFlagSet("update", flag.ExitOnError) flagSet.BoolVar(&endUserMode, "enduser", false, "Write results to /gluetun/servers.json (for end users)") flagSet.BoolVar(&maintainerMode, "maintainer", false, "Write results to ./internal/storage/servers.json to modify the program (for maintainers)") - flagSet.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use") + flagSet.StringVar(&dnsServer, "dns", "", "no longer used, your DNS will use DoH with Cloudflare and Google") const defaultMinRatio = 0.8 flagSet.Float64Var(&options.MinRatio, "minratio", defaultMinRatio, "Minimum ratio of servers to find for the update to succeed") @@ -58,6 +60,10 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e return err } + if dnsServer != "" { + logger.Warn("The -dns flag is no longer used, your DNS will use DoH with Cloudflare and Google") + } + if !endUserMode && !maintainerMode { return fmt.Errorf("%w", ErrModeUnspecified) } @@ -97,10 +103,21 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e return fmt.Errorf("creating servers storage: %w", err) } + dohSettings := doh.Settings{ + UpstreamResolvers: []dnsprovider.Provider{ + dnsprovider.Cloudflare(), + dnsprovider.Google(), + }, + } + dnsDialer, err := doh.New(dohSettings) + if err != nil { + return fmt.Errorf("creating DoH dialer: %w", err) + } + const clientTimeout = 10 * time.Second httpClient := &http.Client{Timeout: clientTimeout} unzipper := unzip.New(httpClient) - parallelResolver := resolver.NewParallelResolver(options.DNSAddress) + parallelResolver := resolver.NewParallelResolver(dnsDialer) nameTokenPairs := []api.NameToken{ {Name: string(api.IPInfo), Token: ipToken}, {Name: string(api.IP2Location)}, diff --git a/internal/configuration/settings/updater.go b/internal/configuration/settings/updater.go index 7cc50f06..8b0f55e3 100644 --- a/internal/configuration/settings/updater.go +++ b/internal/configuration/settings/updater.go @@ -21,10 +21,6 @@ type Updater struct { // updater. It cannot be nil in the internal state. // TODO change to value and add Enabled field. Period *time.Duration - // DNSAddress is the DNS server address to use - // to resolve VPN server hostnames to IP addresses. - // It cannot be the empty string in the internal state. - DNSAddress string // MinRatio is the minimum ratio of servers to // find per provider, compared to the total current // number of servers. It defaults to 0.8. @@ -76,7 +72,6 @@ func (u Updater) Validate() (err error) { func (u *Updater) copy() (copied Updater) { return Updater{ Period: gosettings.CopyPointer(u.Period), - DNSAddress: u.DNSAddress, MinRatio: u.MinRatio, Providers: gosettings.CopySlice(u.Providers), ProtonEmail: gosettings.CopyPointer(u.ProtonEmail), @@ -89,7 +84,6 @@ func (u *Updater) copy() (copied Updater) { // settings. func (u *Updater) overrideWith(other Updater) { u.Period = gosettings.OverrideWithPointer(u.Period, other.Period) - u.DNSAddress = gosettings.OverrideWithComparable(u.DNSAddress, other.DNSAddress) u.MinRatio = gosettings.OverrideWithComparable(u.MinRatio, other.MinRatio) u.Providers = gosettings.OverrideWithSlice(u.Providers, other.Providers) u.ProtonEmail = gosettings.OverrideWithPointer(u.ProtonEmail, other.ProtonEmail) @@ -98,7 +92,6 @@ func (u *Updater) overrideWith(other Updater) { func (u *Updater) SetDefaults(vpnProvider string) { u.Period = gosettings.DefaultPointer(u.Period, 0) - u.DNSAddress = gosettings.DefaultComparable(u.DNSAddress, "1.1.1.1:53") if u.MinRatio == 0 { const defaultMinRatio = 0.8 @@ -125,7 +118,6 @@ func (u Updater) toLinesNode() (node *gotree.Node) { node = gotree.New("Server data updater settings:") node.Appendf("Update period: %s", *u.Period) - node.Appendf("DNS address: %s", u.DNSAddress) node.Appendf("Minimum ratio: %.1f", u.MinRatio) node.Appendf("Providers to update: %s", strings.Join(u.Providers, ", ")) if slices.Contains(u.Providers, providers.Protonvpn) { @@ -142,11 +134,6 @@ func (u *Updater) read(r *reader.Reader) (err error) { return err } - u.DNSAddress, err = readUpdaterDNSAddress() - if err != nil { - return err - } - u.MinRatio, err = r.Float64("UPDATER_MIN_RATIO") if err != nil { return err @@ -166,12 +153,3 @@ func (u *Updater) read(r *reader.Reader) (err error) { return nil } - -func readUpdaterDNSAddress() (address string, err error) { - // TODO this is currently using Cloudflare in - // plaintext to not be blocked by DNS over TLS by default. - // If a plaintext address is set in the DNS settings, this one will be used. - // use custom future encrypted DNS written in Go without blocking - // as it's too much trouble to start another parallel unbound instance for now. - return "", nil -} diff --git a/internal/updater/resolver/interfaces.go b/internal/updater/resolver/interfaces.go new file mode 100644 index 00000000..df51faea --- /dev/null +++ b/internal/updater/resolver/interfaces.go @@ -0,0 +1,10 @@ +package resolver + +import ( + "context" + "net" +) + +type Dialer interface { + Dial(ctx context.Context, network, address string) (net.Conn, error) +} diff --git a/internal/updater/resolver/net.go b/internal/updater/resolver/net.go index 893a1874..9f24cd0a 100644 --- a/internal/updater/resolver/net.go +++ b/internal/updater/resolver/net.go @@ -1,17 +1,12 @@ package resolver import ( - "context" "net" ) -func newResolver(resolverAddress string) *net.Resolver { - d := net.Dialer{} - resolverAddress = net.JoinHostPort(resolverAddress, "53") +func newResolver(d Dialer) *net.Resolver { return &net.Resolver{ PreferGo: true, - Dial: func(ctx context.Context, _, _ string) (net.Conn, error) { - return d.DialContext(ctx, "udp", resolverAddress) - }, + Dial: d.Dial, } } diff --git a/internal/updater/resolver/parallel.go b/internal/updater/resolver/parallel.go index 3d22e1a1..fed660e4 100644 --- a/internal/updater/resolver/parallel.go +++ b/internal/updater/resolver/parallel.go @@ -11,9 +11,9 @@ type Parallel struct { repeatResolver *Repeat } -func NewParallelResolver(resolverAddress string) *Parallel { +func NewParallelResolver(dialer Dialer) *Parallel { return &Parallel{ - repeatResolver: NewRepeat(resolverAddress), + repeatResolver: NewRepeat(dialer), } } diff --git a/internal/updater/resolver/repeat.go b/internal/updater/resolver/repeat.go index 8252ab9b..038d2105 100644 --- a/internal/updater/resolver/repeat.go +++ b/internal/updater/resolver/repeat.go @@ -14,9 +14,9 @@ type Repeat struct { resolver *net.Resolver } -func NewRepeat(resolverAddress string) *Repeat { +func NewRepeat(dialer Dialer) *Repeat { return &Repeat{ - resolver: newResolver(resolverAddress), + resolver: newResolver(dialer), } }