From 0ddeb0c355b870d70674a95f5558022bcd242240 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Wed, 20 Nov 2019 13:21:32 +0800 Subject: [PATCH 1/5] Add additional tags for x509 Input Plugin --- etc/telegraf.conf | 10 ++++- plugins/inputs/x509_cert/README.md | 15 ++++++++ plugins/inputs/x509_cert/x509_cert.go | 55 ++++++++++++++++++++++----- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/etc/telegraf.conf b/etc/telegraf.conf index bab1fb4561bca..7fc4f9463a082 100644 --- a/etc/telegraf.conf +++ b/etc/telegraf.conf @@ -3652,7 +3652,7 @@ # # timeout = "5ms" -# # A plugin to collect stats from Opensmtpd - a validating, recursive, and caching DNS resolver +# # A plugin to collect stats from Opensmtpd - a validating, recursive, and caching DNS resolver # [[inputs.opensmtpd]] # ## If running as a restricted user you can prepend sudo for additional access: # #use_sudo = false @@ -4514,6 +4514,14 @@ # ## Timeout for SSL connection # # timeout = "5s" # +# ## Include Certificate Issuer information in tags +# # include_issuer = false +# +# ## Include Certificate SAN in tag +# # include_san = false +# ## Separator between each SAN in tag +# # san_separator = "," +# # ## Optional TLS Config # # tls_ca = "/etc/telegraf/ca.pem" # # tls_cert = "/etc/telegraf/cert.pem" diff --git a/plugins/inputs/x509_cert/README.md b/plugins/inputs/x509_cert/README.md index 450dd3d1039e0..99bab28c5f384 100644 --- a/plugins/inputs/x509_cert/README.md +++ b/plugins/inputs/x509_cert/README.md @@ -15,10 +15,19 @@ file or network connection. ## Timeout for SSL connection # timeout = "5s" + ## Include Certificate Issuer information in tags + # include_issuer = false + + ## Include Certificate SAN in tag + # include_san = false + ## Separator between each SAN in tag + # san_separator = "," + ## Optional TLS Config # tls_ca = "/etc/telegraf/ca.pem" # tls_cert = "/etc/telegraf/cert.pem" # tls_key = "/etc/telegraf/key.pem" + ``` @@ -33,6 +42,12 @@ file or network connection. - province - locality - verification + - serial_number + - signature_algorithm + - public_key_algorithm + - issuer_common_name + - issuer_serial_number + - san - fields: - verification_code (int) - verification_error (string) diff --git a/plugins/inputs/x509_cert/x509_cert.go b/plugins/inputs/x509_cert/x509_cert.go index 825fd5eeb18d1..c97a86af8d352 100644 --- a/plugins/inputs/x509_cert/x509_cert.go +++ b/plugins/inputs/x509_cert/x509_cert.go @@ -4,7 +4,6 @@ package x509_cert import ( "crypto/tls" "crypto/x509" - "crypto/x509/pkix" "encoding/pem" "fmt" "io/ioutil" @@ -26,6 +25,14 @@ const sampleConfig = ` ## Timeout for SSL connection # timeout = "5s" + ## Include Certificate Issuer information in tags + # include_issuer = false + + ## Include Certificate SAN in tag + # include_san = false + ## Separator between each SAN in tag + # san_separator = "," + ## Optional TLS Config # tls_ca = "/etc/telegraf/ca.pem" # tls_cert = "/etc/telegraf/cert.pem" @@ -35,9 +42,12 @@ const description = "Reads metrics from a SSL certificate" // X509Cert holds the configuration of the plugin. type X509Cert struct { - Sources []string `toml:"sources"` - Timeout internal.Duration `toml:"timeout"` - tlsCfg *tls.Config + Sources []string `toml:"sources"` + Timeout internal.Duration `toml:"timeout"` + IncludeIssuer bool `toml:"include_issuer"` + IncludeSAN bool `toml:"include_san"` + SANSeperator string `toml:"san_separator"` + tlsCfg *tls.Config _tls.ClientConfig } @@ -129,10 +139,15 @@ func getFields(cert *x509.Certificate, now time.Time) map[string]interface{} { return fields } -func getTags(subject pkix.Name, location string) map[string]string { +func (c *X509Cert) getTags(cert *x509.Certificate, location string) map[string]string { + subject := cert.Subject + tags := map[string]string{ - "source": location, - "common_name": subject.CommonName, + "source": location, + "common_name": subject.CommonName, + "serial_number": cert.SerialNumber.Text(16), + "signature_algorithm": cert.SignatureAlgorithm.String(), + "public_key_algorithm": cert.PublicKeyAlgorithm.String(), } if len(subject.Organization) > 0 { @@ -151,6 +166,23 @@ func getTags(subject pkix.Name, location string) map[string]string { tags["locality"] = subject.Locality[0] } + if c.IncludeIssuer { + issuer := cert.Issuer + tags["issuer_common_name"] = issuer.CommonName + tags["issuer_serial_number"] = issuer.SerialNumber + } + + if c.IncludeSAN { + san := append(cert.DNSNames, cert.EmailAddresses...) + for _, ip := range cert.IPAddresses { + san = append(san, ip.String()) + } + for _, uri := range cert.URIs { + san = append(san, uri.String()) + } + tags["san"] = strings.Join(san, c.SANSeperator) + } + return tags } @@ -172,7 +204,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error { for i, cert := range certs { fields := getFields(cert, now) - tags := getTags(cert.Subject, location) + tags := c.getTags(cert, location) // The first certificate is the leaf/end-entity certificate which needs DNS // name validation against the URL hostname. @@ -225,8 +257,11 @@ func (c *X509Cert) Init() error { func init() { inputs.Add("x509_cert", func() telegraf.Input { return &X509Cert{ - Sources: []string{}, - Timeout: internal.Duration{Duration: 5}, + Sources: []string{}, + Timeout: internal.Duration{Duration: 5}, + IncludeIssuer: false, + IncludeSAN: false, + SANSeperator: ",", } }) } From dca03955ed539577cf468b5806c20181b6d829bb Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Tue, 26 Nov 2019 09:14:44 +0800 Subject: [PATCH 2/5] Remove `subject` local variable --- plugins/inputs/x509_cert/x509_cert.go | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/plugins/inputs/x509_cert/x509_cert.go b/plugins/inputs/x509_cert/x509_cert.go index c97a86af8d352..f3432993629dd 100644 --- a/plugins/inputs/x509_cert/x509_cert.go +++ b/plugins/inputs/x509_cert/x509_cert.go @@ -140,30 +140,28 @@ func getFields(cert *x509.Certificate, now time.Time) map[string]interface{} { } func (c *X509Cert) getTags(cert *x509.Certificate, location string) map[string]string { - subject := cert.Subject - tags := map[string]string{ "source": location, - "common_name": subject.CommonName, + "common_name": cert.Subject.CommonName, "serial_number": cert.SerialNumber.Text(16), "signature_algorithm": cert.SignatureAlgorithm.String(), "public_key_algorithm": cert.PublicKeyAlgorithm.String(), } - if len(subject.Organization) > 0 { - tags["organization"] = subject.Organization[0] + if len(cert.Subject.Organization) > 0 { + tags["organization"] = cert.Subject.Organization[0] } - if len(subject.OrganizationalUnit) > 0 { - tags["organizational_unit"] = subject.OrganizationalUnit[0] + if len(cert.Subject.OrganizationalUnit) > 0 { + tags["organizational_unit"] = cert.Subject.OrganizationalUnit[0] } - if len(subject.Country) > 0 { - tags["country"] = subject.Country[0] + if len(cert.Subject.Country) > 0 { + tags["country"] = cert.Subject.Country[0] } - if len(subject.Province) > 0 { - tags["province"] = subject.Province[0] + if len(cert.Subject.Province) > 0 { + tags["province"] = cert.Subject.Province[0] } - if len(subject.Locality) > 0 { - tags["locality"] = subject.Locality[0] + if len(cert.Subject.Locality) > 0 { + tags["locality"] = cert.Subject.Locality[0] } if c.IncludeIssuer { From dc22d65d5415fbb2084dacb190f27ed36928d16d Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Tue, 26 Nov 2019 09:15:05 +0800 Subject: [PATCH 3/5] Undo changes to `etc/telegraf.conf ` --- etc/telegraf.conf | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/etc/telegraf.conf b/etc/telegraf.conf index 7fc4f9463a082..bab1fb4561bca 100644 --- a/etc/telegraf.conf +++ b/etc/telegraf.conf @@ -3652,7 +3652,7 @@ # # timeout = "5ms" -# # A plugin to collect stats from Opensmtpd - a validating, recursive, and caching DNS resolver +# # A plugin to collect stats from Opensmtpd - a validating, recursive, and caching DNS resolver # [[inputs.opensmtpd]] # ## If running as a restricted user you can prepend sudo for additional access: # #use_sudo = false @@ -4514,14 +4514,6 @@ # ## Timeout for SSL connection # # timeout = "5s" # -# ## Include Certificate Issuer information in tags -# # include_issuer = false -# -# ## Include Certificate SAN in tag -# # include_san = false -# ## Separator between each SAN in tag -# # san_separator = "," -# # ## Optional TLS Config # # tls_ca = "/etc/telegraf/ca.pem" # # tls_cert = "/etc/telegraf/cert.pem" From 1cae7843d6450213f5ed4327b1f4166a5cb8e44b Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Tue, 26 Nov 2019 09:17:52 +0800 Subject: [PATCH 4/5] Remove options --- plugins/inputs/x509_cert/README.md | 9 ----- plugins/inputs/x509_cert/x509_cert.go | 51 +++++++++------------------ 2 files changed, 16 insertions(+), 44 deletions(-) diff --git a/plugins/inputs/x509_cert/README.md b/plugins/inputs/x509_cert/README.md index 99bab28c5f384..b302d4992eeb3 100644 --- a/plugins/inputs/x509_cert/README.md +++ b/plugins/inputs/x509_cert/README.md @@ -15,19 +15,10 @@ file or network connection. ## Timeout for SSL connection # timeout = "5s" - ## Include Certificate Issuer information in tags - # include_issuer = false - - ## Include Certificate SAN in tag - # include_san = false - ## Separator between each SAN in tag - # san_separator = "," - ## Optional TLS Config # tls_ca = "/etc/telegraf/ca.pem" # tls_cert = "/etc/telegraf/cert.pem" # tls_key = "/etc/telegraf/key.pem" - ``` diff --git a/plugins/inputs/x509_cert/x509_cert.go b/plugins/inputs/x509_cert/x509_cert.go index f3432993629dd..40ecc7781c963 100644 --- a/plugins/inputs/x509_cert/x509_cert.go +++ b/plugins/inputs/x509_cert/x509_cert.go @@ -25,14 +25,6 @@ const sampleConfig = ` ## Timeout for SSL connection # timeout = "5s" - ## Include Certificate Issuer information in tags - # include_issuer = false - - ## Include Certificate SAN in tag - # include_san = false - ## Separator between each SAN in tag - # san_separator = "," - ## Optional TLS Config # tls_ca = "/etc/telegraf/ca.pem" # tls_cert = "/etc/telegraf/cert.pem" @@ -42,12 +34,9 @@ const description = "Reads metrics from a SSL certificate" // X509Cert holds the configuration of the plugin. type X509Cert struct { - Sources []string `toml:"sources"` - Timeout internal.Duration `toml:"timeout"` - IncludeIssuer bool `toml:"include_issuer"` - IncludeSAN bool `toml:"include_san"` - SANSeperator string `toml:"san_separator"` - tlsCfg *tls.Config + Sources []string `toml:"sources"` + Timeout internal.Duration `toml:"timeout"` + tlsCfg *tls.Config _tls.ClientConfig } @@ -139,7 +128,7 @@ func getFields(cert *x509.Certificate, now time.Time) map[string]interface{} { return fields } -func (c *X509Cert) getTags(cert *x509.Certificate, location string) map[string]string { +func getTags(cert *x509.Certificate, location string) map[string]string { tags := map[string]string{ "source": location, "common_name": cert.Subject.CommonName, @@ -164,22 +153,17 @@ func (c *X509Cert) getTags(cert *x509.Certificate, location string) map[string]s tags["locality"] = cert.Subject.Locality[0] } - if c.IncludeIssuer { - issuer := cert.Issuer - tags["issuer_common_name"] = issuer.CommonName - tags["issuer_serial_number"] = issuer.SerialNumber - } + tags["issuer_common_name"] = cert.Issuer.CommonName + tags["issuer_serial_number"] = cert.Issuer.SerialNumber - if c.IncludeSAN { - san := append(cert.DNSNames, cert.EmailAddresses...) - for _, ip := range cert.IPAddresses { - san = append(san, ip.String()) - } - for _, uri := range cert.URIs { - san = append(san, uri.String()) - } - tags["san"] = strings.Join(san, c.SANSeperator) + san := append(cert.DNSNames, cert.EmailAddresses...) + for _, ip := range cert.IPAddresses { + san = append(san, ip.String()) + } + for _, uri := range cert.URIs { + san = append(san, uri.String()) } + tags["san"] = strings.Join(san, ",") return tags } @@ -202,7 +186,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error { for i, cert := range certs { fields := getFields(cert, now) - tags := c.getTags(cert, location) + tags := getTags(cert, location) // The first certificate is the leaf/end-entity certificate which needs DNS // name validation against the URL hostname. @@ -255,11 +239,8 @@ func (c *X509Cert) Init() error { func init() { inputs.Add("x509_cert", func() telegraf.Input { return &X509Cert{ - Sources: []string{}, - Timeout: internal.Duration{Duration: 5}, - IncludeIssuer: false, - IncludeSAN: false, - SANSeperator: ",", + Sources: []string{}, + Timeout: internal.Duration{Duration: 5}, } }) } From 242a6bbbca0f3ee7b7726dc588616146316307aa Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Tue, 26 Nov 2019 09:45:17 +0800 Subject: [PATCH 5/5] Add tests for new tags --- plugins/inputs/x509_cert/x509_cert_test.go | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/plugins/inputs/x509_cert/x509_cert_test.go b/plugins/inputs/x509_cert/x509_cert_test.go index 188b510d263d9..2fba812087180 100644 --- a/plugins/inputs/x509_cert/x509_cert_test.go +++ b/plugins/inputs/x509_cert/x509_cert_test.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "fmt" "io/ioutil" + "math/big" "os" "testing" "time" @@ -187,6 +188,61 @@ func TestGatherLocal(t *testing.T) { } } +func TestTags(t *testing.T) { + cert := fmt.Sprintf("%s\n%s", pki.ReadServerCert(), pki.ReadCACert()) + + f, err := ioutil.TempFile("", "x509_cert") + if err != nil { + t.Fatal(err) + } + + _, err = f.Write([]byte(cert)) + if err != nil { + t.Fatal(err) + } + + err = f.Close() + if err != nil { + t.Fatal(err) + } + + defer os.Remove(f.Name()) + + sc := X509Cert{ + Sources: []string{f.Name()}, + } + sc.Init() + + acc := testutil.Accumulator{} + err = sc.Gather(&acc) + require.NoError(t, err) + + assert.True(t, acc.HasMeasurement("x509_cert")) + + assert.True(t, acc.HasTag("x509_cert", "common_name")) + assert.Equal(t, "server.localdomain", acc.TagValue("x509_cert", "common_name")) + + assert.True(t, acc.HasTag("x509_cert", "signature_algorithm")) + assert.Equal(t, "SHA256-RSA", acc.TagValue("x509_cert", "signature_algorithm")) + + assert.True(t, acc.HasTag("x509_cert", "public_key_algorithm")) + assert.Equal(t, "RSA", acc.TagValue("x509_cert", "public_key_algorithm")) + + assert.True(t, acc.HasTag("x509_cert", "issuer_common_name")) + assert.Equal(t, "Telegraf Test CA", acc.TagValue("x509_cert", "issuer_common_name")) + + assert.True(t, acc.HasTag("x509_cert", "san")) + assert.Equal(t, "localhost,127.0.0.1", acc.TagValue("x509_cert", "san")) + + assert.True(t, acc.HasTag("x509_cert", "serial_number")) + serialNumber := new(big.Int) + _, validSerialNumber := serialNumber.SetString(acc.TagValue("x509_cert", "serial_number"), 16) + if !validSerialNumber { + t.Errorf("Expected a valid Hex serial number but got %s", acc.TagValue("x509_cert", "serial_number")) + } + assert.Equal(t, big.NewInt(1), serialNumber) +} + func TestGatherChain(t *testing.T) { cert := fmt.Sprintf("%s\n%s", pki.ReadServerCert(), pki.ReadCACert())