COREDNS-DNSTAP(7)

πŸ“¦
version 1.15.0 Β· 3 versions
, in opensuse-leap 16.0

πŸ’… COREDNS-DNSTAP πŸ’…

okay so bestie, you're in the CoreDNS Plugins section 7 and this is COREDNS-DNSTAP(7) β€” the dnstap page. buckle up πŸŽ€

✨ NAME ✨

dnstap β€” literally just enables logging to dnstap. that's the whole vibe.

🌸 DESCRIPTION 🌸

so dnstap is this flexible, structured binary log format for DNS software β€” go look at https://dnstap.info for the lore. with this plugin you make CoreDNS output dnstap logging. no cap. πŸ”₯

every message gets sent to the socket as soon as it comes in, and the dnstap plugin keeps a buffer of 10000 messages. above that number? dnstap messages will be dropped 😭 and yes, that drop gets logged. it's giving "we tried".


πŸ¦‹ SYNTAX πŸ¦‹

πŸ’œ OUTGOING CONNECTIONS (CONNECT TO SINK)

dnstap SOCKET [full] [writebuffer] [queue] {
  [identity IDENTITY]
  [version VERSION]
  [extra EXTRA]
  [skipverify]
}
  • SOCKET β€” this is the socket (path) you hand over to the dnstap command line tool.
  • full β€” include the wire-format DNS message. yes, the whole thing πŸ‘‘
  • writebuffer β€” sets the TCP write buffer multiplier in MiB. valid range: [1, 1024].
  • queue β€” sets the queue multiplier, applied to 10,000 messages. valid range: [1, 4096]. so it multiplies, periodt.
  • IDENTITY β€” override the identity of the server. defaults to the hostname (so it just uses whatever the host is called, cute).
  • VERSION β€” override the version field. defaults to the CoreDNS version.
  • EXTRA β€” define the "extra" field in the dnstap payload. the metadata replacement is available right here (that's the ../metadata/ thing).
  • skipverify β€” skip TLS verification during the connection. default is to be secure πŸ”’ (like, stay safe out there)

🩷 INCOMING CONNECTIONS (ACCEPT FROM SINKS)

dnstap listen SOCKET [full] {
  [identity IDENTITY]
  [version VERSION]
  [extra EXTRA]
  [tls CERT KEY [CA]]
  [skipverify]
}
  • listen β€” this little keyword is saying "hi yes this is a listening socket that accepts incoming connections from dnstap sinks". main character energy fr fr.
  • SOCKET β€” the socket address to listen on. like tcp://127.0.0.1:6000 or unix:///tmp/dnstap.sock.
  • full β€” include the wire-format DNS message.
  • IDENTITY β€” override the identity of the server. defaults to the hostname.
  • VERSION β€” override the version field. defaults to the CoreDNS version.
  • EXTRA β€” define the "extra" field in the dnstap payload. metadata replacement is available here too (../metadata/).
  • tls CERT KEY [CA] β€” enable TLS for the listener. CERT and KEY are paths to the server certificate and key files. the optional CA is the path to the CA certificate for client verification.
  • skipverify β€” skip client certificate verification. default is to verify client certificates. it's equivalent to the CA option above being unspecified.
⚠️ note bestie: incoming connections use unbuffered channels to broadcast events. if a connected sink becomes slow or disconnected, messages are dropped for that sink only, and the connection is closed. so it's not a whole-server situation, it's a that-one-sink situation πŸ’€

🌈 EXAMPLES 🌈

log info about client requests and responses to /tmp/dnstap.sock:

dnstap /tmp/dnstap.sock

log info about client requests and responses with a custom TCP write buffer (1024 MiB) and queue capacity (2048 x 10000):

dnstap /tmp/dnstap.sock full 1024 2048

log info including the wire-format DNS message about client requests and responses to /tmp/dnstap.sock:

dnstap unix:///tmp/dnstap.sock full

log to a remote endpoint:

dnstap tcp://127.0.0.1:6000 full

log to a remote endpoint by FQDN:

dnstap tcp://example.com:6000 full

log to a socket while overriding the default identity and version:

dnstap /tmp/dnstap.sock {
  identity my-dns-server1
  version MyDNSServer-1.2.3
}

log to a socket and customize the "extra" field in the dnstap payload. you may use metadata provided by other plugins in the extra field 🫢

forward . 8.8.8.8
metadata
dnstap /tmp/dnstap.sock {
  extra "upstream: {/forward/upstream}"
}

log to a remote TLS endpoint:

dnstap tls://127.0.0.1:6000 full {
  skipverify
}

listen for incoming dnstap sink connections on a Unix socket:

dnstap listen /tmp/dnstap.sock full

listen for incoming dnstap sink connections on TCP:

dnstap listen tcp://127.0.0.1:6000 full

listen for incoming dnstap sink connections on TLS with mTLS client authentication:

dnstap listen tls://127.0.0.1:6000 full {
  tls /path/to/server-cert.pem /path/to/server-key.pem /path/to/ca.pem
}

listen for incoming dnstap sink connections on TLS without client certificate verification:

dnstap listen tls://127.0.0.1:6000 full {
  tls /path/to/server-cert.pem /path/to/server-key.pem
  skipverify
}

you can use dnstap more than once to define multiple taps πŸ‘‘ the following logs info including the wire-format DNS message about client requests and responses to /tmp/dnstap.sock, and also sends client requests and responses without wire-format DNS messages to a remote FQDN:

dnstap /tmp/dnstap.sock full
dnstap tcp://example.com:6000

and yes you can absolutely combine outgoing connections with incoming listeners:

dnstap tcp://remote-collector.example.com:6000 full
dnstap listen tcp://127.0.0.1:6001 full

🧚 COMMAND LINE TOOL 🧚

dnstap has a command line tool that can be used to inspect the logging. it lives on GitHub at https://github.com/dnstap/golang-dnstap. it's written in Go. slay.

the following command listens on the given socket and decodes messages to stdout:

$ dnstap -u /tmp/dnstap.sock

the following command listens on the given socket and saves message payloads to a binary dnstap-format log file:

$ dnstap -u /tmp/dnstap.sock -w /tmp/test.dnstap

listen for dnstap messages on port 6000:

$ dnstap -l 127.0.0.1:6000

⭐ USING DNSTAP IN YOUR PLUGIN ⭐

in your setup function, collect and store a list of all dnstap plugins loaded in the config:

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
})

and then in your plugin:

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})
    }
    // ...
}

πŸ”₯ SEE ALSO πŸ”₯

the website https://dnstap.info has info on the dnstap protocol. also the forward plugin's dnstap.go uses dnstap to tap messages sent to an upstream β€” iykyk πŸ‘€


🫢 and that's the whole page bestie, no crumbs left. this was COREDNS-DNSTAP(7) from the CoreDNS gang, dated October 2026. go forth and tap those messages πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from