Skip to content

Commit

Permalink
feat(localdns): auto-detect local DNS resolvers
Browse files Browse the repository at this point in the history
- Local DNS resolvers default to system DNS servers if not set
- Local DNS middleware enabled by default
- Add environment variable `MIDDLEWARE_LOCALDNS_ENABLED=on`
  • Loading branch information
qdm12 committed Nov 29, 2023
1 parent 340bb42 commit 09e170e
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 56 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ ENV \
MIDDLEWARE_LOG_DIRECTORY=/var/log/dns/ \
MIDDLEWARE_LOG_REQUESTS=on \
MIDDLEWARE_LOG_RESPONSES=off \
MIDDLEWARE_LOCALDNS_ENABLED=on \
MIDDLEWARE_LOCALDNS_RESOLVERS= \
CACHE_TYPE=lru \
CACHE_LRU_MAX_ENTRIES=10000 \
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ If you're running Kubernetes, there is a separate article on [how to set up K8s]
| `METRICS_TYPE` | `noop` | `noop` or `prometheus` |
| `METRICS_PROMETHEUS_ADDRESS` | `:9090` | HTTP Prometheus server listening address |
| `METRICS_PROMETHEUS_SUBSYSTEM` | `dns` | Prometheus metrics prefix/subsystem |
| `MIDDLEWARE_LOCALDNS_RESOLVERS` | | Comma separated list of local DNS resolvers to use for local names DNS requests |
| `MIDDLEWARE_LOCALDNS_ENABLED` | `on` | Enable or disable the local DNS middleware |
| `MIDDLEWARE_LOCALDNS_RESOLVERS` | Local DNS servers | Comma separated list of local DNS resolvers to use for local names DNS requests |
| `CHECK_DNS` | `on` | `on` or `off`. Check resolving github.com using `127.0.0.1:53` at start |
| `UPDATE_PERIOD` | `24h` | Period to update block lists and restart Unbound. Set to `0` to disable. |
Expand Down
16 changes: 15 additions & 1 deletion internal/config/localdns.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import (
"fmt"
"net/netip"

"github.com/qdm12/dns/v2/pkg/nameserver"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)

type LocalDNS struct {
Enabled *bool
Resolvers []netip.AddrPort
}

Expand All @@ -18,6 +21,12 @@ var (
ErrLocalResolverPortIsZero = errors.New("local resolver port is zero")
)

func (l *LocalDNS) setDefault() {
l.Enabled = gosettings.DefaultPointer(l.Enabled, true)
l.Resolvers = gosettings.DefaultSlice(l.Resolvers,
nameserver.GetDNSServers())
}

func (l *LocalDNS) validate() (err error) {
for _, resolver := range l.Resolvers {
switch {
Expand All @@ -38,7 +47,7 @@ func (l *LocalDNS) String() string {
}

func (l *LocalDNS) ToLinesNode() (node *gotree.Node) {
if len(l.Resolvers) == 0 {
if !*l.Enabled {
return gotree.New("Local DNS middleware: disabled")
}

Expand All @@ -52,6 +61,11 @@ func (l *LocalDNS) ToLinesNode() (node *gotree.Node) {
}

func (l *LocalDNS) read(reader *reader.Reader) (err error) {
l.Enabled, err = reader.BoolPtr("MIDDLEWARE_LOCALDNS_ENABLED")
if err != nil {
return err
}

l.Resolvers, err = reader.CSVNetipAddrPorts("MIDDLEWARE_LOCALDNS_RESOLVERS")
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions internal/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (s *Settings) SetDefaults() {
s.Log.setDefaults()
s.MiddlewareLog.setDefaults()
s.Metrics.setDefaults()
s.LocalDNS.setDefault()
s.CheckDNS = gosettings.DefaultPointer(s.CheckDNS, true)
const defaultUpdaterPeriod = 24 * time.Hour
s.UpdatePeriod = gosettings.DefaultPointer(s.UpdatePeriod, defaultUpdaterPeriod)
Expand Down
4 changes: 2 additions & 2 deletions internal/setup/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ func setupMiddlewares(userSettings config.Settings, cache Cache,
}
middlewares = append(middlewares, cacheMiddleware)

if len(userSettings.LocalDNS.Resolvers) > 0 {
if *userSettings.LocalDNS.Enabled {
localDNSMiddleware, err := localdns.New(localdns.Settings{
Resolvers: userSettings.LocalDNS.Resolvers,
Resolvers: userSettings.LocalDNS.Resolvers, // possibly auto-detected
Logger: logger,
})
if err != nil {
Expand Down
67 changes: 26 additions & 41 deletions pkg/middlewares/localdns/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,30 @@ import (
func Test_New(t *testing.T) {
t.Parallel()

t.Run("resolvers_not_set", func(t *testing.T) {
t.Parallel()

settings := Settings{}

middleware, err := New(settings)
assert.ErrorIs(t, err, ErrResolversNotSet)
require.EqualError(t, err, "validating settings: resolvers are not set")
assert.Nil(t, middleware)
})

t.Run("success", func(t *testing.T) {
t.Parallel()

settings := Settings{
Resolvers: []netip.AddrPort{
netip.AddrPortFrom(netip.MustParseAddr("1.2.3.4"), 53),
},
Logger: NewMockLogger(nil),
}

middleware, err := New(settings)
require.NoError(t, err)

expectedMiddleware := &Middleware{
settings: settings,
}
assert.Equal(t, expectedMiddleware, middleware)

next := dns.HandlerFunc(func(rw dns.ResponseWriter, m *dns.Msg) {})
handler := middleware.Wrap(next)

request := &dns.Msg{Question: []dns.Question{
{Name: "domain.com."},
}}
writer := NewMockResponseWriter(nil)
handler.ServeDNS(writer, request)

err = middleware.Stop()
require.NoError(t, err)
})
settings := Settings{
Resolvers: []netip.AddrPort{
netip.AddrPortFrom(netip.MustParseAddr("1.2.3.4"), 53),
},
Logger: NewMockLogger(nil),
}

middleware, err := New(settings)
require.NoError(t, err)

expectedMiddleware := &Middleware{
settings: settings,
}
assert.Equal(t, expectedMiddleware, middleware)

next := dns.HandlerFunc(func(rw dns.ResponseWriter, m *dns.Msg) {})
handler := middleware.Wrap(next)

request := &dns.Msg{Question: []dns.Question{
{Name: "domain.com."},
}}
writer := NewMockResponseWriter(nil)
handler.ServeDNS(writer, request)

err = middleware.Stop()
require.NoError(t, err)
}
14 changes: 3 additions & 11 deletions pkg/middlewares/localdns/settings.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package localdns

import (
"errors"
"fmt"
"net/netip"

"github.com/qdm12/dns/v2/pkg/log/noop"
"github.com/qdm12/dns/v2/pkg/nameserver"
"github.com/qdm12/gosettings"
"github.com/qdm12/gotree"
)
Expand All @@ -14,26 +13,19 @@ type Settings struct {
// Resolvers is the list of resolvers to use to resolve the
// local domain names. They are each tried after the other
// in order, until one returns an answer for the question.
// This field must be set and non empty.
// If left empty, the local nameservers found are used.
Resolvers []netip.AddrPort
// Logger is the logger to use.
// It defaults to a No-op implementation.
Logger Logger
}

func (s *Settings) SetDefaults() {
s.Resolvers = gosettings.DefaultSlice(s.Resolvers, nameserver.GetDNSServers())
s.Logger = gosettings.DefaultComparable[Logger](s.Logger, noop.New())
}

var (
ErrResolversNotSet = errors.New("resolvers are not set")
)

func (s *Settings) Validate() (err error) {
if len(s.Resolvers) == 0 {
return fmt.Errorf("%w", ErrResolversNotSet)
}

return nil
}

Expand Down

0 comments on commit 09e170e

Please sign in to comment.