Skip to content

Commit

Permalink
Wildcard support for x509_cert files (influxdata#6952)
Browse files Browse the repository at this point in the history
* Accept standard unix glob matching rules

* comply with indentation

* update readme

* move globing expand and url parsing into Init()

* chore: rebase branch on upstream master

* rename refreshFilePaths to expandFilePaths
* expandFilePaths handles '/path/to/*.pem' and 'files:///path/to/*.pem'
* update sample config

* fix: recompile files globing pattern at every gather tic

 * add var globFilePathsToUrls to stack files path
 * add var globpaths to stack compiled globpath
 * rework sourcesToURLs to compile files path and stack them
 * rename expandFilePaths to expandFilePathsToUrls
 * rework expandFilePathsToUrls to only match compiled globpath
 * rework the `Gather` ticker to match globpath at each call

* fix: comply with requested changes

 * add specifics regarding relative paths in sample config
 * add logger and use it in expandFilePathsToUrls()
 * precompile glob for `files://`, `/` and `://`

* fix: update README to match last changes

* fix: comply with last requested changes

 * rename expandFilePathsToUrls() to collectCertURLs()
 * collectCertURLs() now returns []*url.URL to avoid extra field
globFilePathsToUrls in structure
 * update the Gather() ticker accordingly

* fix(windows): do not try to compile glopath for windows path as it's not supposed to be supported by the OS

* fix(ci): apply go fmt

* fix(ci): empty-lines/import-shadowing

Co-authored-by: Anthony LE BERRE <[email protected]>
  • Loading branch information
jaroug and Anthony LE BERRE authored Mar 23, 2021
1 parent f267f34 commit b2b3613
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 24 deletions.
6 changes: 4 additions & 2 deletions plugins/inputs/x509_cert/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ file or network connection.
```toml
# Reads metrics from a SSL certificate
[[inputs.x509_cert]]
## List certificate sources
sources = ["/etc/ssl/certs/ssl-cert-snakeoil.pem", "https://example.org:443"]
## 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",
"/etc/mycerts/*.mydomain.org.pem", "file:///path/to/*.pem"]

## Timeout for SSL connection
# timeout = "5s"
Expand Down
86 changes: 64 additions & 22 deletions plugins/inputs/x509_cert/x509_cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ import (

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/internal/globpath"
_tls "github.com/influxdata/telegraf/plugins/common/tls"
"github.com/influxdata/telegraf/plugins/inputs"
)

const sampleConfig = `
## List certificate sources
sources = ["/etc/ssl/certs/ssl-cert-snakeoil.pem", "tcp://example.org:443"]
## Prefix your entry with 'file://' if you intend to use relative paths
sources = ["/etc/ssl/certs/ssl-cert-snakeoil.pem", "tcp://example.org:443",
"/etc/mycerts/*.mydomain.org.pem", "file:///path/to/*.pem"]
## Timeout for SSL connection
# timeout = "5s"
Expand All @@ -45,6 +48,9 @@ type X509Cert struct {
ServerName string `toml:"server_name"`
tlsCfg *tls.Config
_tls.ClientConfig
locations []*url.URL
globpaths []*globpath.GlobPath
Log telegraf.Logger
}

// Description returns description of the plugin.
Expand All @@ -57,20 +63,31 @@ func (c *X509Cert) SampleConfig() string {
return sampleConfig
}

func (c *X509Cert) locationToURL(location string) (*url.URL, error) {
if strings.HasPrefix(location, "/") {
location = "file://" + location
}
if strings.Index(location, ":\\") == 1 {
location = "file://" + filepath.ToSlash(location)
}
func (c *X509Cert) sourcesToURLs() error {
for _, source := range c.Sources {
if strings.HasPrefix(source, "file://") ||
strings.HasPrefix(source, "/") ||
strings.Index(source, ":\\") != 1 {
source = filepath.ToSlash(strings.TrimPrefix(source, "file://"))
g, err := globpath.Compile(source)
if err != nil {
return fmt.Errorf("could not compile glob %v: %v", source, err)
}
c.globpaths = append(c.globpaths, g)
} else {
if strings.Index(source, ":\\") == 1 {
source = "file://" + filepath.ToSlash(source)
}
u, err := url.Parse(source)
if err != nil {
return fmt.Errorf("failed to parse cert location - %s", err.Error())
}

u, err := url.Parse(location)
if err != nil {
return nil, fmt.Errorf("failed to parse cert location - %s", err.Error())
c.locations = append(c.locations, u)
}
}

return u, nil
return nil
}

func (c *X509Cert) serverName(u *url.URL) (string, error) {
Expand Down Expand Up @@ -204,25 +221,45 @@ func getTags(cert *x509.Certificate, location string) map[string]string {
return tags
}

func (c *X509Cert) collectCertURLs() ([]*url.URL, error) {
var urls []*url.URL

for _, path := range c.globpaths {
files := path.Match()
if len(files) <= 0 {
c.Log.Errorf("could not find file: %v", path)
continue
}
for _, file := range files {
file = "file://" + file
u, err := url.Parse(file)
if err != nil {
return urls, fmt.Errorf("failed to parse cert location - %s", err.Error())
}
urls = append(urls, u)
}
}

return urls, nil
}

// Gather adds metrics into the accumulator.
func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
now := time.Now()
collectedUrls, err := c.collectCertURLs()
if err != nil {
acc.AddError(fmt.Errorf("cannot get file: %s", err.Error()))
}

for _, location := range c.Sources {
u, err := c.locationToURL(location)
if err != nil {
acc.AddError(err)
return nil
}

certs, err := c.getCert(u, c.Timeout.Duration)
for _, location := range append(c.locations, collectedUrls...) {
certs, err := c.getCert(location, c.Timeout.Duration*time.Second)
if err != nil {
acc.AddError(fmt.Errorf("cannot get SSL cert '%s': %s", location, err.Error()))
}

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

// The first certificate is the leaf/end-entity certificate which needs DNS
// name validation against the URL hostname.
Expand All @@ -231,7 +268,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
}
if i == 0 {
opts.DNSName, err = c.serverName(u)
opts.DNSName, err = c.serverName(location)
if err != nil {
return err
}
Expand Down Expand Up @@ -263,6 +300,11 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
}

func (c *X509Cert) Init() error {
err := c.sourcesToURLs()
if err != nil {
return err
}

tlsCfg, err := c.ClientConfig.TLSConfig()
if err != nil {
return err
Expand Down

0 comments on commit b2b3613

Please sign in to comment.