Skip to content

Commit 7d69b5f

Browse files
committed
feat: add MIDDLEWARE_LOCALDNS_RESOLVERS
1 parent 131e42c commit 7d69b5f

File tree

5 files changed

+120
-29
lines changed

5 files changed

+120
-29
lines changed

Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ ENV \
8181
MIDDLEWARE_LOG_DIRECTORY=/var/log/dns/ \
8282
MIDDLEWARE_LOG_REQUESTS=on \
8383
MIDDLEWARE_LOG_RESPONSES=off \
84+
MIDDLEWARE_LOCALDNS_RESOLVERS= \
8485
CACHE_TYPE=lru \
8586
CACHE_LRU_MAX_ENTRIES=10000 \
8687
BLOCK_MALICIOUS=on \

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# DNS over TLS upstream server Docker container
22

3+
remove picker from settings
4+
specify list of names that should be treated as local (localdns and mapfilter middlewares)
5+
36
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
47

58
**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.*
@@ -141,6 +144,7 @@ If you're running Kubernetes, there is a separate article on [how to set up K8s]
141144
| `METRICS_TYPE` | `noop` | `noop` or `prometheus` |
142145
| `METRICS_PROMETHEUS_ADDRESS` | `:9090` | HTTP Prometheus server listening address |
143146
| `METRICS_PROMETHEUS_SUBSYSTEM` | `dns` | Prometheus metrics prefix/subsystem |
147+
| `MIDDLEWARE_LOCALDNS_RESOLVERS` | | Comma separated list of local DNS resolvers to use for local names DNS requests |
144148
| `CHECK_DNS` | `on` | `on` or `off`. Check resolving github.com using `127.0.0.1:53` at start |
145149
| `UPDATE_PERIOD` | `24h` | Period to update block lists and restart Unbound. Set to `0` to disable. |
146150

internal/config/localdns.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/netip"
7+
8+
"github.com/qdm12/gosettings/reader"
9+
"github.com/qdm12/gotree"
10+
)
11+
12+
type LocalDNS struct {
13+
Resolvers []netip.AddrPort
14+
}
15+
16+
var (
17+
ErrLocalResolverAddressNotValid = errors.New("local resolver address is not valid")
18+
ErrLocalResolverPortIsZero = errors.New("local resolver port is zero")
19+
)
20+
21+
func (l *LocalDNS) validate() (err error) {
22+
for _, resolver := range l.Resolvers {
23+
switch {
24+
case !resolver.IsValid():
25+
return fmt.Errorf("%w: %s",
26+
ErrLocalResolverAddressNotValid, resolver)
27+
case resolver.Port() == 0:
28+
return fmt.Errorf("%w: %s",
29+
ErrLocalResolverPortIsZero, resolver)
30+
}
31+
}
32+
33+
return nil
34+
}
35+
36+
func (l *LocalDNS) String() string {
37+
return l.ToLinesNode().String()
38+
}
39+
40+
func (l *LocalDNS) ToLinesNode() (node *gotree.Node) {
41+
node = gotree.New("Local DNS middleware:")
42+
resolversNode := gotree.New("Local resolvers:")
43+
for _, resolver := range l.Resolvers {
44+
resolversNode.Appendf("%s", resolver)
45+
}
46+
node.AppendNode(resolversNode)
47+
return node
48+
}
49+
50+
func (l *LocalDNS) read(reader *reader.Reader) (err error) {
51+
l.Resolvers, err = reader.CSVNetipAddrPorts("MIDDLEWARE_LOCALDNS_RESOLVERS")
52+
if err != nil {
53+
return err
54+
}
55+
return nil
56+
}

