Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional tags for x509 Input Plugin #6686

Merged
merged 5 commits into from
Nov 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions plugins/inputs/x509_cert/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,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)
Expand Down
44 changes: 29 additions & 15 deletions plugins/inputs/x509_cert/x509_cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package x509_cert
import (
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -129,28 +128,43 @@ func getFields(cert *x509.Certificate, now time.Time) map[string]interface{} {
return fields
}

func getTags(subject pkix.Name, location string) map[string]string {
func getTags(cert *x509.Certificate, location string) map[string]string {
tags := map[string]string{
"source": location,
"common_name": subject.CommonName,
"source": location,
"common_name": cert.Subject.CommonName,
"serial_number": cert.SerialNumber.Text(16),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How large is the serial_number? Can you add unit tests with examples of all the data used by these changes?

Also, how does this compare to the cert.Subject.SerialNumber?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The certificate serial number is at most 20 bytes and this will be at most 40 Hex characters. This is supposed to be unique per CA.

On the other hand, based on what I know, cert.Subject.SerialNumber is an optional serial number that the public key owner can include. It's probably not as useful to disambiguate certificates than the certificate serial.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a new test.

"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]
}

tags["issuer_common_name"] = cert.Issuer.CommonName
tags["issuer_serial_number"] = cert.Issuer.SerialNumber

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
}

Expand All @@ -172,7 +186,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error {

for i, cert := range certs {
fields := getFields(cert, now)
tags := getTags(cert.Subject, location)
tags := getTags(cert, location)

// The first certificate is the leaf/end-entity certificate which needs DNS
// name validation against the URL hostname.
Expand Down
56 changes: 56 additions & 0 deletions plugins/inputs/x509_cert/x509_cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/base64"
"fmt"
"io/ioutil"
"math/big"
"os"
"testing"
"time"
Expand Down Expand Up @@ -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())

Expand Down