dnstap tls support (#5917)

Signed-off-by: dmachard <5562930+dmachard@users.noreply.github.com>
This commit is contained in:
Denis MACHARD 2023-02-21 00:34:48 +01:00 committed by GitHub
parent 66df12d980
commit 83fc3bb5da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 6 deletions

View file

@ -18,6 +18,7 @@ Every message is sent to the socket as soon as it comes in, the *dnstap* plugin
dnstap SOCKET [full] { dnstap SOCKET [full] {
[identity IDENTITY] [identity IDENTITY]
[version VERSION] [version VERSION]
[skipverify]
} }
~~~ ~~~
@ -25,6 +26,7 @@ dnstap SOCKET [full] {
* `full` to include the wire-format DNS message. * `full` to include the wire-format DNS message.
* **IDENTITY** to override the identity of the server. Defaults to the hostname. * **IDENTITY** to override the identity of the server. Defaults to the hostname.
* **VERSION** to override the version field. Defaults to the CoreDNS version. * **VERSION** to override the version field. Defaults to the CoreDNS version.
* `skipverify` to skip tls verification during connection. Default to be secure
## Examples ## 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 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*, 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. and also sends client requests and responses without wire-format DNS messages to a remote FQDN.

View file

@ -1,6 +1,7 @@
package dnstap package dnstap
import ( import (
"crypto/tls"
"net" "net"
"sync/atomic" "sync/atomic"
"time" "time"
@ -14,6 +15,8 @@ const (
tcpTimeout = 4 * time.Second tcpTimeout = 4 * time.Second
flushTimeout = 1 * 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. // tapper interface is used in testing to mock the Dnstap method.
@ -31,6 +34,7 @@ type dio struct {
quit chan struct{} quit chan struct{}
flushTimeout time.Duration flushTimeout time.Duration
tcpTimeout time.Duration tcpTimeout time.Duration
skipVerify bool
} }
// newIO returns a new and initialized pointer to a dio. // newIO returns a new and initialized pointer to a dio.
@ -42,14 +46,32 @@ func newIO(proto, endpoint string) *dio {
quit: make(chan struct{}), quit: make(chan struct{}),
flushTimeout: flushTimeout, flushTimeout: flushTimeout,
tcpTimeout: tcpTimeout, tcpTimeout: tcpTimeout,
skipVerify: skipVerify,
} }
} }
func (d *dio) dial() error { func (d *dio) dial() error {
conn, err := net.DialTimeout(d.proto, d.endpoint, d.tcpTimeout) var conn net.Conn
if err != nil { var err error
return err
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 { if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetWriteBuffer(tcpWriteBufSize) tcpConn.SetWriteBuffer(tcpWriteBufSize)
tcpConn.SetNoDelay(false) tcpConn.SetNoDelay(false)

View file

@ -30,17 +30,26 @@ func parseConfig(c *caddy.Controller) ([]*Dnstap, error) {
endpoint = args[0] endpoint = args[0]
if strings.HasPrefix(endpoint, "tcp://") { var dio *dio
if strings.HasPrefix(endpoint, "tls://") {
// remote network endpoint // remote network endpoint
endpointURL, err := url.Parse(endpoint) endpointURL, err := url.Parse(endpoint)
if err != nil { if err != nil {
return nil, c.ArgErr() 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} d = Dnstap{io: dio}
} else { } else {
endpoint = strings.TrimPrefix(endpoint, "unix://") endpoint = strings.TrimPrefix(endpoint, "unix://")
dio := newIO("unix", endpoint) dio = newIO("unix", endpoint)
d = Dnstap{io: dio} d = Dnstap{io: dio}
} }
@ -52,6 +61,10 @@ func parseConfig(c *caddy.Controller) ([]*Dnstap, error) {
for c.NextBlock() { for c.NextBlock() {
switch c.Val() { switch c.Val() {
case "skipverify":
{
dio.skipVerify = true
}
case "identity": case "identity":
{ {
if !c.NextArg() { if !c.NextArg() {

View file

@ -44,6 +44,7 @@ func TestConfig(t *testing.T) {
{"dnstap.sock", true, "unix", []byte("NAME"), []byte("VER")}, {"dnstap.sock", true, "unix", []byte("NAME"), []byte("VER")},
{"127.0.0.1:6000", false, "tcp", []byte("NAME2"), []byte("VER2")}, {"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 { for i, tc := range tests {
c := caddy.NewTestController("dns", tc.in) c := caddy.NewTestController("dns", tc.in)