Added SOA configuration for DNS settings. (#4714)
This will allow to fine TUNE SOA settings sent by Consul in DNS responses, for instance to be able to control negative ttl. Will fix: https://github.com/hashicorp/consul/issues/4713 # Example Override all settings: * min_ttl: 0 => 60s * retry: 600 (10m) => 300s (5 minutes), * expire: 86400 (24h) => 43200 (12h) * refresh: 3600 (1h) => 1800 (30 minutes) ``` consul agent -dev -hcl 'dns_config={soa={min_ttl=60,retry=300,expire=43200,refresh=1800}}' ``` Result: ``` dig +multiline @localhost -p 8600 service.consul ; <<>> DiG 9.12.1 <<>> +multiline @localhost -p 8600 service.consul ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 36557 ;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;service.consul. IN A ;; AUTHORITY SECTION: consul. 0 IN SOA ns.consul. hostmaster.consul. ( 1537959133 ; serial 1800 ; refresh (30 minutes) 300 ; retry (5 minutes) 43200 ; expire (12 hours) 60 ; minimum (1 minute) ) ;; Query time: 4 msec ;; SERVER: 127.0.0.1#8600(127.0.0.1) ;; WHEN: Wed Sep 26 12:52:13 CEST 2018 ;; MSG SIZE rcvd: 93 ```
This commit is contained in:
parent
c22ee028c1
commit
42f250fa53
|
@ -301,6 +301,22 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
|
|||
dnsServiceTTL[k] = b.durationVal(fmt.Sprintf("dns_config.service_ttl[%q]", k), &v)
|
||||
}
|
||||
|
||||
soa := RuntimeSOAConfig{Refresh: 3600, Retry: 600, Expire: 86400, Minttl: 0}
|
||||
if c.DNS.SOA != nil {
|
||||
if c.DNS.SOA.Expire != nil {
|
||||
soa.Expire = *c.DNS.SOA.Expire
|
||||
}
|
||||
if c.DNS.SOA.Minttl != nil {
|
||||
soa.Minttl = *c.DNS.SOA.Minttl
|
||||
}
|
||||
if c.DNS.SOA.Refresh != nil {
|
||||
soa.Refresh = *c.DNS.SOA.Refresh
|
||||
}
|
||||
if c.DNS.SOA.Retry != nil {
|
||||
soa.Retry = *c.DNS.SOA.Retry
|
||||
}
|
||||
}
|
||||
|
||||
leaveOnTerm := !b.boolVal(c.ServerMode)
|
||||
if c.LeaveOnTerm != nil {
|
||||
leaveOnTerm = b.boolVal(c.LeaveOnTerm)
|
||||
|
@ -649,6 +665,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
|
|||
DNSRecursorTimeout: b.durationVal("recursor_timeout", c.DNS.RecursorTimeout),
|
||||
DNSRecursors: dnsRecursors,
|
||||
DNSServiceTTL: dnsServiceTTL,
|
||||
DNSSOA: soa,
|
||||
DNSUDPAnswerLimit: b.intVal(c.DNS.UDPAnswerLimit),
|
||||
DNSNodeMetaTXT: b.boolValWithDefault(c.DNS.NodeMetaTXT, true),
|
||||
|
||||
|
|
|
@ -521,6 +521,14 @@ type ConnectProxyDefaults struct {
|
|||
Config map[string]interface{} `json:"config,omitempty" hcl:"config" mapstructure:"config"`
|
||||
}
|
||||
|
||||
// SOA is the configuration of SOA for DNS
|
||||
type SOA struct {
|
||||
Refresh *uint32 `json:"refresh,omitempty" hcl:"refresh" mapstructure:"refresh"`
|
||||
Retry *uint32 `json:"retry,omitempty" hcl:"retry" mapstructure:"retry"`
|
||||
Expire *uint32 `json:"expire,omitempty" hcl:"expire" mapstructure:"expire"`
|
||||
Minttl *uint32 `json:"min_ttl,omitempty" hcl:"min_ttl" mapstructure:"min_ttl"`
|
||||
}
|
||||
|
||||
type DNS struct {
|
||||
AllowStale *bool `json:"allow_stale,omitempty" hcl:"allow_stale" mapstructure:"allow_stale"`
|
||||
ARecordLimit *int `json:"a_record_limit,omitempty" hcl:"a_record_limit" mapstructure:"a_record_limit"`
|
||||
|
@ -533,6 +541,7 @@ type DNS struct {
|
|||
ServiceTTL map[string]string `json:"service_ttl,omitempty" hcl:"service_ttl" mapstructure:"service_ttl"`
|
||||
UDPAnswerLimit *int `json:"udp_answer_limit,omitempty" hcl:"udp_answer_limit" mapstructure:"udp_answer_limit"`
|
||||
NodeMetaTXT *bool `json:"enable_additional_node_meta_txt,omitempty" hcl:"enable_additional_node_meta_txt" mapstructure:"enable_additional_node_meta_txt"`
|
||||
SOA *SOA `json:"soa,omitempty" hcl:"soa" mapstructure:"soa"`
|
||||
}
|
||||
|
||||
type HTTPConfig struct {
|
||||
|
|
|
@ -16,6 +16,13 @@ import (
|
|||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type RuntimeSOAConfig struct {
|
||||
Refresh uint32 // 3600 by default
|
||||
Retry uint32 // 600
|
||||
Expire uint32 // 86400
|
||||
Minttl uint32 // 0,
|
||||
}
|
||||
|
||||
// RuntimeConfig specifies the configuration the consul agent actually
|
||||
// uses. Is is derived from one or more Config structures which can come
|
||||
// from files, flags and/or environment variables.
|
||||
|
@ -538,6 +545,10 @@ type RuntimeConfig struct {
|
|||
// flags: -dns-port int
|
||||
DNSPort int
|
||||
|
||||
// DNSSOA is the settings applied for DNS SOA
|
||||
// hcl: soa {}
|
||||
DNSSOA RuntimeSOAConfig
|
||||
|
||||
// DataDir is the path to the directory where the local state is stored.
|
||||
//
|
||||
// hcl: data_dir = string
|
||||
|
|
|
@ -4110,6 +4110,7 @@ func TestFullConfig(t *testing.T) {
|
|||
DNSPort: 7001,
|
||||
DNSRecursorTimeout: 4427 * time.Second,
|
||||
DNSRecursors: []string{"63.38.39.58", "92.49.18.18"},
|
||||
DNSSOA: RuntimeSOAConfig{Refresh: 3600, Retry: 600, Expire: 86400, Minttl: 0},
|
||||
DNSServiceTTL: map[string]time.Duration{"*": 32030 * time.Second},
|
||||
DNSUDPAnswerLimit: 29909,
|
||||
DNSNodeMetaTXT: true,
|
||||
|
@ -4754,6 +4755,7 @@ func TestSanitize(t *testing.T) {
|
|||
&net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5678},
|
||||
&net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5678},
|
||||
},
|
||||
DNSSOA: RuntimeSOAConfig{Refresh: 3600, Retry: 600, Expire: 86400, Minttl: 0},
|
||||
HTTPAddrs: []net.Addr{
|
||||
&net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5678},
|
||||
&net.UnixAddr{Name: "/var/run/foo"},
|
||||
|
@ -4894,6 +4896,12 @@ func TestSanitize(t *testing.T) {
|
|||
"DNSRecursorTimeout": "0s",
|
||||
"DNSRecursors": [],
|
||||
"DNSServiceTTL": {},
|
||||
"DNSSOA": {
|
||||
"Refresh": 3600,
|
||||
"Retry": 600,
|
||||
"Expire": 86400,
|
||||
"Minttl": 0
|
||||
},
|
||||
"DNSUDPAnswerLimit": 0,
|
||||
"DataDir": "",
|
||||
"Datacenter": "",
|
||||
|
|
|
@ -940,8 +940,8 @@ func TestCatalog_ListNodes_StaleRead(t *testing.T) {
|
|||
// Try to join
|
||||
joinLAN(t, s2, s1)
|
||||
|
||||
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
||||
testrpc.WaitForLeader(t, s2.RPC, "dc1")
|
||||
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
||||
testrpc.WaitForTestAgent(t, s2.RPC, "dc1")
|
||||
|
||||
// Use the follower as the client
|
||||
var codec rpc.ClientCodec
|
||||
|
|
32
agent/dns.go
32
agent/dns.go
|
@ -39,6 +39,13 @@ const (
|
|||
|
||||
var InvalidDnsRe = regexp.MustCompile(`[^A-Za-z0-9\\-]+`)
|
||||
|
||||
type dnsSOAConfig struct {
|
||||
Refresh uint32 // 3600 by default
|
||||
Retry uint32 // 600
|
||||
Expire uint32 // 86400
|
||||
Minttl uint32 // 0,
|
||||
}
|
||||
|
||||
type dnsConfig struct {
|
||||
AllowStale bool
|
||||
Datacenter string
|
||||
|
@ -53,6 +60,7 @@ type dnsConfig struct {
|
|||
UDPAnswerLimit int
|
||||
ARecordLimit int
|
||||
NodeMetaTXT bool
|
||||
dnsSOAConfig dnsSOAConfig
|
||||
}
|
||||
|
||||
// DNSServer is used to wrap an Agent and expose various
|
||||
|
@ -97,6 +105,7 @@ func NewDNSServer(a *Agent) (*DNSServer, error) {
|
|||
return srv, nil
|
||||
}
|
||||
|
||||
// GetDNSConfig takes global config and creates the config used by DNS server
|
||||
func GetDNSConfig(conf *config.RuntimeConfig) *dnsConfig {
|
||||
return &dnsConfig{
|
||||
AllowStale: conf.DNSAllowStale,
|
||||
|
@ -112,6 +121,12 @@ func GetDNSConfig(conf *config.RuntimeConfig) *dnsConfig {
|
|||
ServiceTTL: conf.DNSServiceTTL,
|
||||
UDPAnswerLimit: conf.DNSUDPAnswerLimit,
|
||||
NodeMetaTXT: conf.DNSNodeMetaTXT,
|
||||
dnsSOAConfig: dnsSOAConfig{
|
||||
Expire: conf.DNSSOA.Expire,
|
||||
Minttl: conf.DNSSOA.Minttl,
|
||||
Refresh: conf.DNSSOA.Refresh,
|
||||
Retry: conf.DNSSOA.Retry,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,17 +364,16 @@ func (d *DNSServer) soa() *dns.SOA {
|
|||
Name: d.domain,
|
||||
Rrtype: dns.TypeSOA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 0,
|
||||
// Has to be consistent with MinTTL to avoid invalidation
|
||||
Ttl: d.config.dnsSOAConfig.Minttl,
|
||||
},
|
||||
Ns: "ns." + d.domain,
|
||||
Serial: uint32(time.Now().Unix()),
|
||||
|
||||
// todo(fs): make these configurable
|
||||
Ns: "ns." + d.domain,
|
||||
Serial: uint32(time.Now().Unix()),
|
||||
Mbox: "hostmaster." + d.domain,
|
||||
Refresh: 3600,
|
||||
Retry: 600,
|
||||
Expire: 86400,
|
||||
Minttl: 0,
|
||||
Refresh: d.config.dnsSOAConfig.Refresh,
|
||||
Retry: d.config.dnsSOAConfig.Retry,
|
||||
Expire: d.config.dnsSOAConfig.Expire,
|
||||
Minttl: d.config.dnsSOAConfig.Minttl,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1115,6 +1115,39 @@ func TestDNS_ServiceReverseLookup_CustomDomain(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDNS_SOA_Settings(t *testing.T) {
|
||||
t.Parallel()
|
||||
testSoaWithConfig := func(config string, ttl, expire, refresh, retry uint) {
|
||||
a := NewTestAgent(t.Name(), config)
|
||||
defer a.Shutdown()
|
||||
testrpc.WaitForLeader(t, a.RPC, "dc1")
|
||||
|
||||
// lookup a non-existing node, we should receive a SOA
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("nofoo.node.dc1.consul.", dns.TypeANY)
|
||||
|
||||
c := new(dns.Client)
|
||||
in, _, err := c.Exchange(m, a.DNSAddr())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, in.Ns, 1)
|
||||
soaRec, ok := in.Ns[0].(*dns.SOA)
|
||||
require.True(t, ok, "NS RR is not a SOA record")
|
||||
require.Equal(t, uint32(ttl), soaRec.Minttl)
|
||||
require.Equal(t, uint32(expire), soaRec.Expire)
|
||||
require.Equal(t, uint32(refresh), soaRec.Refresh)
|
||||
require.Equal(t, uint32(retry), soaRec.Retry)
|
||||
require.Equal(t, uint32(ttl), soaRec.Hdr.Ttl)
|
||||
}
|
||||
// Default configuration
|
||||
testSoaWithConfig("", 0, 86400, 3600, 600)
|
||||
// Override all settings
|
||||
testSoaWithConfig("dns_config={soa={min_ttl=60,expire=43200,refresh=1800,retry=300}}", 60, 43200, 1800, 300)
|
||||
// Override partial settings
|
||||
testSoaWithConfig("dns_config={soa={min_ttl=60,expire=43200}}", 60, 43200, 3600, 600)
|
||||
// Override partial settings, part II
|
||||
testSoaWithConfig("dns_config={soa={refresh=1800,retry=300}}", 0, 86400, 1800, 300)
|
||||
}
|
||||
|
||||
func TestDNS_ServiceReverseLookupNodeAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := NewTestAgent(t.Name(), "")
|
||||
|
|
|
@ -68,7 +68,7 @@ func TestUiNodes(t *testing.T) {
|
|||
t.Parallel()
|
||||
a := NewTestAgent(t.Name(), "")
|
||||
defer a.Shutdown()
|
||||
testrpc.WaitForLeader(t, a.RPC, "dc1")
|
||||
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
|
||||
|
||||
args := &structs.RegisterRequest{
|
||||
Datacenter: "dc1",
|
||||
|
|
|
@ -1242,6 +1242,7 @@ func TestAPI_AgentConnectCARoots_list(t *testing.T) {
|
|||
defer s.Stop()
|
||||
|
||||
agent := c.Agent()
|
||||
s.WaitForSerfCheck(t)
|
||||
list, meta, err := agent.ConnectCARoots(nil)
|
||||
require.NoError(err)
|
||||
require.True(meta.LastIndex > 0)
|
||||
|
@ -1286,6 +1287,7 @@ func TestAPI_AgentConnectAuthorize(t *testing.T) {
|
|||
defer s.Stop()
|
||||
|
||||
agent := c.Agent()
|
||||
s.WaitForSerfCheck(t)
|
||||
params := &AgentAuthorizeParams{
|
||||
Target: "foo",
|
||||
ClientCertSerial: "fake",
|
||||
|
|
|
@ -884,6 +884,28 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
|
|||
same TXT records when they would be added to the Answer section of the response like when querying with type TXT or ANY. This
|
||||
defaults to true.
|
||||
|
||||
* <a name="soa"></a><a href="#soa">`soa`</a> Allow to tune the setting set up in SOA.
|
||||
Non specified values fallback to their default values, all values are integers and
|
||||
expressed as seconds.
|
||||
|
||||
The following settings are available:
|
||||
|
||||
* <a name="soa_expire"></a><a href="soa_expire">expire</a> -
|
||||
Configure SOA Expire duration in seconds, default value is 86400, ie: 24 hours.
|
||||
|
||||
* <a name="soa_min_ttl"></a><a href="soa_min_ttl">`min_ttl`</a> -
|
||||
Configure SOA DNS minimum TTL.
|
||||
As explained in [RFC-2308](https://tools.ietf.org/html/rfc2308) this also controls
|
||||
negative cache TTL in most implementations. Default value is 0, ie: no minimum
|
||||
delay or negative TTL.
|
||||
|
||||
* <a name="soa_refresh"></a><a href="soa_refresh">refresh</a> -
|
||||
Configure SOA Refresh duration in seconds, default value is `3600`, ie: 1 hour.
|
||||
|
||||
* <a name="soa_retry"></a><a href="soa_retry">retry</a> -
|
||||
Configures the Retry duration expressed in seconds, default value is
|
||||
600, ie: 10 minutes.
|
||||
|
||||
* <a name="domain"></a><a href="#domain">`domain`</a> Equivalent to the
|
||||
[`-domain` command-line flag](#_domain).
|
||||
|
||||
|
|
|
@ -65,6 +65,11 @@ client and Consul and set the cache values appropriately. In many cases
|
|||
"appropriately" simply is turning negative response caching off to get the best
|
||||
recovery time when a service becomes available again.
|
||||
|
||||
With versions of Consul greater than 1.3.0, it is now possible to tune SOA
|
||||
responses and modify the negative TTL cache for some resolvers. It can
|
||||
be achieved using the [`soa.min_ttl`](/docs/agent/options.html#soa_min_ttl)
|
||||
configuration within the [`soa`](/docs/agent/options.html#soa) configuration.
|
||||
|
||||
<a name="ttl"></a>
|
||||
## TTL Values
|
||||
|
||||
|
|
Loading…
Reference in New Issue