From e47a8b7f609bea5f6cc14e96155c3d1b8f78d54d Mon Sep 17 00:00:00 2001 From: ThinkChaos Date: Fri, 2 Dec 2022 21:17:55 -0500 Subject: [PATCH] feat(bootstrap): support IP only encrypted DNS Also make `tcp+udp` upstreams use any IPs provided. --- docs/config.yml | 4 +--- docs/configuration.md | 6 ++--- resolver/bootstrap.go | 43 ++++++++++++++++++------------------ resolver/bootstrap_test.go | 45 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 67 insertions(+), 31 deletions(-) diff --git a/docs/config.yml b/docs/config.yml index 6e5a45b9f..738efd126 100644 --- a/docs/config.yml +++ b/docs/config.yml @@ -216,9 +216,7 @@ minTlsServeVersion: 1.3 # optional: use these DNS servers to resolve blacklist urls and upstream DNS servers. It is useful if no system DNS resolver is configured, and/or to encrypt the bootstrap queries. bootstrapDns: - tcp+udp:1.1.1.1 - - usptream: https://1.1.1.1/dns-query - ips: - - 1.1.1.1 + - https://1.1.1.1/dns-query - upstream: https://dns.digitale-gesellschaft.ch/dns-query ips: - 185.95.218.42 diff --git a/docs/configuration.md b/docs/configuration.md index b5312e0df..6bc89ab0c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -155,7 +155,7 @@ It is useful if no system DNS resolver is configured, and/or to encrypt the boot | upstream | Upstream (see above) | no | | | | ips | List of IPs | yes, if upstream is DoT/DoH | | Only valid if upstream is DoH or DoT | -If you only need to specify upstream, you can use the short form: `bootstrapDns: `. +When using an upstream specified by IP, and not by hostname, you can write only the upstream and skip `ips`. !!! note @@ -168,9 +168,7 @@ If you only need to specify upstream, you can use the short form: `bootstrapDns: - upstream: tcp-tls:dns.example.com ips: - 123.123.123.123 - - upstream: https://dns2.example.com/dns-query - ips: - - 234.234.234.234 + - upstream: https://234.234.234.234/dns-query ``` ## Filtering diff --git a/resolver/bootstrap.go b/resolver/bootstrap.go index a3cbed57d..9833df000 100644 --- a/resolver/bootstrap.go +++ b/resolver/bootstrap.go @@ -235,35 +235,34 @@ func newBootstrapedResolvers(b *Bootstrap, cfg config.BootstrapDNSConfig) (boots upstream := upstreamCfg.Upstream - var ips []net.IP - - switch { - case upstream.IsDefault(): + if upstream.IsDefault() { multiErr = multierror.Append( multiErr, fmt.Errorf("item %d: upstream not configured (ips=%v)", i, upstreamCfg.IPs), ) + continue - case upstream.Net == config.NetProtocolTcpUdp: - ip := net.ParseIP(upstream.Host) - if ip == nil { - multiErr = multierror.Append( - multiErr, - fmt.Errorf("item %d: '%s': protocol %s must use IP instead of hostname", i, upstream, upstream.Net), - ) - continue - } + } + var ips []net.IP + + if ip := net.ParseIP(upstream.Host); ip != nil { ips = append(ips, ip) - default: - ips = upstreamCfg.IPs - if len(ips) == 0 { - multiErr = multierror.Append( - multiErr, - fmt.Errorf("item %d: '%s': protocol %s requires IPs to be set", i, upstream, upstream.Net), - ) - continue - } + } else if upstream.Net == config.NetProtocolTcpUdp { + multiErr = multierror.Append( + multiErr, + fmt.Errorf("item %d: '%s': protocol %s must use IP instead of hostname", i, upstream, upstream.Net), + ) + + continue + } + + ips = append(ips, upstreamCfg.IPs...) + + if len(ips) == 0 { + multiErr = multierror.Append(multiErr, fmt.Errorf("item %d: '%s': no IPs configured", i, upstream)) + + continue } resolver := newUpstreamResolverUnchecked(upstream, b) diff --git a/resolver/bootstrap_test.go b/resolver/bootstrap_test.go index dd7b37d5d..483826a42 100644 --- a/resolver/bootstrap_test.go +++ b/resolver/bootstrap_test.go @@ -128,7 +128,7 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() { }) }) - When("IP is invalid", func() { + When("using non IP hostname", func() { It("errors", func() { cfg := config.Config{ BootstrapDNS: []config.BootstrappedUpstreamConfig{ @@ -146,6 +146,29 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() { Expect(err.Error()).Should(ContainSubstring("must use IP instead of hostname")) }) }) + + When("extra IPs are configured", func() { + BeforeEach(func() { + sutConfig = &config.Config{ + BootstrapDNS: []config.BootstrappedUpstreamConfig{ + { + Upstream: config.Upstream{ + Net: config.NetProtocolTcpUdp, + Host: "0.0.0.0", + }, + IPs: []net.IP{net.IPv4allrouter}, + }, + }, + } + }) + It("uses them", func() { + Expect(sut).ShouldNot(BeNil()) + + for _, ips := range sut.bootstraped { + Expect(ips).Should(ContainElements(net.IPv4zero, net.IPv4allrouter)) + } + }) + }) }) Context("using encrypted DNS", func() { @@ -164,7 +187,25 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() { _, err := NewBootstrap(&cfg) Expect(err).ShouldNot(Succeed()) - Expect(err.Error()).Should(ContainSubstring("requires IPs to be set")) + Expect(err.Error()).Should(ContainSubstring("no IPs configured")) + }) + }) + + When("hostname is IP", func() { + It("doesn't require extra IPs", func() { + cfg := config.Config{ + BootstrapDNS: []config.BootstrappedUpstreamConfig{ + { + Upstream: config.Upstream{ + Net: config.NetProtocolTcpTls, + Host: "0.0.0.0", + }, + }, + }, + } + + _, err := NewBootstrap(&cfg) + Expect(err).Should(Succeed()) }) }) })