diff --git a/Dockerfile b/Dockerfile index 9d8592d2..187dbdc4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -105,7 +105,7 @@ ENV VPNSP=pia \ # DNS over TLS DOT=on \ DOT_PROVIDERS=cloudflare \ - 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:0:0/96 \ + 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 \ DOT_VERBOSITY=1 \ DOT_VERBOSITY_DETAILS=0 \ DOT_VALIDATION_LOGLEVEL=0 \ diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index d6b73ae7..6b509998 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -184,6 +184,9 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, if nonRootUsername != defaultUsername { logger.Info("using existing username %s corresponding to user id %d", nonRootUsername, puid) } + // set it for Unbound + // TODO remove this when migrating to qdm12/dns v2 + allSettings.DNS.Unbound.Username = nonRootUsername if err := os.Chown("/etc/unbound", puid, pgid); err != nil { return err @@ -292,7 +295,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "}) unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, httpClient, - unboundLogger, nonRootUsername, puid, pgid) + unboundLogger, os.OpenFile) dnsCtx, dnsDone := dnsWave.Add("unbound", shutdownRoutineTimeout) // wait for unboundLooper.Restart or its ticker launched with RunRestartTicker go unboundLooper.Run(dnsCtx, dnsDone) diff --git a/go.mod b/go.mod index 0c3ff0a8..70c9ee53 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,10 @@ require ( github.com/fatih/color v1.10.0 github.com/golang/mock v1.5.0 github.com/kyokomi/emoji v2.2.4+incompatible - github.com/qdm12/dns v1.4.0 + github.com/qdm12/dns v1.6.0 github.com/qdm12/golibs v0.0.0-20210402232648-cfebf1e87d1b github.com/qdm12/ss-server v0.1.0 - github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a + github.com/qdm12/updated v0.0.0-20210102012151-76b7f5994638 github.com/stretchr/testify v1.7.0 github.com/vishvananda/netlink v1.1.0 golang.org/x/sys v0.0.0-20201223074533-0d417f636930 diff --git a/go.sum b/go.sum index 2e0f9aa6..08a9ab7c 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= +github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= @@ -111,6 +113,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/qdm12/dns v1.4.0 h1:P8kVMGo7yIEZSk18fA9XQh9faL1CW20aHosWP064MAA= github.com/qdm12/dns v1.4.0/go.mod h1:WUY4/U8Z2O8888DPrahrIBv8GdYeoIcEy4aUDecZ+UM= +github.com/qdm12/dns v1.6.0 h1:vnEEp9vl4ZkhVa3t/2xuS2oms4sfSMLalCjP56rH/Kg= +github.com/qdm12/dns v1.6.0/go.mod h1:eI5ynIACRn+CHABDe2k/+ymDy6v4yAarJs/JifeuQaE= github.com/qdm12/golibs v0.0.0-20201227203847-2fd99ffdfdba/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc= github.com/qdm12/golibs v0.0.0-20210124192933-79a950eaf217/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc= github.com/qdm12/golibs v0.0.0-20210402232648-cfebf1e87d1b h1:tSD0qWqssS8AUFTGmlP1JFfbLViOt7xrjIxGZ1AFacY= @@ -119,6 +123,8 @@ github.com/qdm12/ss-server v0.1.0 h1:WV9MkHCDEWRwe4WpnYFeR/zcZAxYoTbfntLDnw9AQ50 github.com/qdm12/ss-server v0.1.0/go.mod h1:ABVUkxubboL3vqBkOwDV9glX1/x7SnYrckBe5d+M/zw= github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a h1:gkyP+gMEeBgMgyRYGrVNcoy6cL1065IvXsyfB6xboIc= github.com/qdm12/updated v0.0.0-20210102005021-dd457d77f94a/go.mod h1:bbJGxEYCnsA8WU4vBcXYU6mOoHyzdP458FIKP4mfLJM= +github.com/qdm12/updated v0.0.0-20210102012151-76b7f5994638 h1:dM8OO6LO+uou14C5a/LUrot0oyqzPcoiSho6cEmOROc= +github.com/qdm12/updated v0.0.0-20210102012151-76b7f5994638/go.mod h1:bbJGxEYCnsA8WU4vBcXYU6mOoHyzdP458FIKP4mfLJM= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= @@ -164,6 +170,7 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -172,6 +179,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -180,6 +189,7 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -200,6 +210,8 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/configuration/dns.go b/internal/configuration/dns.go index 969e9211..87fc37af 100644 --- a/internal/configuration/dns.go +++ b/internal/configuration/dns.go @@ -7,21 +7,19 @@ import ( "strings" "time" - unboundmodels "github.com/qdm12/dns/pkg/models" - unbound "github.com/qdm12/dns/pkg/unbound" + "github.com/qdm12/dns/pkg/blacklist" + "github.com/qdm12/dns/pkg/unbound" "github.com/qdm12/golibs/params" ) // DNS contains settings to configure Unbound for DNS over TLS operation. type DNS struct { //nolint:maligned - Enabled bool - PlaintextAddress net.IP - KeepNameserver bool - BlockMalicious bool - BlockAds bool - BlockSurveillance bool - UpdatePeriod time.Duration - Unbound unboundmodels.Settings + Enabled bool + PlaintextAddress net.IP + KeepNameserver bool + UpdatePeriod time.Duration + Unbound unbound.Settings + BlacklistBuild blacklist.BuilderSettings } func (settings *DNS) String() string { @@ -50,16 +48,9 @@ func (settings *DNS) lines() (lines []string) { lines = append(lines, indent+indent+indent+line) } - if settings.BlockMalicious { - lines = append(lines, indent+indent+lastIndent+"Block malicious: enabled") - } - - if settings.BlockAds { - lines = append(lines, indent+indent+lastIndent+"Block ads: enabled") - } - - if settings.BlockSurveillance { - lines = append(lines, indent+indent+lastIndent+"Block surveillance: enabled") + lines = append(lines, indent+indent+lastIndent+"Blacklist:") + for _, line := range settings.BlacklistBuild.Lines(indent, lastIndent) { + lines = append(lines, indent+indent+indent+line) } if settings.UpdatePeriod > 0 { @@ -71,9 +62,7 @@ func (settings *DNS) lines() (lines []string) { var ( ErrUnboundSettings = errors.New("failed getting Unbound settings") - ErrDNSProviderNoData = errors.New("DNS provider has no associated data") - ErrDNSProviderNoTLS = errors.New("DNS provider does not support DNS over TLS") - ErrDNSNoIPv6Support = errors.New("no DNS provider supports IPv6") + ErrBlacklistSettings = errors.New("failed getting DNS blacklist settings") ) func (settings *DNS) read(r reader) (err error) { @@ -92,46 +81,20 @@ func (settings *DNS) read(r reader) (err error) { } // DNS over TLS external settings - settings.BlockMalicious, err = r.env.OnOff("BLOCK_MALICIOUS", params.Default("on")) - if err != nil { - return err - } - settings.BlockSurveillance, err = r.env.OnOff("BLOCK_SURVEILLANCE", params.Default("on"), - params.RetroKeys([]string{"BLOCK_NSA"}, r.onRetroActive)) - if err != nil { - return err - } - settings.BlockAds, err = r.env.OnOff("BLOCK_ADS", params.Default("off")) - if err != nil { - return err + if err := settings.readBlacklistBuilding(r); err != nil { + return fmt.Errorf("%w: %s", ErrBlacklistSettings, err) } + settings.UpdatePeriod, err = r.env.Duration("DNS_UPDATE_PERIOD", params.Default("24h")) if err != nil { return err } + // Unbound settings if err := settings.readUnbound(r); err != nil { return fmt.Errorf("%w: %s", ErrUnboundSettings, err) } - // Consistency check - IPv6Support := false - for _, provider := range settings.Unbound.Providers { - providerData, ok := unbound.GetProviderData(provider) - switch { - case !ok: - return fmt.Errorf("%w: %s", ErrDNSProviderNoData, provider) - case !providerData.SupportsTLS: - return fmt.Errorf("%w: %s", ErrDNSProviderNoTLS, provider) - case providerData.SupportsIPv6: - IPv6Support = true - } - } - - if settings.Unbound.IPv6 && !IPv6Support { - return ErrDNSNoIPv6Support - } - return nil } diff --git a/internal/configuration/dns_test.go b/internal/configuration/dns_test.go index ccfb4e7f..f407a85e 100644 --- a/internal/configuration/dns_test.go +++ b/internal/configuration/dns_test.go @@ -5,7 +5,9 @@ import ( "testing" "time" - "github.com/qdm12/dns/pkg/models" + "github.com/qdm12/dns/pkg/blacklist" + "github.com/qdm12/dns/pkg/provider" + "github.com/qdm12/dns/pkg/unbound" "github.com/stretchr/testify/assert" ) @@ -28,13 +30,17 @@ func Test_DNS_Lines(t *testing.T) { settings: DNS{ Enabled: true, KeepNameserver: true, - Unbound: models.Settings{ - Providers: []string{"cloudflare"}, + Unbound: unbound.Settings{ + Providers: []provider.Provider{ + provider.Cloudflare(), + }, }, - BlockMalicious: true, - BlockAds: true, - BlockSurveillance: true, - UpdatePeriod: time.Hour, + BlacklistBuild: blacklist.BuilderSettings{ + BlockMalicious: true, + BlockAds: true, + BlockSurveillance: true, + }, + UpdatePeriod: time.Hour, }, lines: []string{ "|--DNS:", @@ -42,7 +48,7 @@ func Test_DNS_Lines(t *testing.T) { " |--DNS over TLS:", " |--Unbound:", " |--DNS over TLS providers:", - " |--cloudflare", + " |--Cloudflare", " |--Listening port: 0", " |--Access control:", " |--Allowed:", @@ -52,12 +58,9 @@ func Test_DNS_Lines(t *testing.T) { " |--Verbosity level: 0/5", " |--Verbosity details level: 0/4", " |--Validation log level: 0/2", - " |--Blocked hostnames:", - " |--Blocked IP addresses:", - " |--Allowed hostnames:", - " |--Block malicious: enabled", - " |--Block ads: enabled", - " |--Block surveillance: enabled", + " |--Username: ", + " |--Blacklist:", + " |--Blocked categories: malicious, surveillance, ads", " |--Update: every 1h0m0s", }, }, diff --git a/internal/configuration/dnsblacklist.go b/internal/configuration/dnsblacklist.go new file mode 100644 index 00000000..bbbb7fda --- /dev/null +++ b/internal/configuration/dnsblacklist.go @@ -0,0 +1,91 @@ +package configuration + +import ( + "errors" + "fmt" + "net" + + "github.com/qdm12/golibs/params" +) + +func (settings *DNS) readBlacklistBuilding(r reader) (err error) { + settings.BlacklistBuild.BlockMalicious, err = r.env.OnOff("BLOCK_MALICIOUS", params.Default("on")) + if err != nil { + return err + } + + settings.BlacklistBuild.BlockSurveillance, err = r.env.OnOff("BLOCK_SURVEILLANCE", params.Default("on"), + params.RetroKeys([]string{"BLOCK_NSA"}, r.onRetroActive)) + if err != nil { + return err + } + + settings.BlacklistBuild.BlockAds, err = r.env.OnOff("BLOCK_ADS", params.Default("off")) + if err != nil { + return err + } + + if err := settings.readPrivateAddresses(r.env); err != nil { + return err + } + + if err := settings.readBlacklistUnblockedHostnames(r); err != nil { + return err + } + + return nil +} + +var ( + ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range") +) + +func (settings *DNS) readPrivateAddresses(env params.Env) (err error) { + privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS") + if err != nil { + return err + } else if len(privateAddresses) == 0 { + return nil + } + + ips := make([]net.IP, 0, len(privateAddresses)) + ipNets := make([]*net.IPNet, 0, len(privateAddresses)) + + for _, address := range privateAddresses { + ip := net.ParseIP(address) + if ip != nil { + ips = append(ips, ip) + continue + } + + _, ipNet, err := net.ParseCIDR(address) + if err == nil && ipNet != nil { + ipNets = append(ipNets, ipNet) + continue + } + + return fmt.Errorf("%w: %s", ErrInvalidPrivateAddress, address) + } + + settings.BlacklistBuild.AddBlockedIPs = append(settings.BlacklistBuild.AddBlockedIPs, ips...) + settings.BlacklistBuild.AddBlockedIPNets = append(settings.BlacklistBuild.AddBlockedIPNets, ipNets...) + + return nil +} + +func (settings *DNS) readBlacklistUnblockedHostnames(r reader) (err error) { + hostnames, err := r.env.CSV("UNBLOCK") + if err != nil { + return err + } else if len(hostnames) == 0 { + return nil + } + for _, hostname := range hostnames { + if !r.regex.MatchHostname(hostname) { + return fmt.Errorf("%w: %s", ErrInvalidHostname, hostname) + } + } + + settings.BlacklistBuild.AllowedHosts = append(settings.BlacklistBuild.AllowedHosts, hostnames...) + return nil +} diff --git a/internal/configuration/unbound.go b/internal/configuration/unbound.go index 9fb6d99f..932a2472 100644 --- a/internal/configuration/unbound.go +++ b/internal/configuration/unbound.go @@ -6,7 +6,7 @@ import ( "net" "strings" - unbound "github.com/qdm12/dns/pkg/unbound" + "github.com/qdm12/dns/pkg/provider" "github.com/qdm12/golibs/params" ) @@ -47,14 +47,6 @@ func (settings *DNS) readUnbound(r reader) (err error) { } settings.Unbound.ValidationLogLevel = uint8(validationLogLevel) - if err := settings.readUnboundPrivateAddresses(r.env); err != nil { - return err - } - - if err := settings.readUnboundUnblockedHostnames(r); err != nil { - return err - } - settings.Unbound.AccessControl.Allowed = []net.IPNet{ { IP: net.IPv4zero, @@ -78,56 +70,16 @@ func (settings *DNS) readUnboundProviders(env params.Env) (err error) { if err != nil { return err } - for _, provider := range strings.Split(s, ",") { - _, ok := unbound.GetProviderData(provider) - if !ok { - return fmt.Errorf("%w: %s", ErrInvalidDNSOverTLSProvider, provider) + for _, field := range strings.Split(s, ",") { + dnsProvider, err := provider.Parse(field) + if err != nil { + return fmt.Errorf("%w: %s", ErrInvalidDNSOverTLSProvider, err) } - settings.Unbound.Providers = append(settings.Unbound.Providers, provider) + settings.Unbound.Providers = append(settings.Unbound.Providers, dnsProvider) } return nil } -var ( - ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range") -) - -func (settings *DNS) readUnboundPrivateAddresses(env params.Env) (err error) { - privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS") - if err != nil { - return err - } else if len(privateAddresses) == 0 { - return nil - } - for _, address := range privateAddresses { - ip := net.ParseIP(address) - _, _, err := net.ParseCIDR(address) - if ip == nil && err != nil { - return fmt.Errorf("%w: %s", ErrInvalidPrivateAddress, address) - } - } - settings.Unbound.BlockedIPs = append( - settings.Unbound.BlockedIPs, privateAddresses...) - return nil -} - var ( ErrInvalidHostname = errors.New("invalid hostname") ) - -func (settings *DNS) readUnboundUnblockedHostnames(r reader) (err error) { - hostnames, err := r.env.CSV("UNBLOCK") - if err != nil { - return err - } else if len(hostnames) == 0 { - return nil - } - for _, hostname := range hostnames { - if !r.regex.MatchHostname(hostname) { - return fmt.Errorf("%w: %s", ErrInvalidHostname, hostname) - } - } - settings.Unbound.AllowedHostnames = append( - settings.Unbound.AllowedHostnames, hostnames...) - return nil -} diff --git a/internal/dns/loop.go b/internal/dns/loop.go index 948e27a9..4ff8d3bf 100644 --- a/internal/dns/loop.go +++ b/internal/dns/loop.go @@ -9,11 +9,15 @@ import ( "sync" "time" + "github.com/qdm12/dns/pkg/blacklist" + "github.com/qdm12/dns/pkg/check" + "github.com/qdm12/dns/pkg/nameserver" "github.com/qdm12/dns/pkg/unbound" "github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/models" "github.com/qdm12/golibs/logging" + "github.com/qdm12/golibs/os" ) type Looper interface { @@ -28,11 +32,9 @@ type Looper interface { type looper struct { state state conf unbound.Configurator + blockBuilder blacklist.Builder client *http.Client logger logging.Logger - username string - puid int - pgid int loopLock sync.Mutex start chan struct{} running chan models.LoopStatus @@ -42,23 +44,22 @@ type looper struct { backoffTime time.Duration timeNow func() time.Time timeSince func(time.Time) time.Duration + openFile os.OpenFileFunc } const defaultBackoffTime = 10 * time.Second func NewLooper(conf unbound.Configurator, settings configuration.DNS, client *http.Client, - logger logging.Logger, username string, puid, pgid int) Looper { + logger logging.Logger, openFile os.OpenFileFunc) Looper { return &looper{ state: state{ status: constants.Stopped, settings: settings, }, conf: conf, + blockBuilder: blacklist.NewBuilder(client), client: client, logger: logger, - username: username, - puid: puid, - pgid: pgid, start: make(chan struct{}), running: make(chan models.LoopStatus), stop: make(chan struct{}), @@ -67,6 +68,7 @@ func NewLooper(conf unbound.Configurator, settings configuration.DNS, client *ht backoffTime: defaultBackoffTime, timeNow: time.Now, timeSince: time.Since, + openFile: openFile, } } @@ -197,12 +199,15 @@ func (l *looper) setupUnbound(ctx context.Context, previousCrashed bool) ( collectLinesDone := make(chan struct{}) go l.collectLines(stdoutLines, stderrLines, collectLinesDone) - l.conf.UseDNSInternally(net.IP{127, 0, 0, 1}) // use Unbound - if err := l.conf.UseDNSSystemWide(net.IP{127, 0, 0, 1}, settings.KeepNameserver); err != nil { // use Unbound + // use Unbound + nameserver.UseDNSInternally(net.IP{127, 0, 0, 1}) + err = nameserver.UseDNSSystemWide(l.openFile, + net.IP{127, 0, 0, 1}, settings.KeepNameserver) + if err != nil { l.logger.Error(err) } - if err := l.conf.WaitForUnbound(ctx); err != nil { + if err := check.WaitForDNS(ctx, net.DefaultResolver); err != nil { if !previousCrashed { l.running <- constants.Crashed } @@ -243,34 +248,25 @@ func (l *looper) useUnencryptedDNS(fallback bool) { } else { l.logger.Info("using plaintext DNS at address %s", targetIP) } - l.conf.UseDNSInternally(targetIP) - if err := l.conf.UseDNSSystemWide(targetIP, settings.KeepNameserver); err != nil { + nameserver.UseDNSInternally(targetIP) + if err := nameserver.UseDNSSystemWide(l.openFile, + targetIP, settings.KeepNameserver); err != nil { l.logger.Error(err) } return } - // Try with any IPv4 address from the providers chosen - for _, provider := range settings.Unbound.Providers { - data, _ := unbound.GetProviderData(provider) - for _, targetIP = range data.IPs { - if targetIP.To4() != nil { - if fallback { - l.logger.Info("falling back on plaintext DNS at address %s", targetIP) - } else { - l.logger.Info("using plaintext DNS at address %s", targetIP) - } - l.conf.UseDNSInternally(targetIP) - if err := l.conf.UseDNSSystemWide(targetIP, settings.KeepNameserver); err != nil { - l.logger.Error(err) - } - return - } - } + provider := settings.Unbound.Providers[0] + targetIP = provider.DoT().IPv4[0] + if fallback { + l.logger.Info("falling back on plaintext DNS at address " + targetIP.String()) + } else { + l.logger.Info("using plaintext DNS at address " + targetIP.String()) + } + nameserver.UseDNSInternally(targetIP) + if err := nameserver.UseDNSSystemWide(l.openFile, targetIP, settings.KeepNameserver); err != nil { + l.logger.Error(err) } - - // No IPv4 address found - l.logger.Error("no ipv4 DNS address found for providers %s", settings.Unbound.Providers) } func (l *looper) RunRestartTicker(ctx context.Context, done chan<- struct{}) { @@ -339,18 +335,16 @@ func (l *looper) updateFiles(ctx context.Context) (err error) { settings := l.GetSettings() l.logger.Info("downloading hostnames and IP block lists") - hostnameLines, ipLines, errs := l.conf.BuildBlocked(ctx, l.client, - settings.BlockMalicious, settings.BlockAds, settings.BlockSurveillance, - settings.Unbound.BlockedHostnames, settings.Unbound.BlockedIPs, - settings.Unbound.AllowedHostnames) + blockedHostnames, blockedIPs, blockedIPNets, errs := l.blockBuilder.All( + ctx, settings.BlacklistBuild) for _, err := range errs { l.logger.Warn(err) } - if err := l.conf.MakeUnboundConf( - settings.Unbound, hostnameLines, ipLines, - l.username, l.puid, l.pgid); err != nil { - return err - } - return nil + // TODO change to BlockHostnames() when migrating to qdm12/dns v2 + settings.Unbound.Blacklist.FqdnHostnames = blockedHostnames + settings.Unbound.Blacklist.IPs = blockedIPs + settings.Unbound.Blacklist.IPNets = blockedIPNets + + return l.conf.MakeUnboundConf(settings.Unbound) }