模板

源代码

template 允许根据传入查询生成动态响应。

说明

通过编写一个 (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 如果 templateZONE 匹配查询名称,但没有正则表达式匹配,则继续下一个 template 实例。如果没有下一个 template,则使用下一个插件继续解析。如果列出了 [FALLTHROUGH-ZONE…] (例如 in-addr.arpaip6.arpa),那么只有针对这些区域的查询才需要进行 FallThrough。如果没有 fallthrough,当 templateZONE 匹配查询,但没有正则表达式匹配,则会返回 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
    }
}
  1. 此模板使用默认区域(. 或所有查询)。
  2. 所有查询都将得到响应(没有 fallthrough)。
  3. 响应始终为 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"
    }
}
  1. 查询 .invalid 将导致 NXDOMAIN(响应代码)。
  2. 将发送一个虚拟 SOA 记录以提供 60 秒的缓存 TTL。
  3. CH 类中查询 .invalid 也将导致 NXDOMAIN/SOA 响应。
  4. 默认正则表达式为 .*

阻止无效的搜索域完成

想象一下你在 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 模板变量。