Maintenance: qdm12/dns from v1.4.0 to v1.6.0
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
91
internal/configuration/dnsblacklist.go
Normal file
91
internal/configuration/dnsblacklist.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user