From 32d4234ae4edb39a9ae86915d457974d167a0d0c Mon Sep 17 00:00:00 2001 From: Alexander Krantz Date: Thu, 22 Jul 2021 17:44:36 -0700 Subject: [PATCH] Prevent x509_cert from hanging on UDP connection (#9323) --- docs/LICENSE_OF_DEPENDENCIES.md | 4 ++ go.mod | 3 +- go.sum | 18 ++++++++- plugins/inputs/x509_cert/README.md | 5 ++- plugins/inputs/x509_cert/x509_cert.go | 44 ++++++++++++++++++++-- plugins/inputs/x509_cert/x509_cert_test.go | 32 ++++++++++++++++ 6 files changed, 100 insertions(+), 6 deletions(-) diff --git a/docs/LICENSE_OF_DEPENDENCIES.md b/docs/LICENSE_OF_DEPENDENCIES.md index 22b8393dc..7ae13c114 100644 --- a/docs/LICENSE_OF_DEPENDENCIES.md +++ b/docs/LICENSE_OF_DEPENDENCIES.md @@ -176,6 +176,10 @@ following works: - github.com/openzipkin/zipkin-go-opentracing [MIT License](https://github.com/openzipkin/zipkin-go-opentracing/blob/master/LICENSE) - github.com/philhofer/fwd [MIT License](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - github.com/pierrec/lz4 [BSD 3-Clause "New" or "Revised" License](https://github.com/pierrec/lz4/blob/master/LICENSE) +- github.com/pion/dtls [MIT License](https://github.com/pion/dtls/blob/master/LICENSE) +- github.com/pion/logging [MIT License](https://github.com/pion/logging/blob/master/LICENSE) +- github.com/pion/transport [MIT License](https://github.com/pion/transport/blob/master/LICENSE) +- github.com/pion/udp [MIT License](https://github.com/pion/udp/blob/master/LICENSE) - github.com/pkg/browser [BSD 2-Clause "Simplified" License](https://github.com/pkg/browser/blob/master/LICENSE) - github.com/pkg/errors [BSD 2-Clause "Simplified" License](https://github.com/pkg/errors/blob/master/LICENSE) - github.com/pmezard/go-difflib [BSD 3-Clause Clear License](https://github.com/pmezard/go-difflib/blob/master/LICENSE) diff --git a/go.mod b/go.mod index 053765ee6..f0f36e2df 100644 --- a/go.mod +++ b/go.mod @@ -105,6 +105,7 @@ require ( github.com/nsqio/go-nsq v1.0.8 github.com/openconfig/gnmi v0.0.0-20180912164834-33a1865c3029 github.com/openzipkin/zipkin-go-opentracing v0.3.4 + github.com/pion/dtls/v2 v2.0.9 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.7.1 github.com/prometheus/client_model v0.2.0 @@ -140,7 +141,7 @@ require ( go.starlark.net v0.0.0-20210406145628-7a1108eaa012 go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 diff --git a/go.sum b/go.sum index ffd7695ca..20b7759fe 100644 --- a/go.sum +++ b/go.sum @@ -1272,6 +1272,15 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pion/dtls/v2 v2.0.9 h1:7Ow+V++YSZQMYzggI0P9vLJz/hUFcffsfGMfT/Qy+u8= +github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pion/transport v0.12.3 h1:vdBfvfU/0Wq8kd2yhUMSDB/x+O4Z9MYVl2fJ5BT4JZw= +github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= +github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= +github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1602,6 +1611,7 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -1691,12 +1701,16 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1815,7 +1829,9 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= diff --git a/plugins/inputs/x509_cert/README.md b/plugins/inputs/x509_cert/README.md index f206f6c09..5211c38e9 100644 --- a/plugins/inputs/x509_cert/README.md +++ b/plugins/inputs/x509_cert/README.md @@ -3,6 +3,8 @@ This plugin provides information about X509 certificate accessible via local file or network connection. +When using a UDP address as a certificate source, the server must support [DTLS](https://en.wikipedia.org/wiki/Datagram_Transport_Layer_Security). + ### Configuration @@ -11,7 +13,8 @@ file or network connection. [[inputs.x509_cert]] ## List certificate sources, support wildcard expands for files ## Prefix your entry with 'file://' if you intend to use relative paths - sources = ["/etc/ssl/certs/ssl-cert-snakeoil.pem", "tcp://example.org:443", + sources = ["tcp://example.org:443", "https://influxdata.com:443", + "udp://127.0.0.1:4433", "/etc/ssl/certs/ssl-cert-snakeoil.pem", "/etc/mycerts/*.mydomain.org.pem", "file:///path/to/*.pem"] ## Timeout for SSL connection diff --git a/plugins/inputs/x509_cert/x509_cert.go b/plugins/inputs/x509_cert/x509_cert.go index 4ac115931..b106f91b7 100644 --- a/plugins/inputs/x509_cert/x509_cert.go +++ b/plugins/inputs/x509_cert/x509_cert.go @@ -7,6 +7,7 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "github.com/pion/dtls/v2" "io/ioutil" "net" "net/url" @@ -24,7 +25,8 @@ import ( const sampleConfig = ` ## List certificate sources ## Prefix your entry with 'file://' if you intend to use relative paths - sources = ["/etc/ssl/certs/ssl-cert-snakeoil.pem", "tcp://example.org:443", + sources = ["tcp://example.org:443", "https://influxdata.com:443", + "udp://127.0.0.1:4433", "/etc/ssl/certs/ssl-cert-snakeoil.pem", "/etc/mycerts/*.mydomain.org.pem", "file:///path/to/*.pem"] ## Timeout for SSL connection @@ -104,11 +106,47 @@ func (c *X509Cert) serverName(u *url.URL) (string, error) { func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certificate, error) { protocol := u.Scheme switch u.Scheme { + case "udp", "udp4", "udp6": + ipConn, err := net.DialTimeout(u.Scheme, u.Host, timeout) + if err != nil { + return nil, err + } + defer ipConn.Close() + + serverName, err := c.serverName(u) + if err != nil { + return nil, err + } + + dtlsCfg := &dtls.Config{ + InsecureSkipVerify: true, + Certificates: c.tlsCfg.Certificates, + RootCAs: c.tlsCfg.RootCAs, + ServerName: serverName, + } + conn, err := dtls.Client(ipConn, dtlsCfg) + if err != nil { + return nil, err + } + defer conn.Close() + + rawCerts := conn.ConnectionState().PeerCertificates + var certs []*x509.Certificate + for _, rawCert := range rawCerts { + parsed, err := x509.ParseCertificate(rawCert) + if err != nil { + return nil, err + } + + if parsed != nil { + certs = append(certs, parsed) + } + } + + return certs, nil case "https": protocol = "tcp" fallthrough - case "udp", "udp4", "udp6": - fallthrough case "tcp", "tcp4", "tcp6": ipConn, err := net.DialTimeout(protocol, u.Host, timeout) if err != nil { diff --git a/plugins/inputs/x509_cert/x509_cert_test.go b/plugins/inputs/x509_cert/x509_cert_test.go index 4f09b903b..9c42c09bd 100644 --- a/plugins/inputs/x509_cert/x509_cert_test.go +++ b/plugins/inputs/x509_cert/x509_cert_test.go @@ -4,8 +4,10 @@ import ( "crypto/tls" "encoding/base64" "fmt" + "github.com/pion/dtls/v2" "io/ioutil" "math/big" + "net" "net/url" "os" "path/filepath" @@ -260,6 +262,36 @@ func TestGatherChain(t *testing.T) { } } +func TestGatherUDPCert(t *testing.T) { + pair, err := tls.X509KeyPair([]byte(pki.ReadServerCert()), []byte(pki.ReadServerKey())) + require.NoError(t, err) + + cfg := &dtls.Config{ + Certificates: []tls.Certificate{pair}, + } + + addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0} + listener, err := dtls.Listen("udp", addr, cfg) + require.NoError(t, err) + defer listener.Close() + + go func() { + _, _ = listener.Accept() + }() + + m := &X509Cert{ + Sources: []string{"udp://" + listener.Addr().String()}, + Log: testutil.Logger{}, + } + require.NoError(t, m.Init()) + + var acc testutil.Accumulator + require.NoError(t, m.Gather(&acc)) + + assert.Len(t, acc.Errors, 0) + assert.True(t, acc.HasMeasurement("x509_cert")) +} + func TestStrings(t *testing.T) { sc := X509Cert{} require.NoError(t, sc.Init())