Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 153aba4

Browse files
committedJul 11, 2023
Minimal changes
1 parent fa541b8 commit 153aba4

23 files changed

+562
-518
lines changed
 

‎.github/labels.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
- name: "Wireguard"
105105
color: "ffc7ea"
106106
description: ""
107-
- name: "Unbound (DNS over TLS)"
107+
- name: "DNS"
108108
color: "ffc7ea"
109109
description: ""
110110
- name: "Firewall"

‎Dockerfile

+2-6
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,6 @@ ENV VPN_SERVICE_PROVIDER=pia \
151151
DOT=on \
152152
DOT_PROVIDERS=cloudflare \
153153
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 \
154-
DOT_VERBOSITY=1 \
155-
DOT_VERBOSITY_DETAILS=0 \
156-
DOT_VALIDATION_LOGLEVEL=0 \
157154
DOT_CACHING=on \
158155
DOT_IPV6=off \
159156
BLOCK_MALICIOUS=on \
@@ -207,12 +204,11 @@ RUN apk add --no-cache --update -l wget && \
207204
apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.17/main" openvpn\~2.5 && \
208205
mv /usr/sbin/openvpn /usr/sbin/openvpn2.5 && \
209206
apk del openvpn && \
210-
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \
207+
apk add --no-cache --update openvpn ca-certificates iptables ip6tables tzdata && \
211208
mv /usr/sbin/openvpn /usr/sbin/openvpn2.6 && \
212209
# Fix vulnerability issue
213210
apk add --no-cache --update busybox && \
214-
rm -rf /var/cache/apk/* /etc/unbound/* /usr/sbin/unbound-* /etc/openvpn/*.sh /usr/lib/openvpn/plugins/openvpn-plugin-down-root.so && \
211+
rm -rf /var/cache/apk/* /etc/openvpn/*.sh /usr/lib/openvpn/plugins/openvpn-plugin-down-root.so && \
215212
deluser openvpn && \
216-
deluser unbound && \
217213
mkdir /gluetun
218214
COPY --from=build /tmp/gobuild/entrypoint /gluetun-entrypoint

‎README.md

-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ Lightweight swiss-knife-like VPN client to multiple VPN service providers
7575
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
7676
- [Custom VPN server side port forwarding for Private Internet Access](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/private-internet-access.md#vpn-server-port-forwarding)
7777
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
78-
- Unbound subprogram drops root privileges once launched
7978
- Can work as a Kubernetes sidecar container, thanks @rorph
8079

8180
## Setup

‎cmd/gluetun/main.go

+2-16
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
_ "time/tzdata"
1414

1515
_ "github.com/breml/rootcerts"
16-
"github.com/qdm12/dns/pkg/unbound"
1716
"github.com/qdm12/gluetun/internal/alpine"
1817
"github.com/qdm12/gluetun/internal/cli"
1918
"github.com/qdm12/gluetun/internal/configuration/settings"
@@ -51,7 +50,6 @@ import (
5150
"github.com/qdm12/goshutdown/order"
5251
"github.com/qdm12/gosplash"
5352
"github.com/qdm12/log"
54-
"github.com/qdm12/updated/pkg/dnscrypto"
5553
)
5654

5755
//nolint:gochecknoglobals
@@ -257,16 +255,11 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
257255
ovpnConf := openvpn.New(
258256
logger.New(log.SetComponent("openvpn configurator")),
259257
cmder, puid, pgid)
260-
dnsCrypto := dnscrypto.New(httpClient, "", "")
261-
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
262-
dnsConf := unbound.NewConfigurator(nil, cmder, dnsCrypto,
263-
"/etc/unbound", "/usr/sbin/unbound", cacertsPath)
264258

265259
err = printVersions(ctx, logger, []printVersionElement{
266260
{name: "Alpine", getVersion: alpineConf.Version},
267261
{name: "OpenVPN 2.5", getVersion: ovpnConf.Version25},
268262
{name: "OpenVPN 2.6", getVersion: ovpnConf.Version26},
269-
{name: "Unbound", getVersion: dnsConf.Version},
270263
{name: "IPtables", getVersion: func(ctx context.Context) (version string, err error) {
271264
return firewall.Version(ctx, cmder)
272265
}},
@@ -296,15 +289,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
296289
if nonRootUsername != defaultUsername {
297290
logger.Info("using existing username " + nonRootUsername + " corresponding to user id " + fmt.Sprint(puid))
298291
}
299-
// set it for Unbound
300-
// TODO remove this when migrating to qdm12/dns v2
301-
allSettings.DNS.DoT.Unbound.Username = nonRootUsername
302292
allSettings.VPN.OpenVPN.ProcessUser = nonRootUsername
303293

304-
if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
305-
return err
306-
}
307-
308294
if err := routingConf.Setup(); err != nil {
309295
if strings.Contains(err.Error(), "operation not permitted") {
310296
logger.Warn("💡 Tip: Are you passing NET_ADMIN capability to gluetun?")
@@ -382,10 +368,10 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
382368
go portForwardLooper.Run(portForwardCtx, portForwardDone)
383369

384370
unboundLogger := logger.New(log.SetComponent("dns over tls"))
385-
unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient,
371+
unboundLooper := dns.NewLoop(allSettings.DNS, httpClient,
386372
unboundLogger)
387373
dnsHandler, dnsCtx, dnsDone := goshutdown.NewGoRoutineHandler(
388-
"unbound", goroutine.OptionTimeout(defaultShutdownTimeout))
374+
"dns", goroutine.OptionTimeout(defaultShutdownTimeout))
389375
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
390376
go unboundLooper.Run(dnsCtx, dnsDone)
391377
otherGroupHandler.Add(dnsHandler)

‎go.mod

+14-4
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@ require (
66
github.com/breml/rootcerts v0.2.11
77
github.com/fatih/color v1.15.0
88
github.com/golang/mock v1.6.0
9-
github.com/qdm12/dns v1.11.0
10-
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
9+
github.com/qdm12/dns/v2 v2.0.0-rc3
10+
github.com/qdm12/golibs v0.0.0-20210915134941-19815c6f95fe
1111
github.com/qdm12/gosettings v0.4.0-rc1
1212
github.com/qdm12/goshutdown v0.3.0
1313
github.com/qdm12/gosplash v0.1.0
1414
github.com/qdm12/gotree v0.2.0
1515
github.com/qdm12/govalid v0.2.0-rc1
1616
github.com/qdm12/log v0.1.0
1717
github.com/qdm12/ss-server v0.5.0-rc1
18-
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
1918
github.com/stretchr/testify v1.8.4
2019
github.com/vishvananda/netlink v1.2.1-beta.2
2120
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
@@ -28,24 +27,35 @@ require (
2827
)
2928

3029
require (
30+
github.com/beorn7/perks v1.0.1 // indirect
31+
github.com/cespare/xxhash/v2 v2.1.1 // indirect
3132
github.com/davecgh/go-spew v1.1.1 // indirect
33+
github.com/golang/protobuf v1.4.3 // indirect
3234
github.com/google/go-cmp v0.5.9 // indirect
3335
github.com/josharian/native v1.0.0 // indirect
3436
github.com/mattn/go-colorable v0.1.13 // indirect
3537
github.com/mattn/go-isatty v0.0.17 // indirect
38+
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
3639
github.com/mdlayher/genetlink v1.2.0 // indirect
3740
github.com/mdlayher/netlink v1.6.2 // indirect
3841
github.com/mdlayher/socket v0.2.3 // indirect
39-
github.com/miekg/dns v1.1.40 // indirect
42+
github.com/miekg/dns v1.1.54 // indirect
4043
github.com/mr-tron/base58 v1.2.0 // indirect
4144
github.com/pmezard/go-difflib v1.0.0 // indirect
45+
github.com/prometheus/client_golang v1.10.0 // indirect
46+
github.com/prometheus/client_model v0.2.0 // indirect
47+
github.com/prometheus/common v0.18.0 // indirect
48+
github.com/prometheus/procfs v0.6.0 // indirect
4249
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
4350
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
4451
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
4552
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296 // indirect
4653
golang.org/x/crypto v0.11.0 // indirect
4754
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
55+
golang.org/x/mod v0.8.0 // indirect
4856
golang.org/x/sync v0.1.0 // indirect
57+
golang.org/x/tools v0.6.0 // indirect
4958
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
59+
google.golang.org/protobuf v1.23.0 // indirect
5060
gopkg.in/yaml.v3 v3.0.1 // indirect
5161
)

‎go.sum

+373-68
Large diffs are not rendered by default.

‎internal/configuration/settings/dnsblacklist.go

+11-9
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package settings
33
import (
44
"errors"
55
"fmt"
6+
"net/http"
67
"net/netip"
78
"regexp"
89

9-
"github.com/qdm12/dns/pkg/blacklist"
10+
"github.com/qdm12/dns/v2/pkg/blockbuilder"
1011
"github.com/qdm12/gosettings"
1112
"github.com/qdm12/gotree"
1213
)
@@ -83,16 +84,17 @@ func (b *DNSBlacklist) overrideWith(other DNSBlacklist) {
8384
b.AddBlockedIPPrefixes = gosettings.OverrideWithSlice(b.AddBlockedIPPrefixes, other.AddBlockedIPPrefixes)
8485
}
8586

86-
func (b DNSBlacklist) ToBlacklistFormat() (settings blacklist.BuilderSettings, err error) {
87-
return blacklist.BuilderSettings{
88-
BlockMalicious: *b.BlockMalicious,
89-
BlockAds: *b.BlockAds,
90-
BlockSurveillance: *b.BlockSurveillance,
87+
func (b DNSBlacklist) ToBlockBuilderSettings(client *http.Client) (
88+
settings blockbuilder.Settings) {
89+
return blockbuilder.Settings{
90+
BlockMalicious: b.BlockMalicious,
91+
BlockAds: b.BlockAds,
92+
BlockSurveillance: b.BlockSurveillance,
9193
AllowedHosts: b.AllowedHosts,
9294
AddBlockedHosts: b.AddBlockedHosts,
93-
AddBlockedIPs: netipAddressesToNetaddrIPs(b.AddBlockedIPs),
94-
AddBlockedIPPrefixes: netipPrefixesToNetaddrIPPrefixes(b.AddBlockedIPPrefixes),
95-
}, nil
95+
AddBlockedIPs: b.AddBlockedIPs,
96+
AddBlockedIPPrefixes: b.AddBlockedIPPrefixes,
97+
}
9698
}
9799

98100
func (b DNSBlacklist) String() string {

‎internal/configuration/settings/settings_test.go

+2-9
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,11 @@ func Test_Settings_String(t *testing.T) {
4343
| └── DNS over TLS settings:
4444
| ├── Enabled: yes
4545
| ├── Update period: every 24h0m0s
46-
| ├── Unbound settings:
46+
| ├── DNS over TLS settings:
4747
| | ├── Authoritative servers:
4848
| | | └── Cloudflare
4949
| | ├── Caching: yes
50-
| | ├── IPv6: no
51-
| | ├── Verbosity level: 1
52-
| | ├── Verbosity details level: 0
53-
| | ├── Validation log level: 0
54-
| | ├── System user: root
55-
| | └── Allowed networks:
56-
| | ├── 0.0.0.0/0
57-
| | └── ::/0
50+
| | └── IPv6: no
5851
| └── DNS filtering settings:
5952
| ├── Block malicious: yes
6053
| ├── Block ads: no

‎internal/configuration/settings/unbound.go

+16-128
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,28 @@ package settings
22

33
import (
44
"errors"
5-
"fmt"
65
"net/netip"
76

8-
"github.com/qdm12/dns/pkg/provider"
9-
"github.com/qdm12/dns/pkg/unbound"
7+
"github.com/qdm12/dns/v2/pkg/provider"
108
"github.com/qdm12/gosettings"
119
"github.com/qdm12/gotree"
1210
)
1311

1412
// Unbound is settings for the Unbound program.
1513
type Unbound struct {
16-
Providers []string `json:"providers"`
17-
Caching *bool `json:"caching"`
18-
IPv6 *bool `json:"ipv6"`
19-
VerbosityLevel *uint8 `json:"verbosity_level"`
20-
VerbosityDetailsLevel *uint8 `json:"verbosity_details_level"`
21-
ValidationLogLevel *uint8 `json:"validation_log_level"`
22-
Username string `json:"username"`
23-
Allowed []netip.Prefix `json:"allowed"`
14+
Providers []string `json:"providers"`
15+
Caching *bool `json:"caching"`
16+
IPv6 *bool `json:"ipv6"`
2417
}
2518

2619
func (u *Unbound) setDefaults() {
27-
if len(u.Providers) == 0 {
28-
u.Providers = []string{
29-
provider.Cloudflare().String(),
30-
}
31-
}
32-
20+
u.Providers = gosettings.DefaultSlice(u.Providers, []string{
21+
provider.Cloudflare().Name,
22+
})
3323
u.Caching = gosettings.DefaultPointer(u.Caching, true)
3424
u.IPv6 = gosettings.DefaultPointer(u.IPv6, false)
35-
36-
const defaultVerbosityLevel = 1
37-
u.VerbosityLevel = gosettings.DefaultPointer(u.VerbosityLevel, defaultVerbosityLevel)
38-
39-
const defaultVerbosityDetailsLevel = 0
40-
u.VerbosityDetailsLevel = gosettings.DefaultPointer(u.VerbosityDetailsLevel, defaultVerbosityDetailsLevel)
41-
42-
const defaultValidationLogLevel = 0
43-
u.ValidationLogLevel = gosettings.DefaultPointer(u.ValidationLogLevel, defaultValidationLogLevel)
44-
45-
if u.Allowed == nil {
46-
u.Allowed = []netip.Prefix{
47-
netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 0),
48-
netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0),
49-
}
50-
}
51-
52-
u.Username = gosettings.DefaultString(u.Username, "root")
5325
}
5426

55-
var (
56-
ErrUnboundVerbosityLevelNotValid = errors.New("Unbound verbosity level is not valid")
57-
ErrUnboundVerbosityDetailsLevelNotValid = errors.New("Unbound verbosity details level is not valid")
58-
ErrUnboundValidationLogLevelNotValid = errors.New("Unbound validation log level is not valid")
59-
)
60-
6127
func (u Unbound) validate() (err error) {
6228
for _, s := range u.Providers {
6329
_, err := provider.Parse(s)
@@ -66,120 +32,51 @@ func (u Unbound) validate() (err error) {
6632
}
6733
}
6834

69-
const maxVerbosityLevel = 5
70-
if *u.VerbosityLevel > maxVerbosityLevel {
71-
return fmt.Errorf("%w: %d must be between 0 and %d",
72-
ErrUnboundVerbosityLevelNotValid,
73-
*u.VerbosityLevel,
74-
maxVerbosityLevel)
75-
}
76-
77-
const maxVerbosityDetailsLevel = 4
78-
if *u.VerbosityDetailsLevel > maxVerbosityDetailsLevel {
79-
return fmt.Errorf("%w: %d must be between 0 and %d",
80-
ErrUnboundVerbosityDetailsLevelNotValid,
81-
*u.VerbosityDetailsLevel,
82-
maxVerbosityDetailsLevel)
83-
}
84-
85-
const maxValidationLogLevel = 2
86-
if *u.ValidationLogLevel > maxValidationLogLevel {
87-
return fmt.Errorf("%w: %d must be between 0 and %d",
88-
ErrUnboundValidationLogLevelNotValid,
89-
*u.ValidationLogLevel, maxValidationLogLevel)
90-
}
91-
9235
return nil
9336
}
9437

9538
func (u Unbound) copy() (copied Unbound) {
9639
return Unbound{
97-
Providers: gosettings.CopySlice(u.Providers),
98-
Caching: gosettings.CopyPointer(u.Caching),
99-
IPv6: gosettings.CopyPointer(u.IPv6),
100-
VerbosityLevel: gosettings.CopyPointer(u.VerbosityLevel),
101-
VerbosityDetailsLevel: gosettings.CopyPointer(u.VerbosityDetailsLevel),
102-
ValidationLogLevel: gosettings.CopyPointer(u.ValidationLogLevel),
103-
Username: u.Username,
104-
Allowed: gosettings.CopySlice(u.Allowed),
40+
Providers: gosettings.CopySlice(u.Providers),
41+
Caching: gosettings.CopyPointer(u.Caching),
42+
IPv6: gosettings.CopyPointer(u.IPv6),
10543
}
10644
}
10745

10846
func (u *Unbound) mergeWith(other Unbound) {
10947
u.Providers = gosettings.MergeWithSlice(u.Providers, other.Providers)
11048
u.Caching = gosettings.MergeWithPointer(u.Caching, other.Caching)
11149
u.IPv6 = gosettings.MergeWithPointer(u.IPv6, other.IPv6)
112-
u.VerbosityLevel = gosettings.MergeWithPointer(u.VerbosityLevel, other.VerbosityLevel)
113-
u.VerbosityDetailsLevel = gosettings.MergeWithPointer(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
114-
u.ValidationLogLevel = gosettings.MergeWithPointer(u.ValidationLogLevel, other.ValidationLogLevel)
115-
u.Username = gosettings.MergeWithString(u.Username, other.Username)
116-
u.Allowed = gosettings.MergeWithSlice(u.Allowed, other.Allowed)
11750
}
11851

11952
func (u *Unbound) overrideWith(other Unbound) {
12053
u.Providers = gosettings.OverrideWithSlice(u.Providers, other.Providers)
12154
u.Caching = gosettings.OverrideWithPointer(u.Caching, other.Caching)
12255
u.IPv6 = gosettings.OverrideWithPointer(u.IPv6, other.IPv6)
123-
u.VerbosityLevel = gosettings.OverrideWithPointer(u.VerbosityLevel, other.VerbosityLevel)
124-
u.VerbosityDetailsLevel = gosettings.OverrideWithPointer(u.VerbosityDetailsLevel, other.VerbosityDetailsLevel)
125-
u.ValidationLogLevel = gosettings.OverrideWithPointer(u.ValidationLogLevel, other.ValidationLogLevel)
126-
u.Username = gosettings.OverrideWithString(u.Username, other.Username)
127-
u.Allowed = gosettings.OverrideWithSlice(u.Allowed, other.Allowed)
128-
}
129-
130-
func (u Unbound) ToUnboundFormat() (settings unbound.Settings, err error) {
131-
providers := make([]provider.Provider, len(u.Providers))
132-
for i := range providers {
133-
providers[i], err = provider.Parse(u.Providers[i])
134-
if err != nil {
135-
return settings, err
136-
}
137-
}
138-
139-
const port = 53
140-
141-
return unbound.Settings{
142-
ListeningPort: port,
143-
IPv4: true,
144-
Providers: providers,
145-
Caching: *u.Caching,
146-
IPv6: *u.IPv6,
147-
VerbosityLevel: *u.VerbosityLevel,
148-
VerbosityDetailsLevel: *u.VerbosityDetailsLevel,
149-
ValidationLogLevel: *u.ValidationLogLevel,
150-
AccessControl: unbound.AccessControlSettings{
151-
Allowed: netipPrefixesToNetaddrIPPrefixes(u.Allowed),
152-
},
153-
Username: u.Username,
154-
}, nil
15556
}
15657

15758
var (
15859
ErrConvertingNetip = errors.New("converting net.IP to netip.Addr failed")
15960
)
16061

161-
func (u Unbound) GetFirstPlaintextIPv4() (ipv4 netip.Addr, err error) {
62+
func (u Unbound) GetFirstPlaintextIPv4() (ipv4 netip.Addr) {
16263
s := u.Providers[0]
16364
provider, err := provider.Parse(s)
16465
if err != nil {
165-
return ipv4, err
66+
// Settings should be validated before calling this function,
67+
// so an error happening here is a programming error.
68+
panic(err)
16669
}
16770

168-
ip := provider.DNS().IPv4[0]
169-
ipv4, ok := netip.AddrFromSlice(ip)
170-
if !ok {
171-
return ipv4, fmt.Errorf("%w: for ip %s (%#v)",
172-
ErrConvertingNetip, ip, ip)
173-
}
174-
return ipv4.Unmap(), nil
71+
return provider.DNS.IPv4[0]
17572
}
17673

17774
func (u Unbound) String() string {
17875
return u.toLinesNode().String()
17976
}
18077

18178
func (u Unbound) toLinesNode() (node *gotree.Node) {
182-
node = gotree.New("Unbound settings:")
79+
node = gotree.New("DNS over TLS settings:")
18380

18481
authServers := node.Appendf("Authoritative servers:")
18582
for _, provider := range u.Providers {
@@ -188,15 +85,6 @@ func (u Unbound) toLinesNode() (node *gotree.Node) {
18885

18986
node.Appendf("Caching: %s", gosettings.BoolToYesNo(u.Caching))
19087
node.Appendf("IPv6: %s", gosettings.BoolToYesNo(u.IPv6))
191-
node.Appendf("Verbosity level: %d", *u.VerbosityLevel)
192-
node.Appendf("Verbosity details level: %d", *u.VerbosityDetailsLevel)
193-
node.Appendf("Validation log level: %d", *u.ValidationLogLevel)
194-
node.Appendf("System user: %s", u.Username)
195-
196-
allowedNetworks := node.Appendf("Allowed networks:")
197-
for _, network := range u.Allowed {
198-
allowedNetworks.Appendf(network.String())
199-
}
20088

20189
return node
20290
}

‎internal/configuration/settings/unbound_test.go

+4-15
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package settings
22

33
import (
44
"encoding/json"
5-
"net/netip"
65
"testing"
76

87
"github.com/stretchr/testify/assert"
@@ -13,25 +12,15 @@ func Test_Unbound_JSON(t *testing.T) {
1312
t.Parallel()
1413

1514
settings := Unbound{
16-
Providers: []string{"cloudflare"},
17-
Caching: boolPtr(true),
18-
IPv6: boolPtr(false),
19-
VerbosityLevel: uint8Ptr(1),
20-
VerbosityDetailsLevel: nil,
21-
ValidationLogLevel: uint8Ptr(0),
22-
Username: "user",
23-
Allowed: []netip.Prefix{
24-
netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 0),
25-
netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0),
26-
},
15+
Providers: []string{"cloudflare"},
16+
Caching: boolPtr(true),
17+
IPv6: boolPtr(false),
2718
}
2819

2920
b, err := json.Marshal(settings)
3021
require.NoError(t, err)
3122

32-
const expected = `{"providers":["cloudflare"],"caching":true,"ipv6":false,` +
33-
`"verbosity_level":1,"verbosity_details_level":null,"validation_log_level":0,` +
34-
`"username":"user","allowed":["0.0.0.0/0","::/0"]}`
23+
const expected = `{"providers":["cloudflare"],"caching":true,"ipv6":false}`
3524

3625
assert.Equal(t, expected, string(b))
3726

‎internal/configuration/sources/env/unbound.go

-15
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,5 @@ func (s *Source) readUnbound() (unbound settings.Unbound, err error) {
1717
return unbound, err
1818
}
1919

20-
unbound.VerbosityLevel, err = s.env.Uint8Ptr("DOT_VERBOSITY")
21-
if err != nil {
22-
return unbound, err
23-
}
24-
25-
unbound.VerbosityDetailsLevel, err = s.env.Uint8Ptr("DOT_VERBOSITY_DETAILS")
26-
if err != nil {
27-
return unbound, err
28-
}
29-
30-
unbound.ValidationLogLevel, err = s.env.Uint8Ptr("DOT_VALIDATION_LOGLEVEL")
31-
if err != nil {
32-
return unbound, err
33-
}
34-
3520
return unbound, nil
3621
}

‎internal/constants/colors.go

-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ package constants
22

33
import "github.com/fatih/color"
44

5-
func ColorUnbound() *color.Color {
6-
return color.New(color.FgCyan)
7-
}
8-
95
func ColorOpenvpn() *color.Color {
106
return color.New(color.FgHiMagenta)
117
}

‎internal/dns/interfaces.go

-15
This file was deleted.

‎internal/dns/logger.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ package dns
33
type Logger interface {
44
Debug(s string)
55
Info(s string)
6-
Warn(s string)
6+
Warner
77
Error(s string)
88
}
9+
10+
type Warner interface {
11+
Warn(s string)
12+
}

‎internal/dns/logs.go

-75
This file was deleted.

‎internal/dns/logs_test.go

-48
This file was deleted.

‎internal/dns/loop.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import (
55
"net/http"
66
"time"
77

8-
"github.com/qdm12/dns/pkg/blacklist"
8+
"github.com/qdm12/dns/v2/pkg/dot"
9+
"github.com/qdm12/dns/v2/pkg/filter/mapfilter"
910
"github.com/qdm12/gluetun/internal/configuration/settings"
1011
"github.com/qdm12/gluetun/internal/constants"
1112
"github.com/qdm12/gluetun/internal/dns/state"
@@ -16,9 +17,9 @@ import (
1617
type Loop struct {
1718
statusManager *loopstate.State
1819
state *state.State
19-
conf Configurator
20+
server *dot.Server
21+
filter *mapfilter.Filter
2022
resolvConf string
21-
blockBuilder blacklist.Builder
2223
client *http.Client
2324
logger Logger
2425
userTrigger bool
@@ -34,23 +35,24 @@ type Loop struct {
3435

3536
const defaultBackoffTime = 10 * time.Second
3637

37-
func NewLoop(conf Configurator, settings settings.DNS,
38+
func NewLoop(settings settings.DNS,
3839
client *http.Client, logger Logger) *Loop {
3940
start := make(chan struct{})
4041
running := make(chan models.LoopStatus)
4142
stop := make(chan struct{})
4243
stopped := make(chan struct{})
4344
updateTicker := make(chan struct{})
44-
4545
statusManager := loopstate.New(constants.Stopped, start, running, stop, stopped)
4646
state := state.New(statusManager, settings, updateTicker)
4747

48+
filter := mapfilter.New(mapfilter.Settings{})
49+
4850
return &Loop{
4951
statusManager: statusManager,
5052
state: state,
51-
conf: conf,
53+
server: nil,
54+
filter: filter,
5255
resolvConf: "/etc/resolv.conf",
53-
blockBuilder: blacklist.NewBuilder(client),
5456
client: client,
5557
logger: logger,
5658
userTrigger: true,

‎internal/dns/plaintext.go

+23-24
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,43 @@ package dns
22

33
import (
44
"net/netip"
5+
"time"
56

6-
"github.com/qdm12/dns/pkg/nameserver"
7+
"github.com/qdm12/dns/v2/pkg/nameserver"
78
)
89

910
func (l *Loop) useUnencryptedDNS(fallback bool) {
1011
settings := l.GetSettings()
1112

1213
// Try with user provided plaintext ip address
13-
// if it's not 127.0.0.1 (default for DoT)
14-
targetIP := settings.ServerAddress
15-
if targetIP.Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
16-
if fallback {
17-
l.logger.Info("falling back on plaintext DNS at address " + targetIP.String())
18-
} else {
19-
l.logger.Info("using plaintext DNS at address " + targetIP.String())
20-
}
21-
nameserver.UseDNSInternally(targetIP.AsSlice())
22-
err := nameserver.UseDNSSystemWide(l.resolvConf, targetIP.AsSlice(), *settings.KeepNameserver)
23-
if err != nil {
24-
l.logger.Error(err.Error())
25-
}
26-
return
27-
}
28-
29-
// Use first plaintext DNS IPv4 address
30-
targetIP, err := settings.DoT.Unbound.GetFirstPlaintextIPv4()
31-
if err != nil {
32-
// Unbound should always have a default provider
33-
panic(err)
14+
// if it's not 127.0.0.1 (default for DoT), otherwise
15+
// use the first DoT provider ipv4 address found.
16+
var targetIP netip.Addr
17+
if settings.ServerAddress.Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
18+
targetIP = settings.ServerAddress
19+
} else {
20+
targetIP = settings.DoT.Unbound.GetFirstPlaintextIPv4()
3421
}
3522

3623
if fallback {
3724
l.logger.Info("falling back on plaintext DNS at address " + targetIP.String())
3825
} else {
3926
l.logger.Info("using plaintext DNS at address " + targetIP.String())
4027
}
41-
nameserver.UseDNSInternally(targetIP.AsSlice())
42-
err = nameserver.UseDNSSystemWide(l.resolvConf, targetIP.AsSlice(), *settings.KeepNameserver)
28+
29+
const dialTimeout = 3 * time.Second
30+
settingsInternalDNS := nameserver.SettingsInternalDNS{
31+
IP: targetIP,
32+
Timeout: dialTimeout,
33+
}
34+
nameserver.UseDNSInternally(settingsInternalDNS)
35+
36+
settingsSystemWide := nameserver.SettingsSystemDNS{
37+
IP: targetIP,
38+
ResolvPath: l.resolvConf,
39+
KeepNameserver: *settings.KeepNameserver,
40+
}
41+
err := nameserver.UseDNSSystemWide(settingsSystemWide)
4342
if err != nil {
4443
l.logger.Error(err.Error())
4544
}

‎internal/dns/run.go

+13-18
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
2323
for ctx.Err() == nil {
2424
// Upper scope variables for Unbound only
2525
// Their values are to be used if DOT=off
26-
waitError := make(chan error)
27-
unboundCancel := func() { waitError <- nil }
28-
closeStreams := func() {}
26+
var runError <-chan error
2927

3028
for *l.GetSettings().DoT.Enabled {
3129
var err error
32-
unboundCancel, waitError, closeStreams, err = l.setupUnbound(ctx)
30+
runError, err = l.setupUnbound(ctx)
3331
if err == nil {
3432
l.backoffTime = defaultBackoffTime
3533
l.logger.Info("ready")
@@ -43,7 +41,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
4341
return
4442
}
4543

46-
if !errors.Is(err, errUpdateFiles) {
44+
if !errors.Is(err, errUpdateBlockLists) {
4745
const fallback = true
4846
l.useUnencryptedDNS(fallback)
4947
}
@@ -61,30 +59,27 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
6159
for stayHere {
6260
select {
6361
case <-ctx.Done():
64-
unboundCancel()
65-
<-waitError
66-
close(waitError)
67-
closeStreams()
62+
stopErr := l.server.Stop()
63+
if stopErr != nil {
64+
l.logger.Error("stopping DoT server: " + stopErr.Error())
65+
}
66+
// TODO revert OS and Go nameserver when exiting
6867
return
6968
case <-l.stop:
7069
l.userTrigger = true
7170
l.logger.Info("stopping")
7271
const fallback = false
7372
l.useUnencryptedDNS(fallback)
74-
unboundCancel()
75-
<-waitError
76-
// do not close waitError or the waitError
77-
// select case will trigger
78-
closeStreams()
73+
err := l.server.Stop()
74+
if err != nil {
75+
l.logger.Error("stopping DoT server: " + err.Error())
76+
}
7977
l.stopped <- struct{}{}
8078
case <-l.start:
8179
l.userTrigger = true
8280
l.logger.Info("starting")
8381
stayHere = false
84-
case err := <-waitError: // unexpected error
85-
closeStreams()
86-
87-
unboundCancel()
82+
case err := <-runError: // unexpected error
8883
l.statusManager.SetStatus(constants.Crashed)
8984
const fallback = true
9085
l.useUnencryptedDNS(fallback)

‎internal/dns/settings.go

+33
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ package dns
33
import (
44
"context"
55

6+
"github.com/qdm12/dns/v2/pkg/cache"
7+
"github.com/qdm12/dns/v2/pkg/cache/lru"
8+
"github.com/qdm12/dns/v2/pkg/dot"
9+
"github.com/qdm12/dns/v2/pkg/filter/mapfilter"
10+
"github.com/qdm12/dns/v2/pkg/provider"
611
"github.com/qdm12/gluetun/internal/configuration/settings"
712
)
813

@@ -12,3 +17,31 @@ func (l *Loop) SetSettings(ctx context.Context, settings settings.DNS) (
1217
outcome string) {
1318
return l.state.SetSettings(ctx, settings)
1419
}
20+
21+
func buildDoTSettings(settings settings.DNS,
22+
filter *mapfilter.Filter, warner Warner) (
23+
dotSettings dot.ServerSettings) {
24+
var cache cache.Interface
25+
if *settings.DoT.Unbound.Caching {
26+
cache = lru.New(lru.Settings{})
27+
}
28+
providers := make([]provider.Provider, len(settings.DoT.Unbound.Providers))
29+
for i := range settings.DoT.Unbound.Providers {
30+
var err error
31+
providers[i], err = provider.Parse(settings.DoT.Unbound.Providers[i])
32+
if err != nil {
33+
panic(err) // this should already been checked
34+
}
35+
}
36+
37+
return dot.ServerSettings{
38+
Resolver: dot.ResolverSettings{
39+
DoTProviders: settings.DoT.Unbound.Providers,
40+
DNSProviders: settings.DoT.Unbound.Providers,
41+
IPv6: *settings.DoT.Unbound.IPv6,
42+
Warner: warner,
43+
},
44+
Filter: filter,
45+
Cache: cache,
46+
}
47+
}

‎internal/dns/setup.go

+29-33
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,55 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"net"
87

9-
"github.com/qdm12/dns/pkg/check"
10-
"github.com/qdm12/dns/pkg/nameserver"
8+
"github.com/qdm12/dns/v2/pkg/check"
9+
"github.com/qdm12/dns/v2/pkg/dot"
10+
"github.com/qdm12/dns/v2/pkg/nameserver"
1111
)
1212

13-
var errUpdateFiles = errors.New("cannot update files")
13+
var errUpdateBlockLists = errors.New("cannot update filter block lists")
1414

15-
// Returning cancel == nil signals we want to re-run setupUnbound
16-
// Returning err == errUpdateFiles signals we should not fall back
17-
// on the plaintext DNS as DOT is still up and running.
18-
func (l *Loop) setupUnbound(ctx context.Context) (
19-
cancel context.CancelFunc, waitError chan error, closeStreams func(), err error) {
15+
func (l *Loop) setupUnbound(ctx context.Context) (runError <-chan error, err error) {
2016
err = l.updateFiles(ctx)
2117
if err != nil {
22-
return nil, nil, nil,
23-
fmt.Errorf("%w: %s", errUpdateFiles, err)
18+
return nil, fmt.Errorf("%w: %w", errUpdateBlockLists, err)
2419
}
2520

2621
settings := l.GetSettings()
2722

28-
unboundCtx, cancel := context.WithCancel(context.Background())
29-
stdoutLines, stderrLines, waitError, err := l.conf.Start(unboundCtx,
30-
*settings.DoT.Unbound.VerbosityDetailsLevel)
23+
dotSettings := buildDoTSettings(settings, l.filter, l.logger)
24+
server, err := dot.NewServer(dotSettings)
3125
if err != nil {
32-
cancel()
33-
return nil, nil, nil, err
26+
return nil, fmt.Errorf("creating DoT server: %w", err)
3427
}
3528

36-
linesCollectionCtx, linesCollectionCancel := context.WithCancel(context.Background())
37-
lineCollectionDone := make(chan struct{})
38-
go l.collectLines(linesCollectionCtx, lineCollectionDone,
39-
stdoutLines, stderrLines)
40-
closeStreams = func() {
41-
linesCollectionCancel()
42-
<-lineCollectionDone
29+
runError, err = server.Start()
30+
if err != nil {
31+
return nil, fmt.Errorf("starting server: %w", err)
4332
}
33+
l.server = server
4434

4535
// use Unbound
46-
nameserver.UseDNSInternally(settings.ServerAddress.AsSlice())
47-
err = nameserver.UseDNSSystemWide(l.resolvConf, settings.ServerAddress.AsSlice(),
48-
*settings.KeepNameserver)
36+
nameserver.UseDNSInternally(nameserver.SettingsInternalDNS{
37+
IP: settings.ServerAddress,
38+
})
39+
err = nameserver.UseDNSSystemWide(nameserver.SettingsSystemDNS{
40+
IP: settings.ServerAddress,
41+
ResolvPath: l.resolvConf,
42+
KeepNameserver: *settings.KeepNameserver,
43+
})
4944
if err != nil {
5045
l.logger.Error(err.Error())
5146
}
5247

53-
if err := check.WaitForDNS(ctx, net.DefaultResolver); err != nil {
54-
cancel()
55-
<-waitError
56-
close(waitError)
57-
closeStreams()
58-
return nil, nil, nil, err
48+
err = check.WaitForDNS(ctx, check.Settings{})
49+
if err != nil {
50+
stopErr := l.server.Stop()
51+
if stopErr != nil {
52+
l.logger.Error("stopping DoT server: " + stopErr.Error())
53+
}
54+
return nil, err
5955
}
6056

61-
return cancel, waitError, closeStreams, nil
57+
return runError, nil
6258
}

‎internal/dns/ticker.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func (l *Loop) RunRestartTicker(ctx context.Context, done chan<- struct{}) {
3434
if err := l.updateFiles(ctx); err != nil {
3535
l.statusManager.SetStatus(constants.Crashed)
3636
l.logger.Error(err.Error())
37-
l.logger.Warn("skipping Unbound restart due to failed files update")
37+
l.logger.Warn("skipping DNS server restart due to failed files update")
3838
continue
3939
}
4040
}

‎internal/dns/update.go

+24-20
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,39 @@
11
package dns
22

3-
import "context"
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/qdm12/dns/v2/pkg/blockbuilder"
8+
"github.com/qdm12/dns/v2/pkg/filter/update"
9+
)
410

511
func (l *Loop) updateFiles(ctx context.Context) (err error) {
6-
l.logger.Info("downloading DNS over TLS cryptographic files")
7-
if err := l.conf.SetupFiles(ctx); err != nil {
8-
return err
9-
}
1012
settings := l.GetSettings()
1113

12-
unboundSettings, err := settings.DoT.Unbound.ToUnboundFormat()
13-
if err != nil {
14-
return err
14+
l.logger.Info("downloading hostnames and IP block lists")
15+
blacklistSettings := settings.DoT.Blacklist.ToBlockBuilderSettings(l.client)
16+
17+
blockBuilder := blockbuilder.New(blacklistSettings)
18+
result := blockBuilder.BuildAll(ctx)
19+
for _, resultErr := range result.Errors {
20+
if err != nil {
21+
err = fmt.Errorf("%w, %w", err, resultErr)
22+
continue
23+
}
24+
err = resultErr
1525
}
1626

17-
l.logger.Info("downloading hostnames and IP block lists")
18-
blacklistSettings, err := settings.DoT.Blacklist.ToBlacklistFormat()
1927
if err != nil {
2028
return err
2129
}
2230

23-
blockedHostnames, blockedIPs, blockedIPPrefixes, errs :=
24-
l.blockBuilder.All(ctx, blacklistSettings)
25-
for _, err := range errs {
26-
l.logger.Warn(err.Error())
31+
updateSettings := update.Settings{
32+
IPs: result.BlockedIPs,
33+
IPPrefixes: result.BlockedIPPrefixes,
2734
}
35+
updateSettings.BlockHostnames(result.BlockedHostnames)
36+
l.filter.Update(updateSettings)
2837

29-
// TODO change to BlockHostnames() when migrating to qdm12/dns v2
30-
unboundSettings.Blacklist.FqdnHostnames = blockedHostnames
31-
unboundSettings.Blacklist.IPs = blockedIPs
32-
unboundSettings.Blacklist.IPPrefixes = blockedIPPrefixes
33-
34-
return l.conf.MakeUnboundConf(unboundSettings)
38+
return nil
3539
}

0 commit comments

Comments
 (0)
Please sign in to comment.