diff --git a/content/blog/coredns-1.4.0.md b/content/blog/coredns-1.4.0.md new file mode 100644 index 0000000..eeaa7e1 --- /dev/null +++ b/content/blog/coredns-1.4.0.md @@ -0,0 +1,89 @@ ++++ +title = "CoreDNS-1.4.0 Release" +description = "CoreDNS-1.4.0 Release Notes." +tags = ["Release", "1.4.0", "Notes"] +release = "1.4.0" +date = "2019-03-03T09:04:07+00:00" +author = "coredns" ++++ + +We are pleased to announce the [release](https://github.com/coredns/coredns/releases/tag/v1.4.0) +of CoreDNS-1.4.0! Our first release after we became a graduated project in +[CNCF](https://www.cncf.io/). + +Deprecation notice for the *next* release: + + * [*auto*](/plugins/auto) will deprecate **TIMEOUT** and `no_reload` and defaults the use of + `reload`. This makes [*file*](/plugins/file) and [*auto*](/plugins/auto) to use the same syntax + for reload intervals. + * [*health*](/plugins/health) will revert back to report process level health without plugin + status. A new *ready* plugin will make sure plugins have at least completed their startup + sequence. + * The [*proxy*](/plugins/proxy) will be moved to an external repository and as such be deprecated + from the default set of plugin; use the [*forward*](/plugins/forward) as a replacement. + +The [previous](/019/01/13/coredns-1.3.1-release/) announced deprecations have been enacted. + +The (unused) gRPC watch functionally was removed from the server. + +Note we're actively working on two (probably related) bugs +([2593](https://github.com/coredns/coredns/issues/2593), +[2624](https://github.com/coredns/coredns/issues/2624)) which should hopefully result and a fix and +a new release fairly quickly. + +# Plugins + +Random updates in documentation and fixes in tests and various plugins. + + * [*kubernetes*](/plugins/kubernetes) fixes the logging now that kubernetes' client lib switched + to klog from glog. + + * [*hosts*](/plugins/hosts) fixes IPv4 addresses in IPV6 syntax. + + * [*etcd*](/plugins/etcd) adds credential support and a fix for the reply when the `host` field is + empty. + + * [*log*](/plugins/log) has been made more efficient. + + * [*forward*](/plugins/forward) drops out of order messages, this is solve occasionally FORMERRs + people saw. + +## Brought to You By + +Think we never had so many contributors for a single release. This is really nice to see. Thank you +all: + +AdamDang, +Anders Ingemann, +Andrey Meshkov, +Brian Bao, +Carl-Magnus Björkell, +Chris Aniszczyk, +Chris O'Haver, +Christophe de Carvalho, +ckcd, +Dan Kohn, +Darshan Chaudhary, +DO ANH TUAN, +Guillaume Gelin, +Guy Templeton, +JoeWrightss, +Kenjiro Nakayama, +LongKB, +Miek Gieben, +mrasu, +Nguyen Hai Truong, +Nguyen Phuong An, +Nguyen Quang Huy, +Nguyen Van Duc, +Nguyen Van Trung, +Rob Maas, +Ruslan Drozhdzh, +Sandeep Rajan, +Thomas Mangin, +tuanvcw, +Uladzimir Trehubenka, +Xiao An, +Xuanwo, +Ye Ben, +Yong Tang. diff --git a/content/plugins/auto.md b/content/plugins/auto.md index c6db258..35df5b1 100644 --- a/content/plugins/auto.md +++ b/content/plugins/auto.md @@ -4,7 +4,7 @@ description = "*auto* enables serving zone data from an RFC 1035-style master fi weight = 1 tags = [ "plugin", "auto" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.558378" +date = "2019-03-03T09:28:16.703380" +++ ## Description @@ -21,7 +21,7 @@ auto [ZONES...] { directory DIR [REGEXP ORIGIN_TEMPLATE [TIMEOUT]] reload DURATION no_reload - upstream [ADDRESS...] + upstream } ~~~ @@ -32,17 +32,17 @@ are used. used to extract the origin. **ORIGIN_TEMPLATE** will be used as a template for the origin. Strings like `{}` are replaced with the respective matches in the file name, e.g. `{1}` is the first match, `{2}` is the second. The default is: `db\.(.*) {1}` i.e. from a file with the - name `db.example.com`, the extracted origin will be `example.com`. **TIMEOUT** specifies how often - CoreDNS should scan the directory; the default is every 60 seconds. This value is in seconds. - The minimum value is 1 second. -* `reload` interval to perform reload of zone if SOA version changes. Default is one minute. + name `db.example.com`, the extracted origin will be `example.com`. + **TIMEOUT** is deprecated and will be removed in a subsequent version. + `reload` will be used, if not defined + (it specifies how often CoreDNS should scan the directory to watch for file removal and addition; + the default is every 60 seconds. This value is in seconds. The minimum value is 1 second.) +* `reload` interval to perform reloads of zones if SOA version changes and zonefiles. Default is one minute. Value of `0` means to not scan for changes and reload. eg. `30s` checks zonefile every 30 seconds and reloads zone when serial changes. * `no_reload` deprecated. Sets reload to 0. * `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs) - pointing to external names. **ADDRESS** can be an IP address, an IP:port or a string pointing to - a file that is structured as /etc/resolv.conf. If no **ADDRESS** is given, CoreDNS will resolve CNAMEs - against itself. + pointing to external names. CoreDNS will resolve CNAMEs against itself. All directives from the *file* plugin are supported. Note that *auto* will load all zones found, even though the directive might only receive queries for a specific zone. I.e: diff --git a/content/plugins/autopath.md b/content/plugins/autopath.md index 1ab4ca3..adb8814 100644 --- a/content/plugins/autopath.md +++ b/content/plugins/autopath.md @@ -4,7 +4,7 @@ description = "*autopath* allows for server-side search path completion." weight = 2 tags = [ "plugin", "autopath" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.558570" +date = "2019-03-03T09:28:16.703747" +++ ## Description diff --git a/content/plugins/bind.md b/content/plugins/bind.md index a2a78ad..fb24083 100644 --- a/content/plugins/bind.md +++ b/content/plugins/bind.md @@ -4,7 +4,7 @@ description = "*bind* overrides the host to which the server should bind." weight = 3 tags = [ "plugin", "bind" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.558685" +date = "2019-03-03T09:28:16.704043" +++ ## Description diff --git a/content/plugins/cache.md b/content/plugins/cache.md index 77f5e2c..badcd77 100644 --- a/content/plugins/cache.md +++ b/content/plugins/cache.md @@ -4,7 +4,7 @@ description = "*cache* enables a frontend cache." weight = 4 tags = [ "plugin", "cache" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.558819" +date = "2019-03-03T09:28:16.704357" +++ ## Description @@ -22,7 +22,7 @@ cache [TTL] [ZONES...] ~~~ * **TTL** max TTL in seconds. If not specified, the maximum TTL will be used, which is 3600 for - noerror responses and 1800 for denial of existence ones. + NOERROR responses and 1800 for denial of existence ones. Setting a TTL of 300: `cache 300` would cache records up to 300 seconds. * **ZONES** zones it should cache for. If empty, the zones from the configuration block are used. diff --git a/content/plugins/chaos.md b/content/plugins/chaos.md index b65218a..7e6924b 100644 --- a/content/plugins/chaos.md +++ b/content/plugins/chaos.md @@ -4,7 +4,7 @@ description = "*chaos* allows for responding to TXT queries in the CH class." weight = 5 tags = [ "plugin", "chaos" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.558924" +date = "2019-03-03T09:28:16.704594" +++ ## Description diff --git a/content/plugins/debug.md b/content/plugins/debug.md index 12e9672..ab5ee49 100644 --- a/content/plugins/debug.md +++ b/content/plugins/debug.md @@ -4,7 +4,7 @@ description = "*debug* disables the automatic recovery upon a crash so that you' weight = 6 tags = [ "plugin", "debug" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559015" +date = "2019-03-03T09:28:16.704820" +++ ## Description diff --git a/content/plugins/dnssec.md b/content/plugins/dnssec.md index a619d62..5de8254 100644 --- a/content/plugins/dnssec.md +++ b/content/plugins/dnssec.md @@ -4,7 +4,7 @@ description = "*dnssec* enable on-the-fly DNSSEC signing of served data." weight = 7 tags = [ "plugin", "dnssec" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559151" +date = "2019-03-03T09:28:16.705154" +++ ## Description diff --git a/content/plugins/dnstap.md b/content/plugins/dnstap.md index 47d5239..ca79ec3 100644 --- a/content/plugins/dnstap.md +++ b/content/plugins/dnstap.md @@ -4,7 +4,7 @@ description = "*dnstap* enable logging to dnstap." weight = 8 tags = [ "plugin", "dnstap" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559256" +date = "2019-03-03T09:28:16.705421" +++ ## Description diff --git a/content/plugins/erratic.md b/content/plugins/erratic.md index 3e0ab24..93abebb 100644 --- a/content/plugins/erratic.md +++ b/content/plugins/erratic.md @@ -4,7 +4,7 @@ description = "*erratic* a plugin useful for testing client behavior." weight = 9 tags = [ "plugin", "erratic" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559358" +date = "2019-03-03T09:28:16.705645" +++ ## Description diff --git a/content/plugins/errors.md b/content/plugins/errors.md index d31f85c..683e0a2 100644 --- a/content/plugins/errors.md +++ b/content/plugins/errors.md @@ -4,7 +4,7 @@ description = "*errors* enable error logging." weight = 10 tags = [ "plugin", "errors" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559460" +date = "2019-03-03T09:28:16.705842" +++ ## Description diff --git a/content/plugins/etcd.md b/content/plugins/etcd.md index 5aec39f..f2eb444 100644 --- a/content/plugins/etcd.md +++ b/content/plugins/etcd.md @@ -4,7 +4,7 @@ description = "*etcd* enables reading zone data from an etcd version 3 instance. weight = 11 tags = [ "plugin", "etcd" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559615" +date = "2019-03-03T09:28:16.706177" +++ ## Description @@ -31,23 +31,22 @@ If you want to `round robin` A and AAAA responses look at the `loadbalance` plug ~~~ etcd [ZONES...] { - stubzones fallthrough [ZONES...] path PATH endpoint ENDPOINT... + credentials USERNAME PASSWORD upstream [ADDRESS...] tls CERT KEY CACERT } ~~~ -* `stubzones` enables the stub zones feature. The stubzone is *only* done in the etcd tree located - under the *first* zone specified. * `fallthrough` If zone matches but no record can be generated, pass request to the next plugin. If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only queries for those zones will be subject to fallthrough. * **PATH** the path inside etcd. Defaults to "/skydns". * **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2379". +* `credentials` is used to set the **USERNAME** and **PASSWORD** for accessing the etcd cluster. * `upstream` upstream resolvers to be used resolve external names found in etcd (think CNAMEs) pointing to external names. If you want CoreDNS to act as a proxy for clients, you'll need to add the proxy plugin. If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself. @@ -65,7 +64,7 @@ etcd [ZONES...] { ## Special Behaviour CoreDNS etcd plugin leverages directory structure to look for related entries. For example an entry `/skydns/test/skydns/mx` would have entries like `/skydns/test/skydns/mx/a`, `/skydns/test/skydns/mx/b` and so on. Similarly a directory `/skydns/test/skydns/mx1` will have all `mx1` entries. -With etcd3, support for [hierarchial keys are dropped](https://coreos.com/etcd/docs/latest/learning/api.html). This means there are no directories but only flat keys with prefixes in etcd3. To accommodate lookups, etcdv3 plugin now does a lookup on prefix `/skydns/test/skydns/mx/` to search for entries like `/skydns/test/skydns/mx/a` etc, and if there is nothing found on `/skydns/test/skydns/mx/`, it looks for `/skydns/test/skydns/mx` to find entries like `/skydns/test/skydns/mx1`. +With etcd3, support for [hierarchical keys are dropped](https://coreos.com/etcd/docs/latest/learning/api.html). This means there are no directories but only flat keys with prefixes in etcd3. To accommodate lookups, etcdv3 plugin now does a lookup on prefix `/skydns/test/skydns/mx/` to search for entries like `/skydns/test/skydns/mx/a` etc, and if there is nothing found on `/skydns/test/skydns/mx/`, it looks for `/skydns/test/skydns/mx` to find entries like `/skydns/test/skydns/mx1`. This causes two lookups from CoreDNS to etcdv3 in certain cases. @@ -82,10 +81,9 @@ This is the default SkyDNS setup, with everything specified in full: ~~~ corefile . { etcd skydns.local { - stubzones path /skydns endpoint http://localhost:2379 - upstream 8.8.8.8:53 8.8.4.4:53 + upstream } prometheus cache 160 skydns.local @@ -101,7 +99,7 @@ when resolving external pointing CNAMEs. . { etcd skydns.local { path /skydns - upstream /etc/resolv.conf + upstream } cache 160 skydns.local proxy . /etc/resolv.conf @@ -118,7 +116,7 @@ etcd skydns.local { Before getting started with these examples, please setup `etcdctl` (with `etcdv3` API) as explained [here](https://coreos.com/etcd/docs/latest/dev-guide/interacting_v3.html). This will help you to put sample keys in your etcd server. -If you prefer, you can use `curl` to populate the `etcd` server, but with `curl` the endpoint URL depends on the version of `etcd`. For instance, `etcd v3.2` or before uses only [CLIENT-URL]/v3alpha/* while `etcd v3.5` or later uses [CLIENT-URL]/v3/* . Also, Key and Value must be base64 encoded in the JSON payload. With, `etcdctl` these details are automatically taken care off. You can check [this document](https://github.com/coreos/etcd/blob/master/Documentation/dev-guide/api_grpc_gateway.md#notes) for details. +If you prefer, you can use `curl` to populate the `etcd` server, but with `curl` the endpoint URL depends on the version of `etcd`. For instance, `etcd v3.2` or before uses only [CLIENT-URL]/v3alpha/* while `etcd v3.5` or later uses [CLIENT-URL]/v3/* . Also, Key and Value must be base64 encoded in the JSON payload. With `etcdctl` these details are automatically taken care off. You can check [this document](https://github.com/coreos/etcd/blob/master/Documentation/dev-guide/api_grpc_gateway.md#notes) for details. ### Reverse zones @@ -128,7 +126,6 @@ need to add the zone `0.0.10.in-addr.arpa` to the list of zones. Showing a snipp ~~~ etcd skydns.local 10.0.0.0/24 { - stubzones ... ~~~ @@ -148,7 +145,7 @@ reverse.skydns.local. ### Zone name as A record -The zone name itself can be used A record. This behavior can be achieved by writing special entries to the ETCD path of your zone. If your zone is named `skydns.local` for example, you can create an `A` record for this zone as follows: +The zone name itself can be used as A record. This behavior can be achieved by writing special entries to the ETCD path of your zone. If your zone is named `skydns.local` for example, you can create an `A` record for this zone as follows: ~~~ % etcdctl put /skydns/local/skydns/ '{"host":"1.1.1.1","ttl":60}' @@ -163,8 +160,8 @@ If you query the zone name itself, you will receive the created `A` record: If you would like to use DNS RR for the zone name, you can set the following: ~~~ -% etcdctl put /skydns/local/skydns/x1 '{"host":"1.1.1.1","ttl":"60"}' -% etcdctl put /skydns/local/skydns/x2 '{"host":"1.1.1.2","ttl":"60"}' +% etcdctl put /skydns/local/skydns/x1 '{"host":"1.1.1.1","ttl":60}' +% etcdctl put /skydns/local/skydns/x2 '{"host":"1.1.1.2","ttl":60}' ~~~ If you query the zone name now, you will get the following response: @@ -179,8 +176,8 @@ If you query the zone name now, you will get the following response: If you would like to use `AAAA` records for the zone name too, you can set the following: ~~~ -% etcdctl put /skydns/local/skydns/x3 '{"host":"2003::8:1","ttl":"60"}' -% etcdctl put /skydns/local/skydns/x4 '{"host":"2003::8:2","ttl":"60"}' +% etcdctl put /skydns/local/skydns/x3 '{"host":"2003::8:1","ttl":60}' +% etcdctl put /skydns/local/skydns/x4 '{"host":"2003::8:2","ttl":60}' ~~~ If you query the zone name for `AAAA` now, you will get the following response: diff --git a/content/plugins/federation.md b/content/plugins/federation.md index 4145a52..a86db98 100644 --- a/content/plugins/federation.md +++ b/content/plugins/federation.md @@ -4,7 +4,7 @@ description = "*federation* enables federated queries to be resolved via the kub weight = 12 tags = [ "plugin", "federation" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559721" +date = "2019-03-03T09:28:16.706382" +++ ## Description @@ -20,16 +20,14 @@ Enabling *federation* without also having *kubernetes* is a noop. ~~~ federation [ZONES...] { NAME DOMAIN - upstream [ADDRESS...] + upstream } ~~~ * Each **NAME** and **DOMAIN** defines federation membership. One entry for each. A duplicate **NAME** will silently overwrite any previous value. -* `upstream` [**ADDRESS**...] defines the upstream resolvers used for resolving the `CNAME` target - produced by this plugin. If no **ADDRESS** is given, CoreDNS - will resolve External Services against itself. **ADDRESS** can be an IP, an IP:port, or a path - to a file structured like resolv.conf. +* `upstream` [**ADDRESS**...] resolve the `CNAME` target produced by this plugin. CoreDNS + will resolve External Services against itself. ## Examples diff --git a/content/plugins/file.md b/content/plugins/file.md index 15c3906..a8be883 100644 --- a/content/plugins/file.md +++ b/content/plugins/file.md @@ -4,7 +4,7 @@ description = "*file* enables serving zone data from an RFC 1035-style master fi weight = 13 tags = [ "plugin", "file" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559816" +date = "2019-03-03T09:28:16.706550" +++ ## Description @@ -32,7 +32,7 @@ file DBFILE [ZONES... ] { transfer to ADDRESS... reload DURATION no_reload - upstream [ADDRESS...] + upstream } ~~~ @@ -44,11 +44,9 @@ file DBFILE [ZONES... ] { Value of `0` means to not scan for changes and reload. For example, `30s` checks the zonefile every 30 seconds and reloads the zone when serial changes. * `no_reload` deprecated. Sets reload to 0. -* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs) - pointing to external names. This is only really useful when CoreDNS is configured as a proxy; for - normal authoritative serving you don't need *or* want to use this. **ADDRESS** can be an IP - address, an IP:port or a string pointing to a file that is structured as /etc/resolv.conf. - If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself. +* `upstream` resolve external names found (think CNAMEs) pointing to external names. This is only + really useful when CoreDNS is configured as a proxy; for normal authoritative serving you don't + need *or* want to use this. CoreDNS will resolve CNAMEs against itself. ## Examples diff --git a/content/plugins/forward.md b/content/plugins/forward.md index 07b161d..64b1155 100644 --- a/content/plugins/forward.md +++ b/content/plugins/forward.md @@ -4,7 +4,7 @@ description = "*forward* facilitates proxying DNS messages to upstream resolvers weight = 14 tags = [ "plugin", "forward" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.559963" +date = "2019-03-03T09:28:16.706791" +++ ## Description @@ -170,7 +170,7 @@ Or with multiple upstreams from the same provider ~~~ corefile . { forward . tls://1.1.1.1 tls://1.0.0.1 { - tls_servername loudflare-dns.com + tls_servername cloudflare-dns.com health_check 5s } cache 30 diff --git a/content/plugins/health.md b/content/plugins/health.md index b8a16f4..f5a525c 100644 --- a/content/plugins/health.md +++ b/content/plugins/health.md @@ -4,7 +4,7 @@ description = "*health* enables a health check endpoint." weight = 15 tags = [ "plugin", "health" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.560114" +date = "2019-03-03T09:28:16.707002" +++ ## Description diff --git a/content/plugins/hosts.md b/content/plugins/hosts.md index 4b6f0ce..7b3d65b 100644 --- a/content/plugins/hosts.md +++ b/content/plugins/hosts.md @@ -4,7 +4,7 @@ description = "*hosts* enables serving zone data from a `/etc/hosts` style file. weight = 16 tags = [ "plugin", "hosts" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.560263" +date = "2019-03-03T09:28:16.707194" +++ ## Description @@ -14,6 +14,9 @@ file that exists on disk. It checks the file for changes and updates the zones a plugin only supports A, AAAA, and PTR records. The hosts plugin can be used with readily available hosts files that block access to advertising servers. +The plugin reloads the content of the hosts file every 5 seconds. Upon reload, CoreDNS will use the new definitions. +Should the file be deleted, any inlined content will continue to be served. When the file is restored, it will then again be used. + This plugin can only be used once per Server Block. ## The hosts file @@ -41,6 +44,9 @@ PTR records for reverse lookups are generated automatically by CoreDNS (based on ~~~ hosts [FILE [ZONES...]] { [INLINE] + ttl SECONDS + no_reverse + reload DURATION fallthrough [ZONES...] } ~~~ @@ -52,7 +58,10 @@ hosts [FILE [ZONES...]] { are used. * **INLINE** the hosts file contents inlined in Corefile. If there are any lines before fallthrough then all of them will be treated as the additional content for hosts file. The specified hosts - file path will still be read but entries will be overrided. + file path will still be read but entries will be overridden. +* `ttl` change the DNS TTL of the records generated (forward and reverse). The default is 3600 seconds (1 hour). +* `reload` change the period between each hostsfile reload. A time of zero seconds disable the feature. Examples of valid durations: "300ms", "1.5h" or "2h45m" are valid duration with units "ns" (nanosecond), "us" (or "µs" for microsecond), "ms" (millisecond), "s" (second), "m" (minute), "h" (hour). +* `no_reverse` disable the automatic generation of the `in-addr.arpa` or `ip6.arpa` entries for the hosts * `fallthrough` If zone matches and no record can be generated, pass request to the next plugin. If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only diff --git a/content/plugins/import.md b/content/plugins/import.md index 3ba836a..3c9b40d 100644 --- a/content/plugins/import.md +++ b/content/plugins/import.md @@ -4,12 +4,12 @@ description = "*import* include files or reference snippets from a Corefile." weight = 17 tags = [ "plugin", "import" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.560394" +date = "2019-03-03T09:28:16.707360" +++ ## Description -The *import* plugin can be used to incude files into the main configuration. Another use it to +The *import* plugin can be used to include files into the main configuration. Another use it to reference predefined snippets. Both can help to avoid some duplication. This is a unique directive in that *import* can appear outside of a server block. In other words, it diff --git a/content/plugins/k8s_external.md b/content/plugins/k8s_external.md index 1b2c573..aa349ad 100644 --- a/content/plugins/k8s_external.md +++ b/content/plugins/k8s_external.md @@ -4,7 +4,7 @@ description = "*k8s_external* resolve load balancer and external IPs from outsid weight = 18 tags = [ "plugin", "k8s_external" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.560508" +date = "2019-03-03T09:28:16.707533" +++ ## Description diff --git a/content/plugins/kubernetes.md b/content/plugins/kubernetes.md index 5fe3b57..6327173 100644 --- a/content/plugins/kubernetes.md +++ b/content/plugins/kubernetes.md @@ -4,7 +4,7 @@ description = "*kubernetes* enables the reading zone data from a Kubernetes clus weight = 19 tags = [ "plugin", "kubernetes" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.560683" +date = "2019-03-03T09:28:16.707791" +++ ## Description @@ -16,7 +16,7 @@ CoreDNS running the kubernetes plugin can be used as a replacement for kube-dns cluster. See the [deployment](https://github.com/coredns/deployment) repository for details on [how to deploy CoreDNS in Kubernetes](https://github.com/coredns/deployment/tree/master/kubernetes). -[stubDomains and upstreamNameservers](http://blog.kubernetes.io/2017/04/configuring-private-dns-zones-upstream-nameservers-kubernetes.html) +[stubDomains and upstreamNameservers](https://kubernetes.io/blog/2017/04/configuring-private-dns-zones-upstream-nameservers-kubernetes/) are implemented via the *proxy* plugin and kubernetes *upstream*. See example below. This plugin can only be used once per Server Block. @@ -35,7 +35,7 @@ all the zones the plugin should be authoritative for. ``` kubernetes [ZONES...] { resyncperiod DURATION - endpoint URL [URL...] + endpoint URL tls CERT KEY CACERT kubeconfig KUBECONFIG CONTEXT namespaces NAMESPACE... @@ -54,10 +54,6 @@ kubernetes [ZONES...] { * `resyncperiod` specifies the Kubernetes data API **DURATION** period. * `endpoint` specifies the **URL** for a remote k8s API endpoint. If omitted, it will connect to k8s in-cluster using the cluster service account. - Multiple k8s API endpoints could be specified: - `endpoint http://k8s-endpoint1:8080 http://k8s-endpoint2:8080`. - CoreDNS will automatically perform a healthcheck and proxy to the healthy k8s API endpoint. - Note that only http is supported when more than one k8s API endpoints are specified at the moment. * `tls` **CERT** **KEY** **CACERT** are the TLS cert, key and the CA cert file names for remote k8s connection. This option is ignored if connecting in-cluster (i.e. endpoint is not specified). * `kubeconfig` **KUBECONFIG** **CONTEXT** authenticates the connection to a remote k8s cluster using a kubeconfig file. It supports TLS, username and password, or token-based authentication. This option is ignored if connecting in-cluster (i.e., the endpoint is not specified). @@ -65,7 +61,7 @@ kubernetes [ZONES...] { If this option is omitted all namespaces are exposed * `labels` **EXPRESSION** only exposes the records for Kubernetes objects that match this label selector. The label selector syntax is described in the - [Kubernetes User Guide - Labels](http://kubernetes.io/docs/user-guide/labels/). An example that + [Kubernetes User Guide - Labels](https://kubernetes.io/docs/user-guide/labels/). An example that only exposes objects labeled as "application=nginx" in the "staging" or "qa" environments, would use: `labels environment in (staging, qa),application=nginx`. * `pods` **POD-MODE** sets the mode for handling IP-based pod A records, e.g. @@ -95,12 +91,12 @@ kubernetes [ZONES...] { that point to external hosts (aka External Services, aka CNAMEs). If no **ADDRESS** is given, CoreDNS will resolve External Services against itself. **ADDRESS** can be an IP, an IP:port, or a path to a file structured like resolv.conf. -* `ttl` allows you to set a custom TTL for responses. The default (and minimum allowed) is - 0 seconds, while the maximum is capped at 3600 seconds. Setting TTL to 0 will prevent records from being cached. +* `ttl` allows you to set a custom TTL for responses. The default is 5 seconds. The minimum TTL allowed is + 0 seconds, and the maximum is capped at 3600 seconds. Setting TTL to 0 will prevent records from being cached. * `noendpoints` will turn off the serving of endpoint records by disabling the watch on endpoints. All endpoint queries and headless service queries will result in an NXDOMAIN. * `transfer` enables zone transfers. It may be specified multiples times. `To` signals the direction - (only `to` is allow). **ADDRESS** must be denoted in CIDR notation (127.0.0.1/32 etc.) or just as + (only `to` is allowed). **ADDRESS** must be denoted in CIDR notation (127.0.0.1/32 etc.) or just as plain addresses. The special wildcard `*` means: the entire internet. Sending DNS notifies is not supported. [Deprecated](https://github.com/kubernetes/dns/blob/master/docs/specification.md#26---deprecated-records) pod records in the subdomain `pod.cluster.local` are not transferred. @@ -110,7 +106,7 @@ kubernetes [ZONES...] { the query. If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only queries for those zones will be subject to fallthrough. -* `ignore empty_service` return NXDOMAIN for services without any ready endpoint addresses (e.g., ready pods). +* `ignore empty_service` returns NXDOMAIN for services without any ready endpoint addresses (e.g., ready pods). This allows the querying pod to continue searching for the service in the search path. The search path could, for example, include another Kubernetes cluster. @@ -119,11 +115,6 @@ kubernetes [ZONES...] { This plugin implements dynamic health checking. Currently this is limited to reporting healthy when the API has synced. -## Watch - -This plugin implements watch. A client that connects to CoreDNS using `coredns/client` can be notified -of changes to A, AAAA, and SRV records for Kubernetes services and endpoints. - ## Examples Handle all queries in the `cluster.local` zone. Connect to Kubernetes in-cluster. Also handle all @@ -220,7 +211,7 @@ or the word "any"), then that label will match all values. The labels that acce * _namespace_ in an `A` record request: service._namespace_.svc.zone, e.g., `nginx.*.svc.cluster.local` * _port and/or protocol_ in an `SRV` request: __port_.__protocol_.service.namespace.svc.zone., e.g., `_http.*.service.ns.svc.cluster.local` - * multiple wild cards are allowed in a single query, e.g., `A` Request `*.*.svc.zone.` or `SRV` request `*.*.*.*.svc.zone.` + * multiple wildcards are allowed in a single query, e.g., `A` Request `*.*.svc.zone.` or `SRV` request `*.*.*.*.svc.zone.` For example, wildcards can be used to resolve all Endpoints for a Service as `A` records. e.g.: `*.service.ns.svc.myzone.local` will return the Endpoint IPs in the Service `service` in namespace `default`: ``` diff --git a/content/plugins/loadbalance.md b/content/plugins/loadbalance.md index 26439ef..f6335b0 100644 --- a/content/plugins/loadbalance.md +++ b/content/plugins/loadbalance.md @@ -4,7 +4,7 @@ description = "*loadbalance* randomize the order of A, AAAA and MX records." weight = 20 tags = [ "plugin", "loadbalance" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.560790" +date = "2019-03-03T09:28:16.707948" +++ ## Description diff --git a/content/plugins/log.md b/content/plugins/log.md index 7492286..7c46fd5 100644 --- a/content/plugins/log.md +++ b/content/plugins/log.md @@ -4,7 +4,7 @@ description = "*log* enables query logging to standard output." weight = 21 tags = [ "plugin", "log" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.560935" +date = "2019-03-03T09:28:16.708171" +++ ## Description diff --git a/content/plugins/loop.md b/content/plugins/loop.md index 5201105..5c3f1d1 100644 --- a/content/plugins/loop.md +++ b/content/plugins/loop.md @@ -4,7 +4,7 @@ description = "*loop* detect simple forwarding loops and halt the server." weight = 22 tags = [ "plugin", "loop" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.561075" +date = "2019-03-03T09:28:16.708368" +++ ## Description diff --git a/content/plugins/metadata.md b/content/plugins/metadata.md index 10f166c..8948dc1 100644 --- a/content/plugins/metadata.md +++ b/content/plugins/metadata.md @@ -4,7 +4,7 @@ description = "*metadata* enable a meta data collector." weight = 23 tags = [ "plugin", "metadata" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.561181" +date = "2019-03-03T09:28:16.708513" +++ ## Description diff --git a/content/plugins/metrics.md b/content/plugins/metrics.md index cb79d75..a51026c 100644 --- a/content/plugins/metrics.md +++ b/content/plugins/metrics.md @@ -4,7 +4,7 @@ description = "*prometheus* enables [Prometheus](https://prometheus.io/) metrics weight = 24 tags = [ "plugin", "metrics" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.561294" +date = "2019-03-03T09:28:16.708659" +++ ## Description @@ -50,12 +50,12 @@ prometheus [ADDRESS] For each zone that you want to see metrics for. -It optionally takes an address to which the metrics are exported; the default -is `localhost:9153`. The metrics path is fixed to `/metrics`. +It optionally takes a bind address to which the metrics are exported; the default +listens on `localhost:9153`. The metrics path is fixed to `/metrics`. ## Examples -Use an alternative address: +Use an alternative listening address: ~~~ corefile . { diff --git a/content/plugins/nsid.md b/content/plugins/nsid.md index 73111c2..3ea3ed0 100644 --- a/content/plugins/nsid.md +++ b/content/plugins/nsid.md @@ -4,7 +4,7 @@ description = "*nsid* adds an identifier of this server to each reply." weight = 25 tags = [ "plugin", "nsid" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.561416" +date = "2019-03-03T09:28:16.708815" +++ ## Description diff --git a/content/plugins/pprof.md b/content/plugins/pprof.md index 37bdd59..8dde5c9 100644 --- a/content/plugins/pprof.md +++ b/content/plugins/pprof.md @@ -4,7 +4,7 @@ description = "*pprof* publishes runtime profiling data at endpoints under `/deb weight = 26 tags = [ "plugin", "pprof" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.561533" +date = "2019-03-03T09:28:16.708987" +++ ## Description diff --git a/content/plugins/proxy.md b/content/plugins/proxy.md index 7980556..2d2584e 100644 --- a/content/plugins/proxy.md +++ b/content/plugins/proxy.md @@ -4,7 +4,7 @@ description = "*proxy* facilitates both a basic reverse proxy and a robust load weight = 27 tags = [ "plugin", "proxy" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.561684" +date = "2019-03-03T09:28:16.709180" +++ ## Description @@ -63,6 +63,7 @@ proxy FROM TO... { ## Policies There are four load-balancing policies available: + * `random` (default) - Randomly select a backend * `least_conn` - Select the backend with the fewest active connections * `round_robin` - Select the backend in round-robin fashion @@ -71,7 +72,7 @@ There are four load-balancing policies available: All polices implement randomly spraying packets to backend hosts when *no healthy* hosts are -available. This is to preeempt the case where the healthchecking (as a mechanism) fails. +available. This is to preempt the case where the healthchecking (as a mechanism) fails. ## Upstream Protocols diff --git a/content/plugins/reload.md b/content/plugins/reload.md index a220fd7..232bb2a 100644 --- a/content/plugins/reload.md +++ b/content/plugins/reload.md @@ -4,7 +4,7 @@ description = "*reload* allows automatic reload of a changed Corefile." weight = 28 tags = [ "plugin", "reload" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.561809" +date = "2019-03-03T09:28:16.709340" +++ ## Description @@ -91,3 +91,10 @@ After the aborted attempt to reload we are left with the old processes running, closed in step 1; so the health endpoint is broken. The same can hopen in the prometheus metrics plugin. In general be careful with assigning new port and expecting reload to work fully. + +Also any `import` statement is not discovered by this plugin. This means if any of these imported files +changes the *reload* plugin is ignorant of that fact. + +## Also See + +See coredns-import(7) and corefile(5). diff --git a/content/plugins/rewrite.md b/content/plugins/rewrite.md index d551809..9a37d8a 100644 --- a/content/plugins/rewrite.md +++ b/content/plugins/rewrite.md @@ -4,7 +4,7 @@ description = "*rewrite* performs internal message rewriting." weight = 29 tags = [ "plugin", "rewrite" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.562006" +date = "2019-03-03T09:28:16.709611" +++ ## Description @@ -178,7 +178,7 @@ rewrite [continue|stop] { Note that the above syntax is strict. For response rewrites only `name` rules are allowed to match the question section, and only by match type `regex`. The answer rewrite must be after the name, as ordered in the -syntax example. There must only be two lines (a `name` follwed by an +syntax example. There must only be two lines (a `name` followed by an `answer`) in the brackets, additional rules are not supported. An alternate syntax for the rewrite of DNS request and response is as diff --git a/content/plugins/root.md b/content/plugins/root.md index da8cf54..2db312f 100644 --- a/content/plugins/root.md +++ b/content/plugins/root.md @@ -4,7 +4,7 @@ description = "*root* simply specifies the root of where to find (zone) files." weight = 30 tags = [ "plugin", "root" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.562106" +date = "2019-03-03T09:28:16.709748" +++ ## Description diff --git a/content/plugins/route53.md b/content/plugins/route53.md index 40a983f..42346de 100644 --- a/content/plugins/route53.md +++ b/content/plugins/route53.md @@ -4,13 +4,14 @@ description = "*route53* enables serving zone data from AWS route53." weight = 31 tags = [ "plugin", "route53" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.562219" +date = "2019-03-03T09:28:16.709898" +++ ## Description -The route53 plugin is useful for serving zones from resource record sets in AWS route53. This plugin -supports all Amazon Route 53 records (https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html). +The route53 plugin is useful for serving zones from resource record +sets in AWS route53. This plugin supports all Amazon Route 53 records +([https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html)). The route53 plugin can be used when coredns is deployed on AWS or elsewhere. ## Syntax @@ -18,33 +19,40 @@ The route53 plugin can be used when coredns is deployed on AWS or elsewhere. ~~~ txt route53 [ZONE:HOSTED_ZONE_ID...] { [aws_access_key AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY] - upstream [ADDRESS...] + upstream credentials PROFILE [FILENAME] fallthrough [ZONES...] } ~~~ -* **ZONE** the name of the domain to be accessed. When there are multiple zones with overlapping domains - (private vs. public hosted zone), CoreDNS does the lookup in the given order here. Therefore, for a - non-existing resource record, SOA response will be from the rightmost zone. -* **HOSTED_ZONE_ID** the ID of the hosted zone that contains the resource record sets to be accessed. -* **AWS_ACCESS_KEY_ID** and **AWS_SECRET_ACCESS_KEY** the AWS access key ID and secret access key - to be used when query AWS (optional). If they are not provided, then coredns tries to access - AWS credentials the same way as AWS CLI, e.g., environmental variables, AWS credentials file, - instance profile credentials, etc. -* `upstream` [**ADDRESS**...] specifies upstream resolver(s) used for resolving services that point - to external hosts (eg. used to resolve CNAMEs). If no **ADDRESS** is given, CoreDNS will resolve - against itself. **ADDRESS** can be an IP, an IP:port or a path to a file structured like - resolv.conf. -* `credentials` used for reading the credential file and setting the profile name for a given zone. -* **PROFILE** AWS account profile name. Defaults to `default`. -* **FILENAME** AWS credentials filename. Defaults to `~/.aws/credentials` - are used. -* `fallthrough` If zone matches and no record can be generated, pass request to the next plugin. - If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin - is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only - queries for those zones will be subject to fallthrough. -* **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block +* **ZONE** the name of the domain to be accessed. When there are multiple zones with overlapping + domains (private vs. public hosted zone), CoreDNS does the lookup in the given order here. + Therefore, for a non-existing resource record, SOA response will be from the rightmost zone. + +* **HOSTED*ZONE*ID** the ID of the hosted zone that contains the resource record sets to be + accessed. + +* **AWS*ACCESS*KEY_ID** and **AWS*SECRET*ACCESS_KEY** the AWS access key ID and secret access key + to be used when query AWS (optional). If they are not provided, then coredns tries to access + AWS credentials the same way as AWS CLI, e.g., environmental variables, AWS credentials file, + instance profile credentials, etc. + +* `upstream`is used for resolving services that point to external hosts (eg. used to resolve + CNAMEs). CoreDNS will resolve against itself. + +* `credentials` is used for reading the credential file and setting the profile name for a given + zone. + +* **PROFILE** AWS account profile name. Defaults to `default`. + +* **FILENAME** AWS credentials filename. Defaults to `~/.aws/credentials` are used. + +* `fallthrough` If zone matches and no record can be generated, pass request to the next plugin. + If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin is + authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then + only queries for those zones will be subject to fallthrough. + +* **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block ## Examples diff --git a/content/plugins/secondary.md b/content/plugins/secondary.md index bc50d75..4d5b248 100644 --- a/content/plugins/secondary.md +++ b/content/plugins/secondary.md @@ -4,7 +4,7 @@ description = "*secondary* enables serving a zone retrieved from a primary serve weight = 32 tags = [ "plugin", "secondary" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.562329" +date = "2019-03-03T09:28:16.710035" +++ ## Description @@ -26,18 +26,16 @@ A working syntax would be: secondary [zones...] { transfer from ADDRESS transfer to ADDRESS - upstream [ADDRESS...] + upstream } ~~~ * `transfer from` specifies from which address to fetch the zone. It can be specified multiple times; if one does not work, another will be tried. * `transfer to` can be enabled to allow this secondary zone to be transferred again. -* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs) - pointing to external names. This is only really useful when CoreDNS is configured as a proxy, for - normal authoritative serving you don't need *or* want to use this. **ADDRESS** can be an IP - address, and IP:port or a string pointing to a file that is structured as /etc/resolv.conf. - If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself. +* `upstream` resolve external names found (think CNAMEs) pointing to external names. This is only + really useful when CoreDNS is configured as a proxy; for normal authoritative serving you don't + need *or* want to use this. CoreDNS will resolve CNAMEs against itself. When a zone is due to be refreshed (Refresh timer fires) a random jitter of 5 seconds is applied, before fetching. In the case of retry this will be 2 seconds. If there are any errors diff --git a/content/plugins/template.md b/content/plugins/template.md index abb7ca0..d4957ec 100644 --- a/content/plugins/template.md +++ b/content/plugins/template.md @@ -4,7 +4,7 @@ description = "*template* allows for dynamic responses based on the incoming que weight = 33 tags = [ "plugin", "template" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.562516" +date = "2019-03-03T09:28:16.710275" +++ ## Description @@ -15,14 +15,13 @@ The *template* plugin allows you to dynamically respond to queries by just writi ~~~ template CLASS TYPE [ZONE...] { - [match REGEX...] - [answer RR] - [additional RR] - [authority RR] - [...] - [rcode CODE] - [upstream [ADDRESS...]] - [fallthrough [ZONE...]] + match REGEX... + answer RR + additional RR + authority RR + rcode CODE + upstream + fallthrough [ZONE...] } ~~~ @@ -33,9 +32,7 @@ template CLASS TYPE [ZONE...] { * `answer|additional|authority` **RR** A [RFC 1035](https://tools.ietf.org/html/rfc1035#section-5) style resource record fragment built 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`. -* `upstream` [**ADDRESS**...] defines the upstream resolvers used for resolving CNAME. - If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself. **ADDRESS** - can be an IP, an IP:port, or a path to a file structured like resolv.conf. +* `upstream` defines the upstream resolvers used for resolving CNAMEs. CoreDNS will resolve CNAMEs against itself. * `fallthrough` Continue with the next plugin if the zone matched but no regex matched. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only queries for those zones will be subject to fallthrough. @@ -154,19 +151,19 @@ The regex-based version can do more complex matching/templating while zone-based . { proxy . 8.8.8.8 - # ip-a-b-c-d.example.com A a.b.c.d + # ip-a-b-c-d.example A a.b.c.d template IN A example { - match (^|[.])ip-10-(?P[0-9]*)-(?P[0-9]*)-(?P[0-9]*)[.]example[.]$ - answer "{{ .Name }} 60 IN A 10.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}" + match (^|[.])ip-(?P[0-9]*)-(?P[0-9]*)-(?P[0-9]*)-(?P[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 10.in-addr.arpa. { - match ^(?P[0-9]*)[.](?P[0-9]*)[.](?P[0-9]*)[.]10[.]in-addr[.]arpa[.]$ - answer "{{ .Name }} 60 IN PTR ip-10-{{ .Group.b }}-{{ .Group.c }}-{{ .Group.d }}.example.com." + template IN PTR in-addr.arpa { + match ^(?P[0-9]*)[.](?P[0-9]*)[.](?P[0-9]*)[.](?P[0-9]*)[.]in-addr[.]arpa[.]$ + answer "{{ .Name }} 60 IN PTR ip-{{ .Group.a }}-{{ .Group.b }}-{{ .Group.c }}-{{ .Group.d }}.example." } } ~~~ diff --git a/content/plugins/tls.md b/content/plugins/tls.md index ef40ff4..88119c8 100644 --- a/content/plugins/tls.md +++ b/content/plugins/tls.md @@ -4,7 +4,7 @@ description = "*tls* allows you to configure the server certificates for the TLS weight = 34 tags = [ "plugin", "tls" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.562647" +date = "2019-03-03T09:28:16.710448" +++ ## Description diff --git a/content/plugins/trace.md b/content/plugins/trace.md index c399857..f4b51d3 100644 --- a/content/plugins/trace.md +++ b/content/plugins/trace.md @@ -4,7 +4,7 @@ description = "*trace* enables OpenTracing-based tracing of DNS requests as they weight = 35 tags = [ "plugin", "trace" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.562795" +date = "2019-03-03T09:28:16.710595" +++ ## Description diff --git a/content/plugins/whoami.md b/content/plugins/whoami.md index 52b5ecf..2ce9a8a 100644 --- a/content/plugins/whoami.md +++ b/content/plugins/whoami.md @@ -4,7 +4,7 @@ description = "*whoami* returns your resolver's local IP address, port and trans weight = 36 tags = [ "plugin", "whoami" ] categories = [ "plugin" ] -date = "2019-01-13T14:59:21.562928" +date = "2019-03-03T09:28:16.710724" +++ ## Description diff --git a/data/coredns.toml b/data/coredns.toml index a7c5019..5a666f6 100644 --- a/data/coredns.toml +++ b/data/coredns.toml @@ -1,2 +1,2 @@ [release] - version = "1.3.1" + version = "1.4.0"