Compare commits
5 Commits
dependabot
...
remove-kee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f0b8f7292 | ||
|
|
25b381e138 | ||
|
|
35b6b709b2 | ||
|
|
40ea51a3ae | ||
|
|
1a93a41a55 |
@@ -167,7 +167,6 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
HEALTH_ICMP_TARGET_IP=1.1.1.1 \
|
HEALTH_ICMP_TARGET_IP=1.1.1.1 \
|
||||||
HEALTH_RESTART_VPN=on \
|
HEALTH_RESTART_VPN=on \
|
||||||
# DNS
|
# DNS
|
||||||
DNS_SERVER=on \
|
|
||||||
DNS_UPSTREAM_RESOLVER_TYPE=DoT \
|
DNS_UPSTREAM_RESOLVER_TYPE=DoT \
|
||||||
DNS_UPSTREAM_RESOLVERS=cloudflare \
|
DNS_UPSTREAM_RESOLVERS=cloudflare \
|
||||||
DNS_BLOCK_IPS= \
|
DNS_BLOCK_IPS= \
|
||||||
@@ -180,8 +179,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
DNS_UNBLOCK_HOSTNAMES= \
|
DNS_UNBLOCK_HOSTNAMES= \
|
||||||
DNS_REBINDING_PROTECTION_EXEMPT_HOSTNAMES= \
|
DNS_REBINDING_PROTECTION_EXEMPT_HOSTNAMES= \
|
||||||
DNS_UPDATE_PERIOD=24h \
|
DNS_UPDATE_PERIOD=24h \
|
||||||
DNS_ADDRESS=127.0.0.1 \
|
DNS_UPSTREAM_PLAIN_ADDRESSES= \
|
||||||
DNS_KEEP_NAMESERVER=off \
|
|
||||||
# HTTP proxy
|
# HTTP proxy
|
||||||
HTTPPROXY= \
|
HTTPPROXY= \
|
||||||
HTTPPROXY_LOG=off \
|
HTTPPROXY_LOG=off \
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ func readObsolete(r *reader.Reader) (warnings []string) {
|
|||||||
"DOT_VALIDATION_LOGLEVEL": "DOT_VALIDATION_LOGLEVEL is obsolete because DNSSEC validation is not implemented.",
|
"DOT_VALIDATION_LOGLEVEL": "DOT_VALIDATION_LOGLEVEL is obsolete because DNSSEC validation is not implemented.",
|
||||||
"HEALTH_VPN_DURATION_INITIAL": "HEALTH_VPN_DURATION_INITIAL is obsolete",
|
"HEALTH_VPN_DURATION_INITIAL": "HEALTH_VPN_DURATION_INITIAL is obsolete",
|
||||||
"HEALTH_VPN_DURATION_ADDITION": "HEALTH_VPN_DURATION_ADDITION is obsolete",
|
"HEALTH_VPN_DURATION_ADDITION": "HEALTH_VPN_DURATION_ADDITION is obsolete",
|
||||||
|
"DNS_SERVER": "DNS_SERVER is obsolete because the forwarding server is always enabled.",
|
||||||
|
"DOT": "DOT is obsolete because the forwarding server is always enabled.",
|
||||||
|
"DNS_KEEP_NAMESERVER": "DNS_KEEP_NAMESERVER is obsolete because the forwarding server is always used and " +
|
||||||
|
"forwards local names to private DNS resolvers found in /etc/resolv.conf",
|
||||||
}
|
}
|
||||||
sortedKeys := maps.Keys(keyToMessage)
|
sortedKeys := maps.Keys(keyToMessage)
|
||||||
slices.Sort(sortedKeys)
|
slices.Sort(sortedKeys)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/dns/v2/pkg/provider"
|
"github.com/qdm12/dns/v2/pkg/provider"
|
||||||
@@ -13,20 +14,25 @@ import (
|
|||||||
"github.com/qdm12/gotree"
|
"github.com/qdm12/gotree"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DNSUpstreamTypeDot = "dot"
|
||||||
|
DNSUpstreamTypeDoh = "doh"
|
||||||
|
DNSUpstreamTypePlain = "plain"
|
||||||
|
)
|
||||||
|
|
||||||
// DNS contains settings to configure DNS.
|
// DNS contains settings to configure DNS.
|
||||||
type DNS struct {
|
type DNS struct {
|
||||||
// ServerEnabled is true if the server should be running
|
// UpstreamType can be [dnsUpstreamTypeDot], [dnsUpstreamTypeDoh]
|
||||||
// and used. It defaults to true, and cannot be nil
|
// or [dnsUpstreamTypePlain]. It defaults to [dnsUpstreamTypeDot].
|
||||||
// in the internal state.
|
|
||||||
ServerEnabled *bool
|
|
||||||
// UpstreamType can be dot or plain, and defaults to dot.
|
|
||||||
UpstreamType string `json:"upstream_type"`
|
UpstreamType string `json:"upstream_type"`
|
||||||
// UpdatePeriod is the period to update DNS block lists.
|
// UpdatePeriod is the period to update DNS block lists.
|
||||||
// It can be set to 0 to disable the update.
|
// It can be set to 0 to disable the update.
|
||||||
// It defaults to 24h and cannot be nil in
|
// It defaults to 24h and cannot be nil in
|
||||||
// the internal state.
|
// the internal state.
|
||||||
UpdatePeriod *time.Duration
|
UpdatePeriod *time.Duration
|
||||||
// Providers is a list of DNS providers
|
// Providers is a list of DNS providers.
|
||||||
|
// It defaults to either ["cloudflare"] or [] if the
|
||||||
|
// UpstreamPlainAddresses field is set.
|
||||||
Providers []string `json:"providers"`
|
Providers []string `json:"providers"`
|
||||||
// Caching is true if the server should cache
|
// Caching is true if the server should cache
|
||||||
// DNS responses.
|
// DNS responses.
|
||||||
@@ -36,32 +42,23 @@ type DNS struct {
|
|||||||
// Blacklist contains settings to configure the filter
|
// Blacklist contains settings to configure the filter
|
||||||
// block lists.
|
// block lists.
|
||||||
Blacklist DNSBlacklist
|
Blacklist DNSBlacklist
|
||||||
// ServerAddress is the DNS server to use inside
|
// UpstreamPlainAddresses are the upstream plaintext DNS resolver
|
||||||
// the Go program and for the system.
|
// addresses to use by the built-in DNS server forwarder.
|
||||||
// It defaults to '127.0.0.1' to be used with the
|
// Note, if the upstream type is [dnsUpstreamTypePlain] these are merged
|
||||||
// local server. It cannot be the zero value in the internal
|
// together with provider names set in the Providers field.
|
||||||
// state.
|
// If this field is set, the Providers field will default to the empty slice.
|
||||||
ServerAddress netip.Addr
|
UpstreamPlainAddresses []netip.AddrPort
|
||||||
// KeepNameserver is true if the existing DNS server
|
|
||||||
// found in /etc/resolv.conf should be used
|
|
||||||
// Note setting this to true will likely DNS traffic
|
|
||||||
// outside the VPN tunnel since it would go through
|
|
||||||
// the local DNS server of your Docker/Kubernetes
|
|
||||||
// configuration, which is likely not going through the tunnel.
|
|
||||||
// This will also disable the DNS forwarder server and the
|
|
||||||
// `ServerAddress` field will be ignored.
|
|
||||||
// It defaults to false and cannot be nil in the
|
|
||||||
// internal state.
|
|
||||||
KeepNameserver *bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrDNSUpstreamTypeNotValid = errors.New("DNS upstream type is not valid")
|
ErrDNSUpstreamTypeNotValid = errors.New("DNS upstream type is not valid")
|
||||||
ErrDNSUpdatePeriodTooShort = errors.New("update period is too short")
|
ErrDNSUpdatePeriodTooShort = errors.New("update period is too short")
|
||||||
|
ErrDNSUpstreamPlainNoIPv6 = errors.New("upstream plain addresses do not contain any IPv6 address")
|
||||||
|
ErrDNSUpstreamPlainNoIPv4 = errors.New("upstream plain addresses do not contain any IPv4 address")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d DNS) validate() (err error) {
|
func (d DNS) validate() (err error) {
|
||||||
if !helpers.IsOneOf(d.UpstreamType, "dot", "doh", "plain") {
|
if !helpers.IsOneOf(d.UpstreamType, DNSUpstreamTypeDot, DNSUpstreamTypeDoh, DNSUpstreamTypePlain) {
|
||||||
return fmt.Errorf("%w: %s", ErrDNSUpstreamTypeNotValid, d.UpstreamType)
|
return fmt.Errorf("%w: %s", ErrDNSUpstreamTypeNotValid, d.UpstreamType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +76,18 @@ func (d DNS) validate() (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.UpstreamType == DNSUpstreamTypePlain {
|
||||||
|
if *d.IPv6 && !slices.ContainsFunc(d.UpstreamPlainAddresses, func(addrPort netip.AddrPort) bool {
|
||||||
|
return addrPort.Addr().Is6()
|
||||||
|
}) {
|
||||||
|
return fmt.Errorf("%w: in %d addresses", ErrDNSUpstreamPlainNoIPv6, len(d.UpstreamPlainAddresses))
|
||||||
|
} else if !slices.ContainsFunc(d.UpstreamPlainAddresses, func(addrPort netip.AddrPort) bool {
|
||||||
|
return addrPort.Addr().Is4()
|
||||||
|
}) {
|
||||||
|
return fmt.Errorf("%w: in %d addresses", ErrDNSUpstreamPlainNoIPv4, len(d.UpstreamPlainAddresses))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = d.Blacklist.validate()
|
err = d.Blacklist.validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -89,15 +98,13 @@ func (d DNS) validate() (err error) {
|
|||||||
|
|
||||||
func (d *DNS) Copy() (copied DNS) {
|
func (d *DNS) Copy() (copied DNS) {
|
||||||
return DNS{
|
return DNS{
|
||||||
ServerEnabled: gosettings.CopyPointer(d.ServerEnabled),
|
UpstreamType: d.UpstreamType,
|
||||||
UpstreamType: d.UpstreamType,
|
UpdatePeriod: gosettings.CopyPointer(d.UpdatePeriod),
|
||||||
UpdatePeriod: gosettings.CopyPointer(d.UpdatePeriod),
|
Providers: gosettings.CopySlice(d.Providers),
|
||||||
Providers: gosettings.CopySlice(d.Providers),
|
Caching: gosettings.CopyPointer(d.Caching),
|
||||||
Caching: gosettings.CopyPointer(d.Caching),
|
IPv6: gosettings.CopyPointer(d.IPv6),
|
||||||
IPv6: gosettings.CopyPointer(d.IPv6),
|
Blacklist: d.Blacklist.copy(),
|
||||||
Blacklist: d.Blacklist.copy(),
|
UpstreamPlainAddresses: d.UpstreamPlainAddresses,
|
||||||
ServerAddress: d.ServerAddress,
|
|
||||||
KeepNameserver: gosettings.CopyPointer(d.KeepNameserver),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,20 +112,17 @@ func (d *DNS) Copy() (copied DNS) {
|
|||||||
// settings object with any field set in the other
|
// settings object with any field set in the other
|
||||||
// settings.
|
// settings.
|
||||||
func (d *DNS) overrideWith(other DNS) {
|
func (d *DNS) overrideWith(other DNS) {
|
||||||
d.ServerEnabled = gosettings.OverrideWithPointer(d.ServerEnabled, other.ServerEnabled)
|
|
||||||
d.UpstreamType = gosettings.OverrideWithComparable(d.UpstreamType, other.UpstreamType)
|
d.UpstreamType = gosettings.OverrideWithComparable(d.UpstreamType, other.UpstreamType)
|
||||||
d.UpdatePeriod = gosettings.OverrideWithPointer(d.UpdatePeriod, other.UpdatePeriod)
|
d.UpdatePeriod = gosettings.OverrideWithPointer(d.UpdatePeriod, other.UpdatePeriod)
|
||||||
d.Providers = gosettings.OverrideWithSlice(d.Providers, other.Providers)
|
d.Providers = gosettings.OverrideWithSlice(d.Providers, other.Providers)
|
||||||
d.Caching = gosettings.OverrideWithPointer(d.Caching, other.Caching)
|
d.Caching = gosettings.OverrideWithPointer(d.Caching, other.Caching)
|
||||||
d.IPv6 = gosettings.OverrideWithPointer(d.IPv6, other.IPv6)
|
d.IPv6 = gosettings.OverrideWithPointer(d.IPv6, other.IPv6)
|
||||||
d.Blacklist.overrideWith(other.Blacklist)
|
d.Blacklist.overrideWith(other.Blacklist)
|
||||||
d.ServerAddress = gosettings.OverrideWithValidator(d.ServerAddress, other.ServerAddress)
|
d.UpstreamPlainAddresses = gosettings.OverrideWithSlice(d.UpstreamPlainAddresses, other.UpstreamPlainAddresses)
|
||||||
d.KeepNameserver = gosettings.OverrideWithPointer(d.KeepNameserver, other.KeepNameserver)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNS) setDefaults() {
|
func (d *DNS) setDefaults() {
|
||||||
d.ServerEnabled = gosettings.DefaultPointer(d.ServerEnabled, true)
|
d.UpstreamType = gosettings.DefaultComparable(d.UpstreamType, DNSUpstreamTypeDot)
|
||||||
d.UpstreamType = gosettings.DefaultComparable(d.UpstreamType, "dot")
|
|
||||||
const defaultUpdatePeriod = 24 * time.Hour
|
const defaultUpdatePeriod = 24 * time.Hour
|
||||||
d.UpdatePeriod = gosettings.DefaultPointer(d.UpdatePeriod, defaultUpdatePeriod)
|
d.UpdatePeriod = gosettings.DefaultPointer(d.UpdatePeriod, defaultUpdatePeriod)
|
||||||
d.Providers = gosettings.DefaultSlice(d.Providers, []string{
|
d.Providers = gosettings.DefaultSlice(d.Providers, []string{
|
||||||
@@ -127,26 +131,53 @@ func (d *DNS) setDefaults() {
|
|||||||
d.Caching = gosettings.DefaultPointer(d.Caching, true)
|
d.Caching = gosettings.DefaultPointer(d.Caching, true)
|
||||||
d.IPv6 = gosettings.DefaultPointer(d.IPv6, false)
|
d.IPv6 = gosettings.DefaultPointer(d.IPv6, false)
|
||||||
d.Blacklist.setDefaults()
|
d.Blacklist.setDefaults()
|
||||||
d.ServerAddress = gosettings.DefaultValidator(d.ServerAddress,
|
d.UpstreamPlainAddresses = gosettings.DefaultSlice(d.UpstreamPlainAddresses, []netip.AddrPort{})
|
||||||
netip.AddrFrom4([4]byte{127, 0, 0, 1}))
|
}
|
||||||
d.KeepNameserver = gosettings.DefaultPointer(d.KeepNameserver, false)
|
|
||||||
|
func defaultDNSProviders() []string {
|
||||||
|
return []string{
|
||||||
|
provider.Cloudflare().Name,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DNS) GetFirstPlaintextIPv4() (ipv4 netip.Addr) {
|
func (d DNS) GetFirstPlaintextIPv4() (ipv4 netip.Addr) {
|
||||||
localhost := netip.AddrFrom4([4]byte{127, 0, 0, 1})
|
if d.UpstreamType == DNSUpstreamTypePlain {
|
||||||
if d.ServerAddress.Compare(localhost) != 0 && d.ServerAddress.Is4() {
|
for _, addrPort := range d.UpstreamPlainAddresses {
|
||||||
return d.ServerAddress
|
if addrPort.Addr().Is4() {
|
||||||
|
return addrPort.Addr()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipv4 = findPlainIPv4InProviders(d.Providers)
|
||||||
|
if ipv4.IsValid() {
|
||||||
|
return ipv4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either:
|
||||||
|
// - all upstream plain addresses are IPv6 and no provider is set
|
||||||
|
// - all providers set do not have a plaintext IPv4 address
|
||||||
|
ipv4 = findPlainIPv4InProviders(defaultDNSProviders())
|
||||||
|
if !ipv4.IsValid() {
|
||||||
|
panic("no plaintext IPv4 address found in default DNS providers")
|
||||||
|
}
|
||||||
|
return ipv4
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPlainIPv4InProviders(providerNames []string) netip.Addr {
|
||||||
providers := provider.NewProviders()
|
providers := provider.NewProviders()
|
||||||
provider, err := providers.Get(d.Providers[0])
|
for _, name := range providerNames {
|
||||||
if err != nil {
|
provider, err := providers.Get(name)
|
||||||
// Settings should be validated before calling this function,
|
if err != nil {
|
||||||
// so an error happening here is a programming error.
|
// Settings should be validated before calling this function,
|
||||||
panic(err)
|
// so an error happening here is a programming error.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if len(provider.Plain.IPv4) > 0 {
|
||||||
|
return provider.Plain.IPv4[0].Addr()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return netip.Addr{}
|
||||||
return provider.Plain.IPv4[0].Addr()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DNS) String() string {
|
func (d DNS) String() string {
|
||||||
@@ -155,22 +186,22 @@ func (d DNS) String() string {
|
|||||||
|
|
||||||
func (d DNS) toLinesNode() (node *gotree.Node) {
|
func (d DNS) toLinesNode() (node *gotree.Node) {
|
||||||
node = gotree.New("DNS settings:")
|
node = gotree.New("DNS settings:")
|
||||||
node.Appendf("Keep existing nameserver(s): %s", gosettings.BoolToYesNo(d.KeepNameserver))
|
|
||||||
if *d.KeepNameserver {
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
node.Appendf("DNS server address to use: %s", d.ServerAddress)
|
|
||||||
|
|
||||||
node.Appendf("DNS forwarder server enabled: %s", gosettings.BoolToYesNo(d.ServerEnabled))
|
|
||||||
if !*d.ServerEnabled {
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
node.Appendf("Upstream resolver type: %s", d.UpstreamType)
|
node.Appendf("Upstream resolver type: %s", d.UpstreamType)
|
||||||
|
|
||||||
upstreamResolvers := node.Append("Upstream resolvers:")
|
upstreamResolvers := node.Append("Upstream resolvers:")
|
||||||
for _, provider := range d.Providers {
|
if len(d.UpstreamPlainAddresses) > 0 {
|
||||||
upstreamResolvers.Append(provider)
|
if d.UpstreamType == DNSUpstreamTypePlain {
|
||||||
|
for _, addr := range d.UpstreamPlainAddresses {
|
||||||
|
upstreamResolvers.Append(addr.String())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.Appendf("Upstream plain addresses: ignored because upstream type is not plain")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, provider := range d.Providers {
|
||||||
|
upstreamResolvers.Append(provider)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node.Appendf("Caching: %s", gosettings.BoolToYesNo(d.Caching))
|
node.Appendf("Caching: %s", gosettings.BoolToYesNo(d.Caching))
|
||||||
@@ -188,11 +219,6 @@ func (d DNS) toLinesNode() (node *gotree.Node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNS) read(r *reader.Reader) (err error) {
|
func (d *DNS) read(r *reader.Reader) (err error) {
|
||||||
d.ServerEnabled, err = r.BoolPtr("DNS_SERVER", reader.RetroKeys("DOT"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.UpstreamType = r.String("DNS_UPSTREAM_RESOLVER_TYPE")
|
d.UpstreamType = r.String("DNS_UPSTREAM_RESOLVER_TYPE")
|
||||||
|
|
||||||
d.UpdatePeriod, err = r.DurationPtr("DNS_UPDATE_PERIOD")
|
d.UpdatePeriod, err = r.DurationPtr("DNS_UPDATE_PERIOD")
|
||||||
@@ -217,15 +243,43 @@ func (d *DNS) read(r *reader.Reader) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.ServerAddress, err = r.NetipAddr("DNS_ADDRESS", reader.RetroKeys("DNS_PLAINTEXT_ADDRESS"))
|
err = d.readUpstreamPlainAddresses(r)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.KeepNameserver, err = r.BoolPtr("DNS_KEEP_NAMESERVER")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DNS) readUpstreamPlainAddresses(r *reader.Reader) (err error) {
|
||||||
|
// If DNS_UPSTREAM_PLAIN_ADDRESSES is set, the user must also set DNS_UPSTREAM_TYPE=plain
|
||||||
|
// for these to be used. This is an added safety measure to reduce misunderstandings, and
|
||||||
|
// reduce odd settings overrides.
|
||||||
|
d.UpstreamPlainAddresses, err = r.CSVNetipAddrPorts("DNS_UPSTREAM_PLAIN_ADDRESSES")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retro-compatibility - remove in v4
|
||||||
|
// If DNS_ADDRESS is set to a non-localhost address, append it to the other
|
||||||
|
// upstream plain addresses, assuming port 53, and force the upstream type to plain AND
|
||||||
|
// clear any user picked providers, to maintain retro-compatibility behavior.
|
||||||
|
serverAddress, err := r.NetipAddr("DNS_ADDRESS",
|
||||||
|
reader.RetroKeys("DNS_PLAINTEXT_ADDRESS"),
|
||||||
|
reader.IsRetro("DNS_UPSTREAM_PLAIN_ADDRESSES"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if !serverAddress.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
isLocalhost := serverAddress.Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) == 0
|
||||||
|
if isLocalhost {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
const defaultPlainPort = 53
|
||||||
|
addrPort := netip.AddrPortFrom(serverAddress, defaultPlainPort)
|
||||||
|
d.UpstreamPlainAddresses = append(d.UpstreamPlainAddresses, addrPort)
|
||||||
|
d.UpstreamType = DNSUpstreamTypePlain
|
||||||
|
d.Providers = []string{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
26
internal/configuration/settings/dns_test.go
Normal file
26
internal/configuration/settings/dns_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/qdm12/dns/v2/pkg/provider"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_defaultDNSProviders(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
names := defaultDNSProviders()
|
||||||
|
|
||||||
|
found := false
|
||||||
|
providers := provider.NewProviders()
|
||||||
|
for _, name := range names {
|
||||||
|
provider, err := providers.Get(name)
|
||||||
|
require.NoError(t, err)
|
||||||
|
if len(provider.Plain.IPv4) > 0 {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.True(t, found, "no default DNS provider has a plaintext IPv4 address")
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
@@ -174,13 +173,11 @@ func (s Settings) Warnings() (warnings []string) {
|
|||||||
"by creating an issue, attaching the new certificate and we will update Gluetun.")
|
"by creating an issue, attaching the new certificate and we will update Gluetun.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO remove in v4
|
for _, upstreamAddress := range s.DNS.UpstreamPlainAddresses {
|
||||||
if s.DNS.ServerAddress.Unmap().Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
|
if upstreamAddress.Addr().IsPrivate() {
|
||||||
warnings = append(warnings, "DNS address is set to "+s.DNS.ServerAddress.String()+
|
warnings = append(warnings, "DNS upstream address "+upstreamAddress.String()+" is private: "+
|
||||||
" so the local forwarding DNS server will not be used."+
|
"DNS traffic might leak out of the VPN tunnel to that address.")
|
||||||
" The default value changed to 127.0.0.1 so it uses the internal DNS server."+
|
}
|
||||||
" If this server fails to start, the IPv4 address of the first plaintext DNS server"+
|
|
||||||
" corresponding to the first DNS provider chosen is used.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return warnings
|
return warnings
|
||||||
|
|||||||
@@ -38,9 +38,6 @@ func Test_Settings_String(t *testing.T) {
|
|||||||
| ├── Run OpenVPN as: root
|
| ├── Run OpenVPN as: root
|
||||||
| └── Verbosity level: 1
|
| └── Verbosity level: 1
|
||||||
├── DNS settings:
|
├── DNS settings:
|
||||||
| ├── Keep existing nameserver(s): no
|
|
||||||
| ├── DNS server address to use: 127.0.0.1
|
|
||||||
| ├── DNS forwarder server enabled: yes
|
|
||||||
| ├── Upstream resolver type: dot
|
| ├── Upstream resolver type: dot
|
||||||
| ├── Upstream resolvers:
|
| ├── Upstream resolvers:
|
||||||
| | └── Cloudflare
|
| | └── Cloudflare
|
||||||
|
|||||||
@@ -18,14 +18,8 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if *l.GetSettings().KeepNameserver {
|
const fallback = false
|
||||||
l.logger.Warn("⚠️⚠️⚠️ keeping the default container nameservers, " +
|
l.useUnencryptedDNS(fallback)
|
||||||
"this will likely leak DNS traffic outside the VPN " +
|
|
||||||
"and go through your container network DNS outside the VPN tunnel!")
|
|
||||||
} else {
|
|
||||||
const fallback = false
|
|
||||||
l.useUnencryptedDNS(fallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-l.start:
|
case <-l.start:
|
||||||
@@ -38,8 +32,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
// Their values are to be used if DOT=off
|
// Their values are to be used if DOT=off
|
||||||
var runError <-chan error
|
var runError <-chan error
|
||||||
|
|
||||||
settings := l.GetSettings()
|
for {
|
||||||
for !*settings.KeepNameserver && *settings.ServerEnabled {
|
|
||||||
var err error
|
var err error
|
||||||
runError, err = l.setupServer(ctx)
|
runError, err = l.setupServer(ctx)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -59,15 +52,11 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
l.useUnencryptedDNS(fallback)
|
l.useUnencryptedDNS(fallback)
|
||||||
}
|
}
|
||||||
l.logAndWait(ctx, err)
|
l.logAndWait(ctx, err)
|
||||||
settings = l.GetSettings()
|
|
||||||
}
|
}
|
||||||
l.signalOrSetStatus(constants.Running)
|
l.signalOrSetStatus(constants.Running)
|
||||||
|
|
||||||
settings = l.GetSettings()
|
const fallback = false
|
||||||
if !*settings.KeepNameserver && !*settings.ServerEnabled {
|
l.useUnencryptedDNS(fallback)
|
||||||
const fallback = false
|
|
||||||
l.useUnencryptedDNS(fallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.userTrigger = false
|
l.userTrigger = false
|
||||||
|
|
||||||
@@ -82,19 +71,15 @@ func (l *Loop) runWait(ctx context.Context, runError <-chan error) (exitLoop boo
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
if !*l.GetSettings().KeepNameserver {
|
l.stopServer()
|
||||||
l.stopServer()
|
// TODO revert OS and Go nameserver when exiting
|
||||||
// TODO revert OS and Go nameserver when exiting
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
case <-l.stop:
|
case <-l.stop:
|
||||||
l.userTrigger = true
|
l.userTrigger = true
|
||||||
l.logger.Info("stopping")
|
l.logger.Info("stopping")
|
||||||
if !*l.GetSettings().KeepNameserver {
|
const fallback = false
|
||||||
const fallback = false
|
l.useUnencryptedDNS(fallback)
|
||||||
l.useUnencryptedDNS(fallback)
|
l.stopServer()
|
||||||
l.stopServer()
|
|
||||||
}
|
|
||||||
l.stopped <- struct{}{}
|
l.stopped <- struct{}{}
|
||||||
case <-l.start:
|
case <-l.start:
|
||||||
l.userTrigger = true
|
l.userTrigger = true
|
||||||
|
|||||||
@@ -26,31 +26,23 @@ func (l *Loop) SetSettings(ctx context.Context, settings settings.DNS) (
|
|||||||
return l.state.SetSettings(ctx, settings)
|
return l.state.SetSettings(ctx, settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildServerSettings(settings settings.DNS,
|
func buildServerSettings(userSettings settings.DNS,
|
||||||
filter *mapfilter.Filter, localResolvers []netip.Addr,
|
filter *mapfilter.Filter, localResolvers []netip.Addr,
|
||||||
logger Logger) (
|
logger Logger) (
|
||||||
serverSettings server.Settings, err error,
|
serverSettings server.Settings, err error,
|
||||||
) {
|
) {
|
||||||
serverSettings.Logger = logger
|
serverSettings.Logger = logger
|
||||||
|
|
||||||
providersData := provider.NewProviders()
|
upstreamResolvers := buildProviders(userSettings)
|
||||||
upstreamResolvers := make([]provider.Provider, len(settings.Providers))
|
|
||||||
for i := range settings.Providers {
|
|
||||||
var err error
|
|
||||||
upstreamResolvers[i], err = providersData.Get(settings.Providers[i])
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // this should already had been checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ipVersion := "ipv4"
|
ipVersion := "ipv4"
|
||||||
if *settings.IPv6 {
|
if *userSettings.IPv6 {
|
||||||
ipVersion = "ipv6"
|
ipVersion = "ipv6"
|
||||||
}
|
}
|
||||||
|
|
||||||
var dialer server.Dialer
|
var dialer server.Dialer
|
||||||
switch settings.UpstreamType {
|
switch userSettings.UpstreamType {
|
||||||
case "dot":
|
case settings.DNSUpstreamTypeDot:
|
||||||
dialerSettings := dot.Settings{
|
dialerSettings := dot.Settings{
|
||||||
UpstreamResolvers: upstreamResolvers,
|
UpstreamResolvers: upstreamResolvers,
|
||||||
IPVersion: ipVersion,
|
IPVersion: ipVersion,
|
||||||
@@ -59,7 +51,7 @@ func buildServerSettings(settings settings.DNS,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return server.Settings{}, fmt.Errorf("creating DNS over TLS dialer: %w", err)
|
return server.Settings{}, fmt.Errorf("creating DNS over TLS dialer: %w", err)
|
||||||
}
|
}
|
||||||
case "doh":
|
case settings.DNSUpstreamTypeDoh:
|
||||||
dialerSettings := doh.Settings{
|
dialerSettings := doh.Settings{
|
||||||
UpstreamResolvers: upstreamResolvers,
|
UpstreamResolvers: upstreamResolvers,
|
||||||
IPVersion: ipVersion,
|
IPVersion: ipVersion,
|
||||||
@@ -68,7 +60,7 @@ func buildServerSettings(settings settings.DNS,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return server.Settings{}, fmt.Errorf("creating DNS over HTTPS dialer: %w", err)
|
return server.Settings{}, fmt.Errorf("creating DNS over HTTPS dialer: %w", err)
|
||||||
}
|
}
|
||||||
case "plain":
|
case settings.DNSUpstreamTypePlain:
|
||||||
dialerSettings := plain.Settings{
|
dialerSettings := plain.Settings{
|
||||||
UpstreamResolvers: upstreamResolvers,
|
UpstreamResolvers: upstreamResolvers,
|
||||||
IPVersion: ipVersion,
|
IPVersion: ipVersion,
|
||||||
@@ -78,11 +70,11 @@ func buildServerSettings(settings settings.DNS,
|
|||||||
return server.Settings{}, fmt.Errorf("creating plain DNS dialer: %w", err)
|
return server.Settings{}, fmt.Errorf("creating plain DNS dialer: %w", err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("unknown upstream type: " + settings.UpstreamType)
|
panic("unknown upstream type: " + userSettings.UpstreamType)
|
||||||
}
|
}
|
||||||
serverSettings.Dialer = dialer
|
serverSettings.Dialer = dialer
|
||||||
|
|
||||||
if *settings.Caching {
|
if *userSettings.Caching {
|
||||||
lruCache, err := lru.New(lru.Settings{})
|
lruCache, err := lru.New(lru.Settings{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return server.Settings{}, fmt.Errorf("creating LRU cache: %w", err)
|
return server.Settings{}, fmt.Errorf("creating LRU cache: %w", err)
|
||||||
@@ -123,3 +115,48 @@ func buildServerSettings(settings settings.DNS,
|
|||||||
|
|
||||||
return serverSettings, nil
|
return serverSettings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildProviders(userSettings settings.DNS) []provider.Provider {
|
||||||
|
if userSettings.UpstreamType == settings.DNSUpstreamTypePlain &&
|
||||||
|
len(userSettings.UpstreamPlainAddresses) > 0 {
|
||||||
|
providers := make([]provider.Provider, len(userSettings.UpstreamPlainAddresses))
|
||||||
|
for i, addrPort := range userSettings.UpstreamPlainAddresses {
|
||||||
|
providers[i] = provider.Provider{
|
||||||
|
Name: addrPort.String(),
|
||||||
|
}
|
||||||
|
if addrPort.Addr().Is4() {
|
||||||
|
providers[i].Plain.IPv4 = []netip.AddrPort{addrPort}
|
||||||
|
} else {
|
||||||
|
providers[i].Plain.IPv6 = []netip.AddrPort{addrPort}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
providersData := provider.NewProviders()
|
||||||
|
providers := make([]provider.Provider, 0, len(userSettings.Providers)+len(userSettings.UpstreamPlainAddresses))
|
||||||
|
for _, providerName := range userSettings.Providers {
|
||||||
|
provider, err := providersData.Get(providerName)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // this should already had been checked
|
||||||
|
}
|
||||||
|
providers = append(providers, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
if userSettings.UpstreamType != settings.DNSUpstreamTypePlain {
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addrPort := range userSettings.UpstreamPlainAddresses {
|
||||||
|
newProvider := provider.Provider{
|
||||||
|
Name: addrPort.String(),
|
||||||
|
}
|
||||||
|
if addrPort.Addr().Is4() {
|
||||||
|
newProvider.Plain.IPv4 = []netip.AddrPort{addrPort}
|
||||||
|
} else {
|
||||||
|
newProvider.Plain.IPv6 = []netip.AddrPort{addrPort}
|
||||||
|
}
|
||||||
|
providers = append(providers, newProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/qdm12/dns/v2/pkg/check"
|
"github.com/qdm12/dns/v2/pkg/check"
|
||||||
"github.com/qdm12/dns/v2/pkg/nameserver"
|
"github.com/qdm12/dns/v2/pkg/nameserver"
|
||||||
@@ -38,12 +37,8 @@ func (l *Loop) setupServer(ctx context.Context) (runError <-chan error, err erro
|
|||||||
l.server = server
|
l.server = server
|
||||||
|
|
||||||
// use internal DNS server
|
// use internal DNS server
|
||||||
const defaultDNSPort = 53
|
nameserver.UseDNSInternally(nameserver.SettingsInternalDNS{})
|
||||||
nameserver.UseDNSInternally(nameserver.SettingsInternalDNS{
|
|
||||||
AddrPort: netip.AddrPortFrom(settings.ServerAddress, defaultDNSPort),
|
|
||||||
})
|
|
||||||
err = nameserver.UseDNSSystemWide(nameserver.SettingsSystemDNS{
|
err = nameserver.UseDNSSystemWide(nameserver.SettingsSystemDNS{
|
||||||
IPs: []netip.Addr{settings.ServerAddress},
|
|
||||||
ResolvPath: l.resolvConf,
|
ResolvPath: l.resolvConf,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ func (s *State) SetSettings(ctx context.Context, settings settings.DNS) (
|
|||||||
|
|
||||||
// Restart
|
// Restart
|
||||||
_, _ = s.statusApplier.ApplyStatus(ctx, constants.Stopped)
|
_, _ = s.statusApplier.ApplyStatus(ctx, constants.Stopped)
|
||||||
if *settings.ServerEnabled {
|
outcome, _ = s.statusApplier.ApplyStatus(ctx, constants.Running)
|
||||||
outcome, _ = s.statusApplier.ApplyStatus(ctx, constants.Running)
|
|
||||||
}
|
|
||||||
return outcome
|
return outcome
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/dns/v2/pkg/check"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/version"
|
"github.com/qdm12/gluetun/internal/version"
|
||||||
)
|
)
|
||||||
@@ -46,14 +45,7 @@ func (l *Loop) onTunnelUp(ctx, loopCtx context.Context, data tunnelUpData) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if *l.dnsLooper.GetSettings().ServerEnabled {
|
_, _ = l.dnsLooper.ApplyStatus(ctx, constants.Running)
|
||||||
_, _ = l.dnsLooper.ApplyStatus(ctx, constants.Running)
|
|
||||||
} else {
|
|
||||||
err := check.WaitForDNS(ctx, check.Settings{})
|
|
||||||
if err != nil {
|
|
||||||
l.logger.Error("waiting for DNS to be ready: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = l.publicip.RunOnce(ctx)
|
err = l.publicip.RunOnce(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user