.\" Generated by Mmark Markdown Processer - mmark.miek.nl .TH "COREDNS-DNSTAP" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP \fIdnstap\fP - enables logging to dnstap. .SH "DESCRIPTION" .PP dnstap is a flexible, structured binary log format for DNS software; see https://dnstap.info \[la]https://dnstap.info\[ra]. With this plugin you make CoreDNS output dnstap logging. .PP Every message is sent to the socket as soon as it comes in, the \fIdnstap\fP plugin has a buffer of 10000 messages, above that number dnstap messages will be dropped (this is logged). .SH "SYNTAX" .PP .RS .nf dnstap SOCKET [full] [writebuffer] [queue] { [identity IDENTITY] [version VERSION] [extra EXTRA] [skipverify] } .fi .RE .IP \(bu 4 \fBSOCKET\fP is the socket (path) supplied to the dnstap command line tool. .IP \(bu 4 \fB\fCfull\fR to include the wire-format DNS message. .IP \(bu 4 \fBwritebuffer\fP sets the TCP write buffer multiplier in MiB. Valid range: [1, 1024]. .IP \(bu 4 \fBqueue\fP sets the queue multiplier, applied to 10,000 messages. Valid range: [1, 4096]. .IP \(bu 4 \fBIDENTITY\fP to override the identity of the server. Defaults to the hostname. .IP \(bu 4 \fBVERSION\fP to override the version field. Defaults to the CoreDNS version. .IP \(bu 4 \fBEXTRA\fP to define "extra" field in dnstap payload, metadata \[la]../metadata/\[ra] replacement available here. .IP \(bu 4 \fB\fCskipverify\fR to skip tls verification during connection. Default to be secure .SH "EXAMPLES" .PP Log information about client requests and responses to \fI/tmp/dnstap.sock\fP. .PP .RS .nf dnstap /tmp/dnstap.sock .fi .RE .PP Log information about client requests and responses with a custom TCP write buffer (1024 MiB) and queue capacity (2048 x 10000). .PP .RS .nf dnstap /tmp/dnstap.sock full 1024 2048 .fi .RE .PP Log information including the wire-format DNS message about client requests and responses to \fI/tmp/dnstap.sock\fP. .PP .RS .nf dnstap unix:///tmp/dnstap.sock full .fi .RE .PP Log to a remote endpoint. .PP .RS .nf dnstap tcp://127.0.0.1:6000 full .fi .RE .PP Log to a remote endpoint by FQDN. .PP .RS .nf dnstap tcp://example.com:6000 full .fi .RE .PP Log to a socket, overriding the default identity and version. .PP .RS .nf dnstap /tmp/dnstap.sock { identity my\-dns\-server1 version MyDNSServer\-1.2.3 } .fi .RE .PP Log to a socket, customize the "extra" field in dnstap payload. You may use metadata provided by other plugins in the extra field. .PP .RS .nf forward . 8.8.8.8 metadata dnstap /tmp/dnstap.sock { extra "upstream: {/forward/upstream}" } .fi .RE .PP Log to a remote TLS endpoint. .PP .RS .nf dnstap tls://127.0.0.1:6000 full { skipverify } .fi .RE .PP You can use \fIdnstap\fP more than once to define multiple taps. The following logs information including the wire-format DNS message about client requests and responses to \fI/tmp/dnstap.sock\fP, and also sends client requests and responses without wire-format DNS messages to a remote FQDN. .PP .RS .nf dnstap /tmp/dnstap.sock full dnstap tcp://example.com:6000 .fi .RE .SH "COMMAND LINE TOOL" .PP Dnstap has a command line tool that can be used to inspect the logging. The tool can be found at GitHub: https://github.com/dnstap/golang-dnstap \[la]https://github.com/dnstap/golang-dnstap\[ra]. It's written in Go. .PP The following command listens on the given socket and decodes messages to stdout. .PP .RS .nf $ dnstap \-u /tmp/dnstap.sock .fi .RE .PP The following command listens on the given socket and saves message payloads to a binary dnstap-format log file. .PP .RS .nf $ dnstap \-u /tmp/dnstap.sock \-w /tmp/test.dnstap .fi .RE .PP Listen for dnstap messages on port 6000. .PP .RS .nf $ dnstap \-l 127.0.0.1:6000 .fi .RE .SH "USING DNSTAP IN YOUR PLUGIN" .PP In your setup function, collect and store a list of all \fIdnstap\fP plugins loaded in the config: .PP .RS .nf x := \&ExamplePlugin{} c.OnStartup(func() error { if taph := dnsserver.GetConfig(c).Handler("dnstap"); taph != nil { for tapPlugin, ok := taph.(*dnstap.Dnstap); ok; tapPlugin, ok = tapPlugin.Next.(*dnstap.Dnstap) { x.tapPlugins = append(x.tapPlugins, tapPlugin) } } return nil }) .fi .RE .PP And then in your plugin: .PP .RS .nf import ( "github.com/coredns/coredns/plugin/dnstap/msg" "github.com/coredns/coredns/request" tap "github.com/dnstap/golang\-dnstap" ) func (x ExamplePlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { for \_, tapPlugin := range x.tapPlugins { q := new(msg.Msg) msg.SetQueryTime(q, time.Now()) msg.SetQueryAddress(q, w.RemoteAddr()) if tapPlugin.IncludeRawMessage { buf, \_ := r.Pack() // r has been seen packed/unpacked before, this should not fail q.QueryMessage = buf } msg.SetType(q, tap.Message\_CLIENT\_QUERY) // if no metadata interpretation is needed, just send the message tapPlugin.TapMessage(q) // OR: to interpret the metadata in "extra" field, give more context info tapPlugin.TapMessageWithMetadata(ctx, q, request.Request{W: w, Req: query}) } // ... } .fi .RE .SH "SEE ALSO" .PP The website dnstap.info \[la]https://dnstap.info\[ra] has info on the dnstap protocol. The \fIforward\fP plugin's \fB\fCdnstap.go\fR uses dnstap to tap messages sent to an upstream.