internal/config/settings.go

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Settings struct {
3131
Log Log
3232
MiddlewareLog MiddlewareLog
3333
Metrics Metrics
34+
LocalDNS LocalDNS
3435
CheckDNS *bool
3536
UpdatePeriod *time.Duration
3637
}
@@ -74,6 +75,7 @@ func (s *Settings) Validate() (err error) {
7475
"log": s.Log.validate,
7576
"middleware log": s.MiddlewareLog.validate,
7677
"metrics": s.Metrics.validate,
78+
"local DNS": s.LocalDNS.validate,
7779
}
7880
for name, validate := range nameToValidate {
7981
err = validate()
@@ -115,6 +117,7 @@ func (s *Settings) ToLinesNode() (node *gotree.Node) {
115117
node.AppendNode(s.Log.ToLinesNode())
116118
node.AppendNode(s.MiddlewareLog.ToLinesNode())
117119
node.AppendNode(s.Metrics.ToLinesNode())
120+
node.AppendNode(s.LocalDNS.ToLinesNode())
118121
node.Appendf("Check DNS: %s", gosettings.BoolToYesNo(s.CheckDNS))
119122

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

165168
s.Metrics.read(reader)
166169

170+
err = s.LocalDNS.read(reader)
171+
if err != nil {
172+
return fmt.Errorf("local DNS settings: %w", err)
173+
}
174+
167175
s.CheckDNS, err = reader.BoolPtr("CHECK_DNS")
168176
if err != nil {
169177
return err

internal/setup/dns.go

+51-29
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/qdm12/dns/v2/pkg/metrics/prometheus"
88
cachemiddleware "github.com/qdm12/dns/v2/pkg/middlewares/cache"
99
filtermiddleware "github.com/qdm12/dns/v2/pkg/middlewares/filter"
10+
"github.com/qdm12/dns/v2/pkg/middlewares/localdns"
1011
)
1112

1213
type Service interface {
@@ -18,14 +19,61 @@ type Service interface {
1819
func DNS(userSettings config.Settings, ipv6Support bool, //nolint:ireturn
1920
cache Cache, filter Filter, logger Logger, promRegistry PrometheusRegistry) (
2021
server Service, err error) {
21-
var middlewares []Middleware
22+
commonPrometheus := prometheus.Settings{
23+
Prefix: *userSettings.Metrics.Prometheus.Subsystem,
24+
Registry: promRegistry,
25+
}
26+
27+
middlewares, err := setupMiddlewares(userSettings, cache,
28+
filter, logger, commonPrometheus)
29+
if err != nil {
30+
return nil, fmt.Errorf("setting up middlewares: %w", err)
31+
}
32+
33+
switch userSettings.Upstream {
34+
case "dot":
35+
dotMetrics, err := dotMetrics(userSettings.Metrics.Type, commonPrometheus)
36+
if err != nil {
37+
return nil, fmt.Errorf("DoT metrics: %w", err)
38+
}
39+
40+
return dotServer(userSettings, ipv6Support, middlewares,
41+
logger, dotMetrics)
42+
case "doh":
43+
dohMetrics, err := dohMetrics(userSettings.Metrics.Type, commonPrometheus)
44+
if err != nil {
45+
return nil, fmt.Errorf("DoH metrics: %w", err)
46+
}
47+
48+
return dohServer(userSettings, ipv6Support, middlewares,
49+
logger, dohMetrics)
50+
default:
51+
panic(fmt.Sprintf("unknown upstream: %s", userSettings.Upstream))
52+
}
53+
}
2254

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

64+
if len(userSettings.LocalDNS.Resolvers) > 0 {
65+
localDNSMiddleware, err := localdns.New(localdns.Settings{
66+
Resolvers: userSettings.LocalDNS.Resolvers,
67+
Logger: logger,
68+
})
69+
if err != nil {
70+
return nil, fmt.Errorf("creating local DNS middleware: %w", err)
71+
}
72+
// Place after cache middleware, since we want to avoid caching for local
73+
// hostnames that may change regularly.
74+
middlewares = append(middlewares, localDNSMiddleware)
75+
}
76+
2977
filterMiddleware, err := filtermiddleware.New(filtermiddleware.Settings{Filter: filter})
3078
if err != nil {
3179
return nil, fmt.Errorf("creating filter middleware: %w", err)
@@ -34,14 +82,7 @@ func DNS(userSettings config.Settings, ipv6Support bool, //nolint:ireturn
3482
// to catch filtered responses found from the cache.
3583
middlewares = append(middlewares, filterMiddleware)
3684

37-
commonPrometheus := prometheus.Settings{
38-
Prefix: *userSettings.Metrics.Prometheus.Subsystem,
39-
Registry: promRegistry,
40-
}
41-
42-
metricsType := userSettings.Metrics.Type
43-
44-
metricsMiddleware, err := middlewareMetrics(metricsType,
85+
metricsMiddleware, err := middlewareMetrics(userSettings.Metrics.Type,
4586
commonPrometheus)
4687
if err != nil {
4788
return nil, fmt.Errorf("middleware metrics: %w", err)
@@ -58,24 +99,5 @@ func DNS(userSettings config.Settings, ipv6Support bool, //nolint:ireturn
5899
}
59100
middlewares = append(middlewares, logMiddleware)
60101

61-
switch userSettings.Upstream {
62-
case "dot":
63-
dotMetrics, err := dotMetrics(metricsType, commonPrometheus)
64-
if err != nil {
65-
return nil, fmt.Errorf("DoT metrics: %w", err)
66-
}
67-
68-
return dotServer(userSettings, ipv6Support, middlewares,
69-
logger, dotMetrics)
70-
case "doh":
71-
dohMetrics, err := dohMetrics(metricsType, commonPrometheus)
72-
if err != nil {
73-
return nil, fmt.Errorf("DoH metrics: %w", err)
74-
}
75-
76-
return dohServer(userSettings, ipv6Support, middlewares,
77-
logger, dohMetrics)
78-
default:
79-
panic(fmt.Sprintf("unknown upstream: %s", userSettings.Upstream))
80-
}
102+
return middlewares, nil
81103
}

0 commit comments

Comments
 (0)