Skip to content

Commit

Permalink
Unify cert gathering
Browse files Browse the repository at this point in the history
  • Loading branch information
glinton committed Jul 20, 2018
1 parent 146d616 commit e53e1e7
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 74 deletions.
133 changes: 62 additions & 71 deletions plugins/inputs/x509_cert/x509_cert.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package x509_cert reports metrics from an SSL certificate.
package x509_cert

import (
Expand All @@ -7,7 +8,7 @@ import (
"fmt"
"io/ioutil"
"net"
"strings"
"net/url"
"time"

"github.com/influxdata/telegraf"
Expand All @@ -17,25 +18,25 @@ import (
)

const sampleConfig = `
## List of local SSL files
# files = ["/etc/ssl/certs/ssl-cert-snakeoil.pem"]
## List of servers
# servers = ["tcp://example.org:443"]
## List of servers and local SSL files
# sources = ["/etc/ssl/certs/ssl-cert-snakeoil.pem", "tcp://example.org:443"]
## Timeout for SSL connection
# timeout = 5
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false
`
const description = "Reads metrics from a SSL certificate"

// X509Cert holds the configuration of the plugin.
type X509Cert struct {
Servers []string `toml:"servers"`
Files []string `toml:"files"`
Sources []string `toml:"sources"`
Timeout internal.Duration `toml:"timeout"`
_tls.ClientConfig
}
Expand All @@ -50,61 +51,65 @@ func (c *X509Cert) SampleConfig() string {
return sampleConfig
}

func (c *X509Cert) getRemoteCert(server string, timeout time.Duration) (*x509.Certificate, error) {
tlsCfg, err := c.ClientConfig.TLSConfig()
func (c *X509Cert) getCert(location string, timeout time.Duration) ([]*x509.Certificate, error) {
u, err := url.Parse(location)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to parse cert location - %s\n", err.Error())
}

network := "tcp"
host_port := server
vals := strings.Split(server, "://")

if len(vals) > 1 {
network = vals[0]
host_port = vals[1]
}
switch u.Scheme {
case "https":
u.Scheme = "tcp"
fallthrough
case "udp":
fallthrough
case "tcp":
tlsCfg, err := c.ClientConfig.TLSConfig()
if err != nil {
return nil, err
}

ipConn, err := net.DialTimeout(network, host_port, timeout)
if err != nil {
return nil, err
}
defer ipConn.Close()
ipConn, err := net.DialTimeout(u.Scheme, u.Host, timeout)
if err != nil {
return nil, err
}
defer ipConn.Close()

conn := tls.Client(ipConn, tlsCfg)
defer conn.Close()
conn := tls.Client(ipConn, tlsCfg)
defer conn.Close()

hsErr := conn.Handshake()
if hsErr != nil {
return nil, hsErr
}
hsErr := conn.Handshake()
if hsErr != nil {
return nil, hsErr
}

certs := conn.ConnectionState().PeerCertificates
certs := conn.ConnectionState().PeerCertificates

if certs == nil || len(certs) < 1 {
return nil, fmt.Errorf("couldn't get remote certificate")
}
if certs == nil || len(certs) < 1 {
return nil, fmt.Errorf("couldn't get remote certificate")
}

return certs[0], nil
}
return certs, nil
case "file":
fallthrough
default:
content, err := ioutil.ReadFile(u.Path)
if err != nil {
return nil, err
}

func getLocalCert(filename string) (*x509.Certificate, error) {
content, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(content)
if block == nil {
return nil, fmt.Errorf("failed to parse certificate PEM")
}

block, _ := pem.Decode(content)
if block == nil {
return nil, fmt.Errorf("failed to parse certificate PEM")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}

cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
return []*x509.Certificate{cert}, nil
}

return cert, nil
}

func getFields(cert *x509.Certificate, now time.Time) map[string]interface{} {
Expand All @@ -129,34 +134,21 @@ func getFields(cert *x509.Certificate, now time.Time) map[string]interface{} {
func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
now := time.Now()

for _, server := range c.Servers {
cert, err := c.getRemoteCert(server, c.Timeout.Duration*time.Second)
for _, location := range c.Sources {
certs, err := c.getCert(location, c.Timeout.Duration*time.Second)
if err != nil {
return fmt.Errorf("cannot get remote SSL cert '%s': %s", server, err)
return fmt.Errorf("cannot get SSL cert '%s': %s", location, err.Error())
}

tags := map[string]string{
"server": server,
"source": location,
}

fields := getFields(cert, now)
for _, cert := range certs {
fields := getFields(cert, now)

acc.AddFields("x509_cert", fields, tags)
}

for _, file := range c.Files {
cert, err := getLocalCert(file)
if err != nil {
return fmt.Errorf("cannot get local SSL cert '%s': %s", file, err)
}

tags := map[string]string{
"file": file,
acc.AddFields("x509_cert", fields, tags)
}

fields := getFields(cert, now)

acc.AddFields("x509_cert", fields, tags)
}

return nil
Expand All @@ -165,8 +157,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
func init() {
inputs.Add("x509_cert", func() telegraf.Input {
return &X509Cert{
Files: []string{},
Servers: []string{},
Sources: []string{},
Timeout: internal.Duration{Duration: 5},
}
})
Expand Down
6 changes: 3 additions & 3 deletions plugins/inputs/x509_cert/x509_cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ func TestGatherRemote(t *testing.T) {
}()

if test.server == "" {
test.server = ln.Addr().String()
test.server = "tcp://" + ln.Addr().String()
}

sc := X509Cert{
Servers: []string{test.server},
Sources: []string{test.server},
Timeout: internal.Duration{Duration: test.timeout},
}

Expand Down Expand Up @@ -145,7 +145,7 @@ func TestGatherLocal(t *testing.T) {
defer os.Remove(f.Name())

sc := X509Cert{
Files: []string{f.Name()},
Sources: []string{f.Name()},
}

error := false
Expand Down

0 comments on commit e53e1e7

Please sign in to comment.