From e3c90a0b82f93580f9ab5538ef5b60b26efbe4e0 Mon Sep 17 00:00:00 2001 From: Dane Strandboge Date: Wed, 29 Jan 2025 10:52:35 -0600 Subject: [PATCH 1/2] fix(inputs.x509_cert): Fix serial number leading zeroes trimmed --- plugins/inputs/x509_cert/x509_cert.go | 9 +++++++-- plugins/inputs/x509_cert/x509_cert_test.go | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/x509_cert/x509_cert.go b/plugins/inputs/x509_cert/x509_cert.go index 97097ded3d80f..5743243a00962 100644 --- a/plugins/inputs/x509_cert/x509_cert.go +++ b/plugins/inputs/x509_cert/x509_cert.go @@ -215,7 +215,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error { func (c *X509Cert) processCertificate(certificate *x509.Certificate, opts x509.VerifyOptions) error { chains, err := certificate.Verify(opts) if err != nil { - c.Log.Debugf("Invalid certificate %v", certificate.SerialNumber.Text(16)) + c.Log.Debugf("Invalid certificate %v", getSerialNumberString(certificate)) c.Log.Debugf(" cert DNS names: %v", certificate.DNSNames) c.Log.Debugf(" cert IP addresses: %v", certificate.IPAddresses) c.Log.Debugf(" cert subject: %v", certificate.Subject) @@ -470,7 +470,7 @@ func getTags(cert *x509.Certificate, location string) map[string]string { tags := map[string]string{ "source": location, "common_name": cert.Subject.CommonName, - "serial_number": cert.SerialNumber.Text(16), + "serial_number": getSerialNumberString(cert), "signature_algorithm": cert.SignatureAlgorithm.String(), "public_key_algorithm": cert.PublicKeyAlgorithm.String(), } @@ -524,6 +524,11 @@ func (c *X509Cert) collectCertURLs() []*url.URL { return urls } +func getSerialNumberString(cert *x509.Certificate) string { + // Rather than calling '.Text(16)' this retains leading zeroes + return hex.EncodeToString(cert.SerialNumber.Bytes()) +} + func init() { inputs.Add("x509_cert", func() telegraf.Input { return &X509Cert{ diff --git a/plugins/inputs/x509_cert/x509_cert_test.go b/plugins/inputs/x509_cert/x509_cert_test.go index bb4f72cc14d07..b7b1f3a1f212b 100644 --- a/plugins/inputs/x509_cert/x509_cert_test.go +++ b/plugins/inputs/x509_cert/x509_cert_test.go @@ -482,6 +482,14 @@ func TestServerName(t *testing.T) { } } +func TestCertificateSerialNumberRetainsLeadingZeroes(t *testing.T) { + bi := &big.Int{} + bi.SetString("0123456789abcdef", 16) + require.Equal(t, "0123456789abcdef", getSerialNumberString(&x509.Certificate{ + SerialNumber: bi, + })) +} + // Bases on code from // https://medium.com/@shaneutt/create-sign-x509-certificates-in-golang-8ac4ae49f903 func TestClassification(t *testing.T) { From dcbd744017f34231e386a13f7fd37d4a08eb7529 Mon Sep 17 00:00:00 2001 From: Dane Strandboge Date: Fri, 31 Jan 2025 09:27:38 -0600 Subject: [PATCH 2/2] chore: pad serial to 128-bits, add config --- plugins/inputs/x509_cert/README.md | 3 +++ plugins/inputs/x509_cert/sample.conf | 3 +++ plugins/inputs/x509_cert/x509_cert.go | 17 ++++++++++------- plugins/inputs/x509_cert/x509_cert_test.go | 12 +++++++++--- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/plugins/inputs/x509_cert/README.md b/plugins/inputs/x509_cert/README.md index 80f038a4aa8ba..cf2ced8614bd2 100644 --- a/plugins/inputs/x509_cert/README.md +++ b/plugins/inputs/x509_cert/README.md @@ -39,6 +39,9 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. ## Only output the leaf certificates and omit the root ones. # exclude_root_certs = false + ## Pad certificate serial number with zeroes to 128-bits. + # pad_serial_with_zeroes = false + ## Optional TLS Config # tls_ca = "/etc/telegraf/ca.pem" # tls_cert = "/etc/telegraf/cert.pem" diff --git a/plugins/inputs/x509_cert/sample.conf b/plugins/inputs/x509_cert/sample.conf index d1b94e41a4e65..1c29fad9a2ef9 100644 --- a/plugins/inputs/x509_cert/sample.conf +++ b/plugins/inputs/x509_cert/sample.conf @@ -19,6 +19,9 @@ ## Only output the leaf certificates and omit the root ones. # exclude_root_certs = false + ## Pad certificate serial number with zeroes to 128-bits. + # pad_serial_with_zeroes = false + ## Optional TLS Config # tls_ca = "/etc/telegraf/ca.pem" # tls_cert = "/etc/telegraf/cert.pem" diff --git a/plugins/inputs/x509_cert/x509_cert.go b/plugins/inputs/x509_cert/x509_cert.go index 5743243a00962..4962997f91a8f 100644 --- a/plugins/inputs/x509_cert/x509_cert.go +++ b/plugins/inputs/x509_cert/x509_cert.go @@ -44,6 +44,7 @@ type X509Cert struct { Timeout config.Duration `toml:"timeout"` ServerName string `toml:"server_name"` ExcludeRootCerts bool `toml:"exclude_root_certs"` + PadSerial bool `toml:"pad_serial_with_zeroes"` Log telegraf.Logger `toml:"-"` common_tls.ClientConfig proxy.TCPProxy @@ -132,7 +133,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error { for i, cert := range certs { fields := getFields(cert, now) - tags := getTags(cert, location.String()) + tags := c.getTags(cert, location.String()) // Extract the verification result err := results[i] @@ -215,7 +216,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error { func (c *X509Cert) processCertificate(certificate *x509.Certificate, opts x509.VerifyOptions) error { chains, err := certificate.Verify(opts) if err != nil { - c.Log.Debugf("Invalid certificate %v", getSerialNumberString(certificate)) + c.Log.Debugf("Invalid certificate %v", c.getSerialNumberString(certificate)) c.Log.Debugf(" cert DNS names: %v", certificate.DNSNames) c.Log.Debugf(" cert IP addresses: %v", certificate.IPAddresses) c.Log.Debugf(" cert subject: %v", certificate.Subject) @@ -466,11 +467,11 @@ func getFields(cert *x509.Certificate, now time.Time) map[string]interface{} { return fields } -func getTags(cert *x509.Certificate, location string) map[string]string { +func (c *X509Cert) getTags(cert *x509.Certificate, location string) map[string]string { tags := map[string]string{ "source": location, "common_name": cert.Subject.CommonName, - "serial_number": getSerialNumberString(cert), + "serial_number": c.getSerialNumberString(cert), "signature_algorithm": cert.SignatureAlgorithm.String(), "public_key_algorithm": cert.PublicKeyAlgorithm.String(), } @@ -524,9 +525,11 @@ func (c *X509Cert) collectCertURLs() []*url.URL { return urls } -func getSerialNumberString(cert *x509.Certificate) string { - // Rather than calling '.Text(16)' this retains leading zeroes - return hex.EncodeToString(cert.SerialNumber.Bytes()) +func (c *X509Cert) getSerialNumberString(cert *x509.Certificate) string { + if c.PadSerial { + return fmt.Sprintf("%016x", cert.SerialNumber) + } + return cert.SerialNumber.Text(16) } func init() { diff --git a/plugins/inputs/x509_cert/x509_cert_test.go b/plugins/inputs/x509_cert/x509_cert_test.go index b7b1f3a1f212b..fa3960c9909fc 100644 --- a/plugins/inputs/x509_cert/x509_cert_test.go +++ b/plugins/inputs/x509_cert/x509_cert_test.go @@ -484,10 +484,16 @@ func TestServerName(t *testing.T) { func TestCertificateSerialNumberRetainsLeadingZeroes(t *testing.T) { bi := &big.Int{} - bi.SetString("0123456789abcdef", 16) - require.Equal(t, "0123456789abcdef", getSerialNumberString(&x509.Certificate{ + bi.SetString("123456789abcdef", 16) + + plugin := &X509Cert{} + certificate := &x509.Certificate{ SerialNumber: bi, - })) + } + + require.Equal(t, "123456789abcdef", plugin.getSerialNumberString(certificate)) + plugin.PadSerial = true + require.Equal(t, "0123456789abcdef", plugin.getSerialNumberString(certificate)) } // Bases on code from