Align plugin/template usage and syntax with other plugins (#1360)

* Align plugin/template usage and syntax with other plugins

* Use new fallthrough logic in plugin/template

* Use zone name normalization for plugin/template

* Test fallthrough parsing in plugin/template

* Rework scoping of match checks

Most matches are not plugin global but per template. The plugin does only a
very rough check while detailed checks are done per-template.

Per template checks include:
- Zones
- Class/Type
- Regex
- Fallthrough

* Remove trailing `.` from fully qualified domain names

* Register template metrics with zone/class/type instead of regex

* Remove trailing fqdn dot from multiple testcases
This commit is contained in:
Rene Treffer
2018-01-09 22:30:58 +01:00
committed by Miek Gieben
parent a7590897fb
commit 0091e1c9dc
6 changed files with 412 additions and 134 deletions

View File

@@ -9,29 +9,35 @@ The *template* plugin allows you to dynamically repond to queries by just writin
## Syntax
~~~
template CLASS TYPE [REGEX...] {
template CLASS TYPE [ZONE...] {
[match REGEX...]
[answer RR]
[additional RR]
[authority RR]
[...]
[rcode CODE]
[rcode responsecode]
[fallthrough [fallthrough zone...]]
}
~~~
* **CLASS** the query class (usually IN or ANY)
* **TYPE** the query type (A, PTR, ...)
* **CLASS** the query class (usually IN or ANY).
* **TYPE** the query type (A, PTR, ... can be ANY to match all types).
* **ZONE** the zone scope(s) for this template. Defaults to the server zones.
* **REGEX** [Go regexp](https://golang.org/pkg/regexp/) that are matched against the incoming question name. Specifying no regex matches everything (default: `.*`). First matching regex wins.
* `answer|additional|authority` **RR** A [RFC 1035](https://tools.ietf.org/html/rfc1035#section-5) style resource record fragment
build by a [Go template](https://golang.org/pkg/text/template/) that contains the reply.
* `rcode` **CODE** A response code (`NXDOMAIN, SERVFAIL, ...`). The default is `SUCCESS`.
* `fallthrough` Continue with the next plugin if the zone matched but no regex did not match.
* `fallthrough zone` One or more zones that may fall through to other plugins. Defaults to all zones of the template.
At least one answer section or rcode is needed.
At least one `answer` or `rcode` directive is needed (e.g. `rcode NXDOMAIN`).
[Also see](#also-see) contains an additional reading list.
## Templates
Each resource record is a full-featured [Go template](https://golang.org/pkg/text/template/) with the following predefined data
* `.Zone` the matched zone string (e.g. `example.`).
* `.Name` the query name, as a string (lowercased).
* `.Class` the query class (usually `IN`).
* `.Type` the RR type requested (e.g. `PTR`).
@@ -57,6 +63,22 @@ Both failure cases indicate a problem with the template configuration.
## Examples
### Resolve everything to NXDOMAIN
The most simplistic template is
~~~ corefile
. {
template ANY ANY {
rcode NXDOMAIN
}
}
~~~
1. This template uses the default zone (`.` or all queries)
2. All queries will be answered (no `fallthrough`)
3. The answer is always NXDOMAIN
### Resolve .invalid as NXDOMAIN
The `.invalid` domain is a reserved TLD (see [RFC-2606 Reserved Top Level DNS Names](https://tools.ietf.org/html/rfc2606#section-2)) to indicate invalid domains.
@@ -65,7 +87,7 @@ The `.invalid` domain is a reserved TLD (see [RFC-2606 Reserved Top Level DNS Na
. {
proxy . 8.8.8.8
template ANY ANY "[.]invalid[.]$" {
template ANY ANY invalid {
rcode NXDOMAIN
answer "invalid. 60 {{ .Class }} SOA a.invalid. b.invalid. (1 60 60 60 60)"
}
@@ -75,6 +97,7 @@ The `.invalid` domain is a reserved TLD (see [RFC-2606 Reserved Top Level DNS Na
1. A query to .invalid will result in NXDOMAIN (rcode)
2. A dummy SOA record is send to hand out a TTL of 60s for caching
3. Querying `.invalid` of `CH` will also cause a NXDOMAIN/SOA response
4. The default regex is `.*`
### Block invalid search domain completions
@@ -88,14 +111,29 @@ path (`dc1.example.com`) added.
. {
proxy . 8.8.8.8
template IN ANY "[.](example[.]com[.]dc1[.]example[.]com[.])$" {
template IN ANY example.com.dc1.example.com {
rcode NXDOMAIN
answer "{{ index .Match 1 }} 60 IN SOA a.{{ index .Match 1 }} b.{{ index .Match 1 }} (1 60 60 60 60)"
answer "{{ .Zone }} 60 IN SOA a.{{ .Zone }} b.{{ .Zone }} (1 60 60 60 60)"
}
}
~~~
Using numbered matches works well if there are a few groups (1-4).
A more verbose regex based equivalent would be
~~~ corefile
. {
proxy . 8.8.8.8
template IN ANY example.com {
match "(example.com.dc1.example.com)$"
rcode NXDOMAIN
answer "{{ index .Match 1 }} 60 IN SOA a.{{ index .Match 1 }} b.{{ index .Match 1 }} (1 60 60 60 60)"
fallthrough
}
}
~~~
The regex based version can do more complex matching/templating while zone based templating is easier to read and use.
### Resolve A/PTR for .example
@@ -105,13 +143,16 @@ Using numbered matches works well if there are a few groups (1-4).
# ip-a-b-c-d.example.com A a.b.c.d
template IN A (^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ {
template IN A example {
match (^|[.])ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$
answer "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"
fallthrough
}
# d.c.b.a.in-addr.arpa PTR ip-a-b-c-d.example
template IN PTR ^(?P<d>[0-9]*)[.](?P<c>[0-9]*)[.](?P<b>[0-9]*)[.]10[.]in-addr[.]arpa[.]$ {
template IN PTR 10.in-addr.arpa. {
match ^(?P<d>[0-9]*)[.](?P<c>[0-9]*)[.](?P<b>[0-9]*)[.]10[.]in-addr[.]arpa[.]$
answer "{{ .Name }} 60 IN PTR ip-10-{{ .Group.b }}-{{ .Group.c }}-{{ .Group.d }}.example.com."
}
}
@@ -124,14 +165,19 @@ Note that the A record is actually a wildcard, any subdomain of the ip will reso
Having templates to map certain PTR/A pairs is a common pattern.
Fallthrough is needed for mixed domains where only some responses are templated.
### Resolve multiple ip patterns
~~~ corefile
. {
proxy . 8.8.8.8
template IN A "^ip-(?P<a>10)-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]dc[.]example[.]$" "^(?P<a>[0-9]*)[.](?P<b>[0-9]*)[.](?P<c>[0-9]*)[.](?P<d>[0-9]*)[.]ext[.]example[.]$" {
template IN A example {
match "^ip-(?P<a>10)-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]dc[.]example[.]$"
match "^(?P<a>[0-9]*)[.](?P<b>[0-9]*)[.](?P<c>[0-9]*)[.](?P<d>[0-9]*)[.]ext[.]example[.]$"
answer "{{ .Name }} 60 IN A {{ .Group.a}}.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"
fallthrough
}
}
~~~
@@ -144,12 +190,16 @@ Named capture groups can be used to template one response for multiple patterns.
. {
proxy . 8.8.8.8
template IN A ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ {
template IN A example {
match ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$
answer "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"
fallthrough
}
template IN MX ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ {
template IN MX example {
match ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$
answer "{{ .Name }} 60 IN MX 10 {{ .Name }}"
additional "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"
fallthrough
}
}
~~~
@@ -160,20 +210,24 @@ Named capture groups can be used to template one response for multiple patterns.
. {
proxy . 8.8.8.8
template IN A ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ {
template IN A example {
match ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$
answer "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"
authority "example. 60 IN NS ns0.example."
authority "example. 60 IN NS ns1.example."
additional "ns0.example. 60 IN A 203.0.113.8"
additional "ns1.example. 60 IN A 198.51.100.8"
fallthrough
}
template IN MX ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ {
template IN MX example {
match ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$
answer "{{ .Name }} 60 IN MX 10 {{ .Name }}"
additional "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"
authority "example. 60 IN NS ns0.example."
authority "example. 60 IN NS ns1.example."
additional "ns0.example. 60 IN A 203.0.113.8"
additional "ns1.example. 60 IN A 198.51.100.8"
fallthrough
}
}
~~~