As mentioned before in this manual, plugins are the thing that make CoreDNS tick. We’ve seen a bunch of configuration in the previous section, but how can you write your own plugin?
The canonical example plugin is the example plugin. Its github repository shows the most minimal code (with tests!) that is needed to create a plugin.
setup_test.go, which implement the parsing of configuration from the Corefile. The (usually named)
setupfunction is called whenever the Corefile parser see the plugin’s name; in this case, “example”.
<plugin_name>.go), which contains logic for handling the query, and
example_test.go, which has basic units tests to check if the plugin works.
README.mdthat documents in a Unix manual style how this plugin can be configured.
- A LICENSE file. For inclusion in CoreDNS, this needs to have an APL like license.
The code also has extensive comments; feel free to fork it and base your plugin off of it.
When CoreDNS wants to use a plugin it calls the method
ServeDNS has three parameters:
dns.ResponseWriterthat is basically the client’s connection;
*dns.Msgthat is the request from the client.
ServeDNS returns two values: a (response) code and an error. The error is logged when the
errors is used in this server.
The code tells CoreDNS if a reply has been written by the plugin chain or not. In the latter case, CoreDNS will take care of that. For the code’s values, we reuse the DNS return codes (rcodes) from the dns package.
- SERVFAIL (dns.RcodeServerFailure)
- REFUSED (dns.RcodeRefused)
- FORMERR (dns.RcodeFormatError)
- NOTIMP (dns.RcodeNotImplemented)
As special and will then assume nothing has been written to the client. In all other cases, it assumes something has been written to the client (by the plugin).
See this post on how to compile CoreDNS with your plugin.
Use the log package to add logging to your plugin. You initialize it with:
Now you can
log.Infof("...") to print something to standard output prefixed with level
[INFO] plugin/example. Level can be:
In general, logging should be left to the higher layers when returning an error. However, if there is a reason to consume the error but still notify the user, then logging in the plugin can be acceptable.
When exporting metrics, the Namespace should be
plugin.Namespace (=“coredns”), and the
Subsystem should be the name of the plugin. The README.md for the plugin should then also contain
a Metrics section detailing the metrics. If the plugin supports readiness
reporting, it should also have a Ready section detailing it.
Each plugin should have a README.md explaining what the plugin does and how it is configured. The file should have the following layout:
- Title: use the plugin’s name
- Subsection titled: “Named”
<plugin name> - <one line description>., i.e. NAME DASH DESCRIPTION DOT.
- Subsection titled: “Description” with a longer description and all the options the plugin supports.
- Subsection titled: “Syntax” detailing syntax and supported directives.
- Subsection titled: “Examples”.
- Optional Subsection titled: “See Also”, that references external documentation, like IETF RFCs.
- Optional Subsection titled: “Bugs” that lists things that do not work yet.
More sections are, of course, possible.
We use the Unix manual page style:
- The name of the plugin in the running text should be italic:
- All CAPITAL user supplied arguments in the running text reference use strong text:
- Optional text is in block quotes:
- Use three dots to indicate multiple options are allowed:
- Item used literal:
Please be sure to use
example.net in any examples and tests you provide. These
are the standard domain names created for this purpose. If you don’t, there is a chance your fantasy
domain name is registered by someone and will actually serve web content (which you may like or not).
In a perfect world, the following would be true for plugins: “Either you are responsible for a zone or
not”. If the answer is “not”, the plugin should call the next plugin in the chain. If “yes” it
should handle all names that fall in this zone and the names below - i.e. it should handle the
entire domain and all sub domains, also see here
on how a query is process with
In this example the file plugin is handling all names below (and including)
a query comes in that is not a subdomain (or equal to)
example.org the next plugin is called.
Now, the world isn’t perfect, and there are good reasons to “fallthrough” to the next middleware, meaning a plugin is only responsible for a subset of names within the zone. The first of these to appear was the reverse plugin, now replaced with the generalized template plugin that can synthesizes various responses.
The nature of the template plugin might only deal with specified record TYPEs, and then only
for a subset of the names. Ideally, you would want to layer template in front of another
plugin such as file or auto. This means template could handle some special
reverse cases and all other requests are handled by the backing plugin. This is exactly what
“fallthrough” does. To keep things explicit we’ve opted that plugins implementing such behavior
should implement a
fallthrough directive should optionally accept a list of zones. Only queries for records
in one of those zones should be allowed to fallthrough.
See this document describing the requirements.