Wildcard support for x509_cert files (#6952)
* 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 <aleberre@vente-privee.com>
This commit is contained in:
parent
f267f342ae
commit
b2b361356e
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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.
|
||||
|
|
@ -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())
|
||||
}
|
||||
|
||||
c.locations = append(c.locations, u)
|
||||
}
|
||||
}
|
||||
|
||||
u, err := url.Parse(location)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse cert location - %s", err.Error())
|
||||
}
|
||||
|
||||
return u, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *X509Cert) serverName(u *url.URL) (string, error) {
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue