Skip to content

Commit

Permalink
feat: add MIDDLEWARE_LOCALDNS_RESOLVERS
Browse files Browse the repository at this point in the history
  • Loading branch information
qdm12 committed Nov 26, 2023
1 parent 131e42c commit 7d69b5f
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 29 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_RESOLVERS= \
CACHE_TYPE=lru \
CACHE_LRU_MAX_ENTRIES=10000 \
BLOCK_MALICIOUS=on \
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# DNS over TLS upstream server Docker container

remove picker from settings
specify list of names that should be treated as local (localdns and mapfilter middlewares)

DNS over TLS upstream server connected to DNS over TLS (IPv4 and IPv6) servers with DNSSEC, DNS rebinding protection, built-in Docker healthcheck and fine grain IPs + hostnames blocking

**Announcement**: *The `:latest` image is now based on v2 which is a pure Go implementation for a DNS server connecting over DoT and DoH with a bunch of features.*
Expand Down Expand Up @@ -141,6 +144,7 @@ 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 |
| `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
56 changes: 56 additions & 0 deletions internal/config/localdns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package config

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

"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)

type LocalDNS struct {
Resolvers []netip.AddrPort
}

var (
ErrLocalResolverAddressNotValid = errors.New("local resolver address is not valid")
ErrLocalResolverPortIsZero = errors.New("local resolver port is zero")
)

func (l *LocalDNS) validate() (err error) {
for _, resolver := range l.Resolvers {
switch {
case !resolver.IsValid():
return fmt.Errorf("%w: %s",
ErrLocalResolverAddressNotValid, resolver)
case resolver.Port() == 0:
return fmt.Errorf("%w: %s",
ErrLocalResolverPortIsZero, resolver)
}
}

return nil
}

func (l *LocalDNS) String() string {
return l.ToLinesNode().String()
}

func (l *LocalDNS) ToLinesNode() (node *gotree.Node) {
node = gotree.New("Local DNS middleware:")
resolversNode := gotree.New("Local resolvers:")
for _, resolver := range l.Resolvers {
resolversNode.Appendf("%s", resolver)
}
node.AppendNode(resolversNode)
return node
}

func (l *LocalDNS) read(reader *reader.Reader) (err error) {
l.Resolvers, err = reader.CSVNetipAddrPorts("MIDDLEWARE_LOCALDNS_RESOLVERS")
if err != nil {
return err
}
return nil
}
8 changes: 8 additions & 0 deletions internal/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Settings struct {
Log Log
MiddlewareLog MiddlewareLog
Metrics Metrics
LocalDNS LocalDNS
CheckDNS *bool
UpdatePeriod *time.Duration
}
Expand Down Expand Up @@ -74,6 +75,7 @@ func (s *Settings) Validate() (err error) {
"log": s.Log.validate,
"middleware log": s.MiddlewareLog.validate,
"metrics": s.Metrics.validate,
"local DNS": s.LocalDNS.validate,
}
for name, validate := range nameToValidate {
err = validate()
Expand Down Expand Up @@ -115,6 +117,7 @@ func (s *Settings) ToLinesNode() (node *gotree.Node) {
node.AppendNode(s.Log.ToLinesNode())
node.AppendNode(s.MiddlewareLog.ToLinesNode())
node.AppendNode(s.Metrics.ToLinesNode())
node.AppendNode(s.LocalDNS.ToLinesNode())
node.Appendf("Check DNS: %s", gosettings.BoolToYesNo(s.CheckDNS))

if *s.UpdatePeriod == 0 {
Expand Down Expand Up @@ -164,6 +167,11 @@ func (s *Settings) Read(reader *reader.Reader, warner Warner) (err error) {

s.Metrics.read(reader)

err = s.LocalDNS.read(reader)
if err != nil {
return fmt.Errorf("local DNS settings: %w", err)
}

s.CheckDNS, err = reader.BoolPtr("CHECK_DNS")
if err != nil {
return err
Expand Down
80 changes: 51 additions & 29 deletions internal/setup/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/qdm12/dns/v2/pkg/metrics/prometheus"
cachemiddleware "github.com/qdm12/dns/v2/pkg/middlewares/cache"
filtermiddleware "github.com/qdm12/dns/v2/pkg/middlewares/filter"
"github.com/qdm12/dns/v2/pkg/middlewares/localdns"
)

type Service interface {
Expand All @@ -18,14 +19,61 @@ type Service interface {
func DNS(userSettings config.Settings, ipv6Support bool, //nolint:ireturn
cache Cache, filter Filter, logger Logger, promRegistry PrometheusRegistry) (
server Service, err error) {
var middlewares []Middleware
commonPrometheus := prometheus.Settings{
Prefix: *userSettings.Metrics.Prometheus.Subsystem,
Registry: promRegistry,
}

middlewares, err := setupMiddlewares(userSettings, cache,
filter, logger, commonPrometheus)
if err != nil {
return nil, fmt.Errorf("setting up middlewares: %w", err)
}

switch userSettings.Upstream {
case "dot":
dotMetrics, err := dotMetrics(userSettings.Metrics.Type, commonPrometheus)
if err != nil {
return nil, fmt.Errorf("DoT metrics: %w", err)
}

return dotServer(userSettings, ipv6Support, middlewares,
logger, dotMetrics)
case "doh":
dohMetrics, err := dohMetrics(userSettings.Metrics.Type, commonPrometheus)
if err != nil {
return nil, fmt.Errorf("DoH metrics: %w", err)
}

return dohServer(userSettings, ipv6Support, middlewares,
logger, dohMetrics)
default:
panic(fmt.Sprintf("unknown upstream: %s", userSettings.Upstream))
}
}

func setupMiddlewares(userSettings config.Settings, cache Cache,
filter Filter, logger Logger, commonPrometheus prometheus.Settings) (
middlewares []Middleware, err error) {
cacheMiddleware, err := cachemiddleware.New(cachemiddleware.Settings{Cache: cache})
if err != nil {
return nil, fmt.Errorf("creating cache middleware: %w", err)
}
middlewares = append(middlewares, cacheMiddleware)

if len(userSettings.LocalDNS.Resolvers) > 0 {
localDNSMiddleware, err := localdns.New(localdns.Settings{
Resolvers: userSettings.LocalDNS.Resolvers,
Logger: logger,
})
if err != nil {
return nil, fmt.Errorf("creating local DNS middleware: %w", err)
}
// Place after cache middleware, since we want to avoid caching for local
// hostnames that may change regularly.
middlewares = append(middlewares, localDNSMiddleware)
}

filterMiddleware, err := filtermiddleware.New(filtermiddleware.Settings{Filter: filter})
if err != nil {
return nil, fmt.Errorf("creating filter middleware: %w", err)
Expand All @@ -34,14 +82,7 @@ func DNS(userSettings config.Settings, ipv6Support bool, //nolint:ireturn
// to catch filtered responses found from the cache.
middlewares = append(middlewares, filterMiddleware)

commonPrometheus := prometheus.Settings{
Prefix: *userSettings.Metrics.Prometheus.Subsystem,
Registry: promRegistry,
}

metricsType := userSettings.Metrics.Type

metricsMiddleware, err := middlewareMetrics(metricsType,
metricsMiddleware, err := middlewareMetrics(userSettings.Metrics.Type,
commonPrometheus)
if err != nil {
return nil, fmt.Errorf("middleware metrics: %w", err)
Expand All @@ -58,24 +99,5 @@ func DNS(userSettings config.Settings, ipv6Support bool, //nolint:ireturn
}
middlewares = append(middlewares, logMiddleware)

switch userSettings.Upstream {
case "dot":
dotMetrics, err := dotMetrics(metricsType, commonPrometheus)
if err != nil {
return nil, fmt.Errorf("DoT metrics: %w", err)
}

return dotServer(userSettings, ipv6Support, middlewares,
logger, dotMetrics)
case "doh":
dohMetrics, err := dohMetrics(metricsType, commonPrometheus)
if err != nil {
return nil, fmt.Errorf("DoH metrics: %w", err)
}

return dohServer(userSettings, ipv6Support, middlewares,
logger, dohMetrics)
default:
panic(fmt.Sprintf("unknown upstream: %s", userSettings.Upstream))
}
return middlewares, nil
}

0 comments on commit 7d69b5f

Please sign in to comment.