From 83fc3bb5dac9b4e9082a331a2f6ef3c303c91c47 Mon Sep 17 00:00:00 2001 From: Denis MACHARD <5562930+dmachard@users.noreply.github.com> Date: Tue, 21 Feb 2023 00:34:48 +0100 Subject: [PATCH] dnstap tls support (#5917) Signed-off-by: dmachard <5562930+dmachard@users.noreply.github.com> --- plugin/dnstap/README.md | 10 ++++++++++ plugin/dnstap/io.go | 28 +++++++++++++++++++++++++--- plugin/dnstap/setup.go | 19 ++++++++++++++++--- plugin/dnstap/setup_test.go | 1 + 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/plugin/dnstap/README.md b/plugin/dnstap/README.md index fc59033ac..8b0716044 100644 --- a/plugin/dnstap/README.md +++ b/plugin/dnstap/README.md @@ -18,6 +18,7 @@ Every message is sent to the socket as soon as it comes in, the *dnstap* plugin dnstap SOCKET [full] { [identity IDENTITY] [version VERSION] + [skipverify] } ~~~ @@ -25,6 +26,7 @@ dnstap SOCKET [full] { * `full` to include the wire-format DNS message. * **IDENTITY** to override the identity of the server. Defaults to the hostname. * **VERSION** to override the version field. Defaults to the CoreDNS version. +* `skipverify` to skip tls verification during connection. Default to be secure ## Examples @@ -61,6 +63,14 @@ dnstap /tmp/dnstap.sock { } ~~~ +Log to a remote TLS endpoint. + +~~~ txt +dnstap tls://127.0.0.1:6000 full { + skipverify +} +~~~ + You can use _dnstap_ more than once to define multiple taps. The following logs information 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. diff --git a/plugin/dnstap/io.go b/plugin/dnstap/io.go index d15d5669b..f95e4b5e8 100644 --- a/plugin/dnstap/io.go +++ b/plugin/dnstap/io.go @@ -1,6 +1,7 @@ package dnstap import ( + "crypto/tls" "net" "sync/atomic" "time" @@ -14,6 +15,8 @@ const ( tcpTimeout = 4 * time.Second flushTimeout = 1 * time.Second + + skipVerify = false // by default, every tls connection is verified to be secure ) // tapper interface is used in testing to mock the Dnstap method. @@ -31,6 +34,7 @@ type dio struct { quit chan struct{} flushTimeout time.Duration tcpTimeout time.Duration + skipVerify bool } // newIO returns a new and initialized pointer to a dio. @@ -42,14 +46,32 @@ func newIO(proto, endpoint string) *dio { quit: make(chan struct{}), flushTimeout: flushTimeout, tcpTimeout: tcpTimeout, + skipVerify: skipVerify, } } func (d *dio) dial() error { - conn, err := net.DialTimeout(d.proto, d.endpoint, d.tcpTimeout) - if err != nil { - return err + var conn net.Conn + var err error + + if d.proto == "tls" { + config := &tls.Config{ + InsecureSkipVerify: d.skipVerify, + } + dialer := &net.Dialer{ + Timeout: d.tcpTimeout, + } + conn, err = tls.DialWithDialer(dialer, "tcp", d.endpoint, config) + if err != nil { + return err + } + } else { + conn, err = net.DialTimeout(d.proto, d.endpoint, d.tcpTimeout) + if err != nil { + return err + } } + if tcpConn, ok := conn.(*net.TCPConn); ok { tcpConn.SetWriteBuffer(tcpWriteBufSize) tcpConn.SetNoDelay(false) diff --git a/plugin/dnstap/setup.go b/plugin/dnstap/setup.go index ff9b3d89e..c61c453fd 100644 --- a/plugin/dnstap/setup.go +++ b/plugin/dnstap/setup.go @@ -30,17 +30,26 @@ func parseConfig(c *caddy.Controller) ([]*Dnstap, error) { endpoint = args[0] - if strings.HasPrefix(endpoint, "tcp://") { + var dio *dio + if strings.HasPrefix(endpoint, "tls://") { // remote network endpoint endpointURL, err := url.Parse(endpoint) if err != nil { return nil, c.ArgErr() } - dio := newIO("tcp", endpointURL.Host) + dio = newIO("tls", endpointURL.Host) + d = Dnstap{io: dio} + } else if strings.HasPrefix(endpoint, "tcp://") { + // remote network endpoint + endpointURL, err := url.Parse(endpoint) + if err != nil { + return nil, c.ArgErr() + } + dio = newIO("tcp", endpointURL.Host) d = Dnstap{io: dio} } else { endpoint = strings.TrimPrefix(endpoint, "unix://") - dio := newIO("unix", endpoint) + dio = newIO("unix", endpoint) d = Dnstap{io: dio} } @@ -52,6 +61,10 @@ func parseConfig(c *caddy.Controller) ([]*Dnstap, error) { for c.NextBlock() { switch c.Val() { + case "skipverify": + { + dio.skipVerify = true + } case "identity": { if !c.NextArg() { diff --git a/plugin/dnstap/setup_test.go b/plugin/dnstap/setup_test.go index 92ce5f055..029a4a73b 100644 --- a/plugin/dnstap/setup_test.go +++ b/plugin/dnstap/setup_test.go @@ -44,6 +44,7 @@ func TestConfig(t *testing.T) { {"dnstap.sock", true, "unix", []byte("NAME"), []byte("VER")}, {"127.0.0.1:6000", false, "tcp", []byte("NAME2"), []byte("VER2")}, }}, + {"dnstap tls://127.0.0.1:6000", false, []results{{"127.0.0.1:6000", false, "tls", []byte(hostname), []byte("-")}}}, } for i, tc := range tests { c := caddy.NewTestController("dns", tc.in)