说明
通过编写一个 (Go) 模板,template 插件允许您动态响应查询。
语法
template CLASS TYPE [ZONE...] {
match REGEX...
answer RR
additional RR
authority RR
rcode CODE
ederror EXTENDED_ERROR_CODE [EXTRA_REASON]
fallthrough [FALLTHROUGH-ZONE...]
}
- CLASS 查询类(通常为 IN 或 ANY)。
- TYPE 查询类型(A、PTR、…可以为 ANY 以匹配所有类型)。
- ZONE 此模板的区域作用域。默认为服务器区域。
match
REGEX Go 正则表达式,它与传入问题名称进行匹配。不指定正则表达式将匹配所有内容(默认值:.*
)。第一个匹配的正则表达式获胜。answer|additional|authority
RR RFC 1035 样式的资源记录片段,由 Go 模板 构建,其中包含回复。不指定答案将导致响应的答案部分为空。rcode
CODE 响应代码(NXDOMAIN、SERVFAIL、…
)。默认值为NOERROR
。有效的响应代码值是msg.go
中的miekg/dns
包定义的RcodeToString
映射。ederror
EXTENDED_ERROR_CODE 是在RFC8914
(0、1、2、…、24)中定义的数字形式的扩展 DNS 错误代码。EXTRA_REASON 是一个附加字符串,说明错误返回的原因。fallthrough
如果 template 的 ZONE 匹配查询名称,但没有正则表达式匹配,则继续下一个 template 实例。如果没有下一个 template,则使用下一个插件继续解析。如果列出了 [FALLTHROUGH-ZONE…] (例如in-addr.arpa
和ip6.arpa
),那么只有针对这些区域的查询才需要进行 FallThrough。如果没有fallthrough
,当 template 的 ZONE 匹配查询,但没有正则表达式匹配,则会返回SERVFAIL
响应。
另外请参阅 包含其他阅读清单。
模板
每个资源记录都是一个功能齐全的 Go 模板,具有以下预定义数据
.Zone
匹配的区域字符串(例如example.
)。.Name
查询名称,作为字符串(小写)。.Class
查询类(通常为IN
)。.Type
请求的 RR 类型(例如PTR
)。.Match
所有匹配项的数组。index .Match 0
指整个匹配项。.Group
命名捕获组的映射。.Message
完整的传入 DNS 消息。.Question
匹配的问题部分。.Remote
客户端 IP 地址.Meta
,一个获取元数据名称并返回其值的函数,只要元数据插件已启用。例如,.Meta "kubernetes/client-namespace"
以下是一些预定义的 模板函数
parseInt
根据给定的基数和位大小解释字符串。等效于 strconv.ParseUint。
模板输出必须为 RFC 1035 样式的资源记录(通常称为“区域文件”)。
注意 Go 模板和 CoreDNS 配置文件之间存在语法问题。CoreDNS(和 Caddy)会将诸如 {{$var}}
的表达式解释为对环境变量的引用,而 {{ $var }}
可以正常工作。请参阅 错误 和 corefile(5)。
监控
如果启用了监控(通过 prometheus 插件),则导出以下监控指标
coredns_template_matches_total{server, zone, view, class, type}
按正则表达式匹配的请求总数。coredns_template_template_failures_total{server, zone, view, class, type, section, template}
Go 模板化失败的次数。正则表达式、section 和模板标签值可用于将错误映射回配置文件。coredns_template_rr_failures_total{server, zone, view, class, type, section, template}
模板化资源记录无效且无法解析的次数。正则表达式、section 和模板标签值可用于将错误映射回配置文件。
两种失败情况都表示模板配置有问题。server
标签指示增加指标的服务器,有关详细信息,请参阅 metrics 插件。
示例
将所有解析结果解析为 NXDOMAIN
最简单的模板如下
. {
template ANY ANY {
rcode NXDOMAIN
}
}
- 此模板使用默认区域(
.
或所有查询)。 - 所有查询都将得到响应(没有
fallthrough
)。 - 响应始终为 NXDOMAIN。
将 .invalid 解析为 NXDOMAIN
.invalid
域是保留的 TLD(请参阅 RFC 2606 保留的顶级 DNS 名称)表示无效的域。
. {
forward . 8.8.8.8
template ANY ANY invalid {
rcode NXDOMAIN
authority "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)"
ederror 21 "Blocked according to RFC2606"
}
}
- 查询 .invalid 将导致 NXDOMAIN(响应代码)。
- 将发送一个虚拟 SOA 记录以提供 60 秒的缓存 TTL。
- 在
CH
类中查询.invalid
也将导致 NXDOMAIN/SOA 响应。 - 默认正则表达式为
.*
。
阻止无效的搜索域完成
想象一下你在 example.com
中运行带有数据中心 dc1.example.com
的情况。数据中心域是 DNS 搜索域的一部分。但是,something.example.com.dc1.example.com
将表明一个完全限定的域名(something.example.com
),而该域名无意中添加了默认域或搜索路径(dc1.example.com
)。
. {
forward . 8.8.8.8
template IN ANY example.com.dc1.example.com {
rcode NXDOMAIN
authority "{{ .Zone }} 60 IN SOA ns.example.com hostmaster.example.com (1 60 60 60 60)"
}
}
基于正则表达式的更冗长的等效代码如下:
. {
forward . 8.8.8.8
template IN ANY example.com {
match "example\.com\.(dc1\.example\.com\.)$"
rcode NXDOMAIN
authority "{{ index .Match 1 }} 60 IN SOA ns.{{ index .Match 1 }} hostmaster.{{ index .Match 1 }} (1 60 60 60 60)"
fallthrough
}
}
基于正则表达式的版本可以进行更复杂的匹配/模板化操作,而基于区域的模板化操作则更易于阅读和使用。
解析 .example 的 A/PTR
. {
forward . 8.8.8.8
# ip-a-b-c-d.example A a.b.c.d
template IN A example {
match (^|[.])ip-(?P<a>[0-9]*)-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$
answer "{{ .Name }} 60 IN A {{ .Group.a }}.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"
fallthrough
}
# d.c.b.a.in-addr.arpa PTR ip-a-b-c-d.example
template IN PTR in-addr.arpa {
match ^(?P<d>[0-9]*)[.](?P<c>[0-9]*)[.](?P<b>[0-9]*)[.](?P<a>[0-9]*)[.]in-addr[.]arpa[.]$
answer "{{ .Name }} 60 IN PTR ip-{{ .Group.a }}-{{ .Group.b }}-{{ .Group.c }}-{{ .Group.d }}.example."
}
}
IPv4 地址由 4 个字节组成,a.b.c.d
。命名组使在 PTR 情况下反转 IP 地址的出错可能性降低。尝试使用命名组来解释正则表达式和模板的功能。
请注意,A 记录实际上是一个通配符:IP 地址的任何子域都将解析到 IP 地址。
使用模板来映射某些 PTR/A 对是一种常见模式。
对于仅模板化部分响应的混合域,需要 Fallthrough。
使用 parseInt 解析十六进制 IP 模式
. {
forward . 8.8.8.8
template IN A example {
match "^ip0a(?P<b>[a-f0-9]{2})(?P<c>[a-f0-9]{2})(?P<d>[a-f0-9]{2})[.]example[.]$"
answer "{{ .Name }} 60 IN A 10.{{ parseInt .Group.b 16 8 }}.{{ parseInt .Group.c 16 8 }}.{{ parseInt .Group.d 16 8 }}"
fallthrough
}
}
可以使用其十六进制编码以更紧凑的形式表示 IPv4 地址。例如,ip-10-123-123.example.
也可以表示为 ip0a7b7b7b.example.
解析多个 IP 模式
. {
forward . 8.8.8.8
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
}
}
命名的捕获组可用于为多个模式设置一个响应模板。
解析 .example 中 IP 模板的 A 和 MX 记录
. {
forward . 8.8.8.8
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 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
}
}
将授权名称服务器添加到响应中
. {
forward . 8.8.8.8
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 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
}
}
伪造 CNAME
此示例对针对 foogle.com
发出精确 DNS 查询的任何请求响应 CNAME 至 google.com
。如果上游名称服务器能够返回与请求类型匹配的记录,则响应还将包含 google.com
的记录。
. {
template IN ANY foogle.com {
match "^foogle\.com\.$"
answer "foogle.com 60 IN CNAME google.com"
}
forward . 8.8.8.8
}
另请参阅
错误
CoreDNS 支持 caddyfile 环境变量,采用 {$ENV_VAR}
的概念。此解析器功能会破坏 Go 模板变量 原则,例如{{$variable}}
。等效原则 {{ $variable }}
将起作用。尝试避免在此插件的上下文中使用 Go 模板变量。