diff --git a/Dockerfile b/Dockerfile index 20bdfd9b..ad290b0d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -167,11 +167,12 @@ ENV VPN_SERVICE_PROVIDER=pia \ HEALTH_ICMP_TARGET_IP=1.1.1.1 \ HEALTH_RESTART_VPN=on \ # DNS - DOT=on \ - DOT_PROVIDERS=cloudflare \ - DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:7f00:1/104,::ffff:a00:0/104,::ffff:a9fe:0/112,::ffff:ac10:0/108,::ffff:c0a8:0/112 \ - DOT_CACHING=on \ - DOT_IPV6=off \ + DNS_SERVER=on \ + DNS_UPSTREAM_RESOLVER_TYPE=DoT \ + DNS_UPSTREAM_RESOLVERS=cloudflare \ + DNS_PRIVATE_ADDRESSES=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:7f00:1/104,::ffff:a00:0/104,::ffff:a9fe:0/112,::ffff:ac10:0/108,::ffff:c0a8:0/112 \ + DNS_CACHING=on \ + DNS_UPSTREAM_IPV6=off \ BLOCK_MALICIOUS=on \ BLOCK_SURVEILLANCE=off \ BLOCK_ADS=off \ diff --git a/internal/configuration/settings/dns.go b/internal/configuration/settings/dns.go index 9d6e0ca9..acd57ce5 100644 --- a/internal/configuration/settings/dns.go +++ b/internal/configuration/settings/dns.go @@ -7,6 +7,7 @@ import ( "time" "github.com/qdm12/dns/v2/pkg/provider" + "github.com/qdm12/gluetun/internal/configuration/settings/helpers" "github.com/qdm12/gosettings" "github.com/qdm12/gosettings/reader" "github.com/qdm12/gotree" @@ -14,21 +15,23 @@ import ( // DNS contains settings to configure DNS. type DNS struct { - // DoTEnabled is true if the DoT server should be running + // ServerEnabled is true if the server should be running // and used. It defaults to true, and cannot be nil // in the internal state. - DoTEnabled *bool + ServerEnabled *bool + // UpstreamType can be dot or plain, and defaults to dot. + UpstreamType string `json:"upstream_type"` // UpdatePeriod is the period to update DNS block lists. // It can be set to 0 to disable the update. // It defaults to 24h and cannot be nil in // the internal state. UpdatePeriod *time.Duration - // Providers is a list of DNS over TLS providers + // Providers is a list of DNS providers Providers []string `json:"providers"` - // Caching is true if the DoT server should cache + // Caching is true if the server should cache // DNS responses. Caching *bool `json:"caching"` - // IPv6 is true if the DoT server should connect over IPv6. + // IPv6 is true if the server should connect over IPv6. IPv6 *bool `json:"ipv6"` // Blacklist contains settings to configure the filter // block lists. @@ -36,7 +39,7 @@ type DNS struct { // ServerAddress is the DNS server to use inside // the Go program and for the system. // It defaults to '127.0.0.1' to be used with the - // DoT server. It cannot be the zero value in the internal + // local server. It cannot be the zero value in the internal // state. ServerAddress netip.Addr // KeepNameserver is true if the existing DNS server @@ -45,20 +48,27 @@ type DNS struct { // outside the VPN tunnel since it would go through // the local DNS server of your Docker/Kubernetes // configuration, which is likely not going through the tunnel. - // This will also disable the DNS over TLS server and the + // This will also disable the DNS forwarder server and the // `ServerAddress` field will be ignored. // It defaults to false and cannot be nil in the // internal state. KeepNameserver *bool } -var ErrDoTUpdatePeriodTooShort = errors.New("update period is too short") +var ( + ErrDNSUpstreamTypeNotValid = errors.New("DNS upstream type is not valid") + ErrDNSUpdatePeriodTooShort = errors.New("update period is too short") +) func (d DNS) validate() (err error) { + if !helpers.IsOneOf(d.UpstreamType, "dot", "plain") { + return fmt.Errorf("%w: %s", ErrDNSUpstreamTypeNotValid, d.UpstreamType) + } + const minUpdatePeriod = 30 * time.Second if *d.UpdatePeriod != 0 && *d.UpdatePeriod < minUpdatePeriod { return fmt.Errorf("%w: %s must be bigger than %s", - ErrDoTUpdatePeriodTooShort, *d.UpdatePeriod, minUpdatePeriod) + ErrDNSUpdatePeriodTooShort, *d.UpdatePeriod, minUpdatePeriod) } providers := provider.NewProviders() @@ -79,7 +89,8 @@ func (d DNS) validate() (err error) { func (d *DNS) Copy() (copied DNS) { return DNS{ - DoTEnabled: gosettings.CopyPointer(d.DoTEnabled), + ServerEnabled: gosettings.CopyPointer(d.ServerEnabled), + UpstreamType: d.UpstreamType, UpdatePeriod: gosettings.CopyPointer(d.UpdatePeriod), Providers: gosettings.CopySlice(d.Providers), Caching: gosettings.CopyPointer(d.Caching), @@ -94,7 +105,8 @@ func (d *DNS) Copy() (copied DNS) { // settings object with any field set in the other // settings. func (d *DNS) overrideWith(other DNS) { - d.DoTEnabled = gosettings.OverrideWithPointer(d.DoTEnabled, other.DoTEnabled) + d.ServerEnabled = gosettings.OverrideWithPointer(d.ServerEnabled, other.ServerEnabled) + d.UpstreamType = gosettings.OverrideWithComparable(d.UpstreamType, other.UpstreamType) d.UpdatePeriod = gosettings.OverrideWithPointer(d.UpdatePeriod, other.UpdatePeriod) d.Providers = gosettings.OverrideWithSlice(d.Providers, other.Providers) d.Caching = gosettings.OverrideWithPointer(d.Caching, other.Caching) @@ -105,7 +117,8 @@ func (d *DNS) overrideWith(other DNS) { } func (d *DNS) setDefaults() { - d.DoTEnabled = gosettings.DefaultPointer(d.DoTEnabled, true) + d.ServerEnabled = gosettings.DefaultPointer(d.ServerEnabled, true) + d.UpstreamType = gosettings.DefaultComparable(d.UpstreamType, "dot") const defaultUpdatePeriod = 24 * time.Hour d.UpdatePeriod = gosettings.DefaultPointer(d.UpdatePeriod, defaultUpdatePeriod) d.Providers = gosettings.DefaultSlice(d.Providers, []string{ @@ -148,16 +161,12 @@ func (d DNS) toLinesNode() (node *gotree.Node) { } node.Appendf("DNS server address to use: %s", d.ServerAddress) - node.Appendf("DNS over TLS forwarder enabled: %s", gosettings.BoolToYesNo(d.DoTEnabled)) - if !*d.DoTEnabled { + node.Appendf("DNS forwarder server enabled: %s", gosettings.BoolToYesNo(d.ServerEnabled)) + if !*d.ServerEnabled { return node } - update := "disabled" - if *d.UpdatePeriod > 0 { - update = "every " + d.UpdatePeriod.String() - } - node.Appendf("Update period: %s", update) + node.Appendf("Upstream resolver type: %s", d.UpstreamType) upstreamResolvers := node.Append("Upstream resolvers:") for _, provider := range d.Providers { @@ -167,30 +176,38 @@ func (d DNS) toLinesNode() (node *gotree.Node) { node.Appendf("Caching: %s", gosettings.BoolToYesNo(d.Caching)) node.Appendf("IPv6: %s", gosettings.BoolToYesNo(d.IPv6)) + update := "disabled" + if *d.UpdatePeriod > 0 { + update = "every " + d.UpdatePeriod.String() + } + node.Appendf("Update period: %s", update) + node.AppendNode(d.Blacklist.toLinesNode()) return node } func (d *DNS) read(r *reader.Reader) (err error) { - d.DoTEnabled, err = r.BoolPtr("DOT") + d.ServerEnabled, err = r.BoolPtr("DNS_SERVER", reader.RetroKeys("DOT")) if err != nil { return err } + d.UpstreamType = r.String("DNS_UPSTREAM_RESOLVER_TYPE") + d.UpdatePeriod, err = r.DurationPtr("DNS_UPDATE_PERIOD") if err != nil { return err } - d.Providers = r.CSV("DOT_PROVIDERS") + d.Providers = r.CSV("DNS_UPSTREAM_RESOLVERS", reader.RetroKeys("DOT_PROVIDERS")) - d.Caching, err = r.BoolPtr("DOT_CACHING") + d.Caching, err = r.BoolPtr("DNS_CACHING", reader.RetroKeys("DOT_CACHING")) if err != nil { return err } - d.IPv6, err = r.BoolPtr("DOT_IPV6") + d.IPv6, err = r.BoolPtr("DNS_UPSTREAM_IPV6", reader.RetroKeys("DOT_IPV6")) if err != nil { return err } diff --git a/internal/configuration/settings/dnsblacklist.go b/internal/configuration/settings/dnsblacklist.go index 7853a8f2..f1828449 100644 --- a/internal/configuration/settings/dnsblacklist.go +++ b/internal/configuration/settings/dnsblacklist.go @@ -150,7 +150,7 @@ func (b *DNSBlacklist) read(r *reader.Reader) (err error) { } b.AddBlockedIPs, b.AddBlockedIPPrefixes, - err = readDoTPrivateAddresses(r) // TODO v4 split in 2 + err = readDNSPrivateAddresses(r) // TODO v4 split in 2 if err != nil { return err } @@ -162,7 +162,7 @@ func (b *DNSBlacklist) read(r *reader.Reader) (err error) { var ErrPrivateAddressNotValid = errors.New("private address is not a valid IP or CIDR range") -func readDoTPrivateAddresses(reader *reader.Reader) (ips []netip.Addr, +func readDNSPrivateAddresses(reader *reader.Reader) (ips []netip.Addr, ipPrefixes []netip.Prefix, err error, ) { privateAddresses := reader.CSV("DOT_PRIVATE_ADDRESS") diff --git a/internal/configuration/settings/settings.go b/internal/configuration/settings/settings.go index eb34d7d1..091c4a4d 100644 --- a/internal/configuration/settings/settings.go +++ b/internal/configuration/settings/settings.go @@ -177,10 +177,10 @@ func (s Settings) Warnings() (warnings []string) { // 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()+ - " so the DNS over TLS (DoT) server will not be used."+ - " The default value changed to 127.0.0.1 so it uses the internal DoT serves."+ - " If the DoT server fails to start, the IPv4 address of the first plaintext DNS server"+ - " corresponding to the first DoT provider chosen is used.") + " so the local forwarding DNS server will not be used."+ + " The default value changed to 127.0.0.1 so it uses the internal DNS server."+ + " If this server fails to start, the IPv4 address of the first plaintext DNS server"+ + " corresponding to the first DNS provider chosen is used.") } return warnings diff --git a/internal/configuration/settings/settings_test.go b/internal/configuration/settings/settings_test.go index f97de8b2..907655c5 100644 --- a/internal/configuration/settings/settings_test.go +++ b/internal/configuration/settings/settings_test.go @@ -40,12 +40,13 @@ func Test_Settings_String(t *testing.T) { ├── DNS settings: | ├── Keep existing nameserver(s): no | ├── DNS server address to use: 127.0.0.1 -| ├── DNS over TLS forwarder enabled: yes -| ├── Update period: every 24h0m0s +| ├── DNS forwarder server enabled: yes +| ├── Upstream resolver type: dot | ├── Upstream resolvers: | | └── Cloudflare | ├── Caching: yes | ├── IPv6: no +| ├── Update period: every 24h0m0s | └── DNS filtering settings: | ├── Block malicious: yes | ├── Block ads: no diff --git a/internal/dns/run.go b/internal/dns/run.go index f56474a2..652c205f 100644 --- a/internal/dns/run.go +++ b/internal/dns/run.go @@ -26,12 +26,12 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { } for ctx.Err() == nil { - // Upper scope variables for the DNS over TLS server only + // Upper scope variables for the DNS forwarder server only // Their values are to be used if DOT=off var runError <-chan error settings := l.GetSettings() - for !*settings.KeepNameserver && *settings.DoTEnabled { + for !*settings.KeepNameserver && *settings.ServerEnabled { var err error runError, err = l.setupServer(ctx) if err == nil { @@ -56,7 +56,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { } settings = l.GetSettings() - if !*settings.KeepNameserver && !*settings.DoTEnabled { + if !*settings.KeepNameserver && !*settings.ServerEnabled { const fallback = false l.useUnencryptedDNS(fallback) } @@ -101,6 +101,6 @@ func (l *Loop) runWait(ctx context.Context, runError <-chan error) (exitLoop boo func (l *Loop) stopServer() { stopErr := l.server.Stop() if stopErr != nil { - l.logger.Error("stopping DoT server: " + stopErr.Error()) + l.logger.Error("stopping server: " + stopErr.Error()) } } diff --git a/internal/dns/settings.go b/internal/dns/settings.go index aaff6d67..60309804 100644 --- a/internal/dns/settings.go +++ b/internal/dns/settings.go @@ -9,6 +9,7 @@ import ( "github.com/qdm12/dns/v2/pkg/middlewares/cache/lru" filtermiddleware "github.com/qdm12/dns/v2/pkg/middlewares/filter" "github.com/qdm12/dns/v2/pkg/middlewares/filter/mapfilter" + "github.com/qdm12/dns/v2/pkg/plain" "github.com/qdm12/dns/v2/pkg/provider" "github.com/qdm12/dns/v2/pkg/server" "github.com/qdm12/gluetun/internal/configuration/settings" @@ -22,31 +23,51 @@ func (l *Loop) SetSettings(ctx context.Context, settings settings.DNS) ( return l.state.SetSettings(ctx, settings) } -func buildDoTSettings(settings settings.DNS, +func buildServerSettings(settings settings.DNS, filter *mapfilter.Filter, logger Logger) ( serverSettings server.Settings, err error, ) { serverSettings.Logger = logger - var dotSettings dot.Settings providersData := provider.NewProviders() - dotSettings.UpstreamResolvers = make([]provider.Provider, len(settings.Providers)) + upstreamResolvers := make([]provider.Provider, len(settings.Providers)) for i := range settings.Providers { var err error - dotSettings.UpstreamResolvers[i], err = providersData.Get(settings.Providers[i]) + upstreamResolvers[i], err = providersData.Get(settings.Providers[i]) if err != nil { panic(err) // this should already had been checked } } - dotSettings.IPVersion = "ipv4" + + ipVersion := "ipv4" if *settings.IPv6 { - dotSettings.IPVersion = "ipv6" + ipVersion = "ipv6" } - serverSettings.Dialer, err = dot.New(dotSettings) - if err != nil { - return server.Settings{}, fmt.Errorf("creating DNS over TLS dialer: %w", err) + var dialer server.Dialer + switch settings.UpstreamType { + case "dot": + dialerSettings := dot.Settings{ + UpstreamResolvers: upstreamResolvers, + IPVersion: ipVersion, + } + dialer, err = dot.New(dialerSettings) + if err != nil { + return server.Settings{}, fmt.Errorf("creating DNS over TLS dialer: %w", err) + } + case "plain": + dialerSettings := plain.Settings{ + UpstreamResolvers: upstreamResolvers, + IPVersion: ipVersion, + } + dialer, err = plain.New(dialerSettings) + if err != nil { + return server.Settings{}, fmt.Errorf("creating plain DNS dialer: %w", err) + } + default: + panic("unknown upstream type: " + settings.UpstreamType) } + serverSettings.Dialer = dialer if *settings.Caching { lruCache, err := lru.New(lru.Settings{}) diff --git a/internal/dns/setup.go b/internal/dns/setup.go index 4eb97d95..38c3be6e 100644 --- a/internal/dns/setup.go +++ b/internal/dns/setup.go @@ -20,14 +20,14 @@ func (l *Loop) setupServer(ctx context.Context) (runError <-chan error, err erro settings := l.GetSettings() - dotSettings, err := buildDoTSettings(settings, l.filter, l.logger) + serverSettings, err := buildServerSettings(settings, l.filter, l.logger) if err != nil { - return nil, fmt.Errorf("building DoT settings: %w", err) + return nil, fmt.Errorf("building server settings: %w", err) } - server, err := server.New(dotSettings) + server, err := server.New(serverSettings) if err != nil { - return nil, fmt.Errorf("creating DoT server: %w", err) + return nil, fmt.Errorf("creating server: %w", err) } runError, err = server.Start(ctx) diff --git a/internal/dns/state/settings.go b/internal/dns/state/settings.go index a6cce5d4..302c0e7c 100644 --- a/internal/dns/state/settings.go +++ b/internal/dns/state/settings.go @@ -40,7 +40,7 @@ func (s *State) SetSettings(ctx context.Context, settings settings.DNS) ( // Restart _, _ = s.statusApplier.ApplyStatus(ctx, constants.Stopped) - if *settings.DoTEnabled { + if *settings.ServerEnabled { outcome, _ = s.statusApplier.ApplyStatus(ctx, constants.Running) } return outcome diff --git a/internal/vpn/tunnelup.go b/internal/vpn/tunnelup.go index 3e02295c..009b9f3d 100644 --- a/internal/vpn/tunnelup.go +++ b/internal/vpn/tunnelup.go @@ -46,7 +46,7 @@ func (l *Loop) onTunnelUp(ctx, loopCtx context.Context, data tunnelUpData) { return } - if *l.dnsLooper.GetSettings().DoTEnabled { + if *l.dnsLooper.GetSettings().ServerEnabled { _, _ = l.dnsLooper.ApplyStatus(ctx, constants.Running) } else { err := check.WaitForDNS(ctx, check.Settings{})