Add certificate verification status to x509_cert input (#6143)
This commit is contained in:
parent
92cabcd323
commit
3e50db904a
|
@ -19,9 +19,6 @@ file or network connection.
|
|||
# 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
|
||||
```
|
||||
|
||||
|
||||
|
@ -35,7 +32,10 @@ file or network connection.
|
|||
- country
|
||||
- province
|
||||
- locality
|
||||
- verification
|
||||
- fields:
|
||||
- verification_code (int)
|
||||
- verification_error (string)
|
||||
- expiry (int, seconds)
|
||||
- age (int, seconds)
|
||||
- startdate (int, seconds)
|
||||
|
@ -45,6 +45,8 @@ file or network connection.
|
|||
### Example output
|
||||
|
||||
```
|
||||
x509_cert,host=myhost,source=https://example.org age=1753627i,expiry=5503972i,startdate=1516092060i,enddate=1523349660i 1517845687000000000
|
||||
x509_cert,host=myhost,source=/etc/ssl/certs/ssl-cert-snakeoil.pem age=7522207i,expiry=308002732i,startdate=1510323480i,enddate=1825848420i 1517845687000000000
|
||||
x509_cert,common_name=ubuntu,source=/etc/ssl/certs/ssl-cert-snakeoil.pem,verification=valid age=7693222i,enddate=1871249033i,expiry=307666777i,startdate=1555889033i,verification_code=0i 1563582256000000000
|
||||
x509_cert,common_name=www.example.org,country=US,locality=Los\ Angeles,organization=Internet\ Corporation\ for\ Assigned\ Names\ and\ Numbers,organizational_unit=Technology,province=California,source=https://example.org:443,verification=invalid age=20219055i,enddate=1606910400i,expiry=43328144i,startdate=1543363200i,verification_code=1i,verification_error="x509: certificate signed by unknown authority" 1563582256000000000
|
||||
x509_cert,common_name=DigiCert\ SHA2\ Secure\ Server\ CA,country=US,organization=DigiCert\ Inc,source=https://example.org:443,verification=valid age=200838255i,enddate=1678276800i,expiry=114694544i,startdate=1362744000i,verification_code=0i 1563582256000000000
|
||||
x509_cert,common_name=DigiCert\ Global\ Root\ CA,country=US,organization=DigiCert\ Inc,organizational_unit=www.digicert.com,source=https://example.org:443,verification=valid age=400465455i,enddate=1952035200i,expiry=388452944i,startdate=1163116800i,verification_code=0i 1563582256000000000
|
||||
```
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[[inputs.x509_cert]]
|
||||
sources = ["https://www.influxdata.com:443"]
|
||||
sources = ["https://expired.badssl.com:443", "https://wrong.host.badssl.com:443"]
|
||||
|
||||
[[outputs.file]]
|
||||
files = ["stdout"]
|
||||
|
|
|
@ -30,9 +30,6 @@ const sampleConfig = `
|
|||
# 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"
|
||||
|
||||
|
@ -40,6 +37,7 @@ const description = "Reads metrics from a SSL certificate"
|
|||
type X509Cert struct {
|
||||
Sources []string `toml:"sources"`
|
||||
Timeout internal.Duration `toml:"timeout"`
|
||||
tlsCfg *tls.Config
|
||||
_tls.ClientConfig
|
||||
}
|
||||
|
||||
|
@ -53,16 +51,20 @@ func (c *X509Cert) SampleConfig() string {
|
|||
return sampleConfig
|
||||
}
|
||||
|
||||
func (c *X509Cert) getCert(location string, timeout time.Duration) ([]*x509.Certificate, error) {
|
||||
func (c *X509Cert) locationToURL(location string) (*url.URL, error) {
|
||||
if strings.HasPrefix(location, "/") {
|
||||
location = "file://" + location
|
||||
}
|
||||
|
||||
u, err := url.Parse(location)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse cert location - %s\n", err.Error())
|
||||
return nil, fmt.Errorf("failed to parse cert location - %s", err.Error())
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certificate, error) {
|
||||
switch u.Scheme {
|
||||
case "https":
|
||||
u.Scheme = "tcp"
|
||||
|
@ -70,22 +72,15 @@ func (c *X509Cert) getCert(location string, timeout time.Duration) ([]*x509.Cert
|
|||
case "udp", "udp4", "udp6":
|
||||
fallthrough
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
tlsCfg, err := c.ClientConfig.TLSConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ipConn, err := net.DialTimeout(u.Scheme, u.Host, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ipConn.Close()
|
||||
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &tls.Config{}
|
||||
}
|
||||
tlsCfg.ServerName = u.Hostname()
|
||||
conn := tls.Client(ipConn, tlsCfg)
|
||||
c.tlsCfg.ServerName = u.Hostname()
|
||||
c.tlsCfg.InsecureSkipVerify = true
|
||||
conn := tls.Client(ipConn, c.tlsCfg)
|
||||
defer conn.Close()
|
||||
|
||||
hsErr := conn.Handshake()
|
||||
|
@ -114,7 +109,7 @@ func (c *X509Cert) getCert(location string, timeout time.Duration) ([]*x509.Cert
|
|||
|
||||
return []*x509.Certificate{cert}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsuported scheme '%s' in location %s\n", u.Scheme, location)
|
||||
return nil, fmt.Errorf("unsuported scheme '%s' in location %s", u.Scheme, u.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,15 +159,41 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
|
|||
now := time.Now()
|
||||
|
||||
for _, location := range c.Sources {
|
||||
certs, err := c.getCert(location, c.Timeout.Duration*time.Second)
|
||||
u, err := c.locationToURL(location)
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
certs, err := c.getCert(u, c.Timeout.Duration*time.Second)
|
||||
if err != nil {
|
||||
acc.AddError(fmt.Errorf("cannot get SSL cert '%s': %s", location, err.Error()))
|
||||
}
|
||||
|
||||
for _, cert := range certs {
|
||||
for i, cert := range certs {
|
||||
fields := getFields(cert, now)
|
||||
tags := getTags(cert.Subject, location)
|
||||
|
||||
// The first certificate is the leaf/end-entity certificate which needs DNS
|
||||
// name validation against the URL hostname.
|
||||
opts := x509.VerifyOptions{}
|
||||
if i == 0 {
|
||||
opts.DNSName = u.Hostname()
|
||||
}
|
||||
if c.tlsCfg.RootCAs != nil {
|
||||
opts.Roots = c.tlsCfg.RootCAs
|
||||
}
|
||||
|
||||
_, err = cert.Verify(opts)
|
||||
if err == nil {
|
||||
tags["verification"] = "valid"
|
||||
fields["verification_code"] = 0
|
||||
} else {
|
||||
tags["verification"] = "invalid"
|
||||
fields["verification_code"] = 1
|
||||
fields["verification_error"] = err.Error()
|
||||
}
|
||||
|
||||
acc.AddFields("x509_cert", fields, tags)
|
||||
}
|
||||
}
|
||||
|
@ -180,6 +201,20 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *X509Cert) Init() error {
|
||||
tlsCfg, err := c.ClientConfig.TLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &tls.Config{}
|
||||
}
|
||||
|
||||
c.tlsCfg = tlsCfg
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("x509_cert", func() telegraf.Input {
|
||||
return &X509Cert{
|
||||
|
|
|
@ -110,6 +110,7 @@ func TestGatherRemote(t *testing.T) {
|
|||
Sources: []string{test.server},
|
||||
Timeout: internal.Duration{Duration: test.timeout},
|
||||
}
|
||||
sc.Init()
|
||||
|
||||
sc.InsecureSkipVerify = true
|
||||
testErr := false
|
||||
|
@ -169,6 +170,7 @@ func TestGatherLocal(t *testing.T) {
|
|||
sc := X509Cert{
|
||||
Sources: []string{f.Name()},
|
||||
}
|
||||
sc.Init()
|
||||
|
||||
error := false
|
||||
|
||||
|
@ -218,6 +220,7 @@ func TestGatherChain(t *testing.T) {
|
|||
sc := X509Cert{
|
||||
Sources: []string{f.Name()},
|
||||
}
|
||||
sc.Init()
|
||||
|
||||
error := false
|
||||
|
||||
|
@ -237,6 +240,7 @@ func TestGatherChain(t *testing.T) {
|
|||
|
||||
func TestStrings(t *testing.T) {
|
||||
sc := X509Cert{}
|
||||
sc.Init()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -265,6 +269,7 @@ func TestGatherCert(t *testing.T) {
|
|||
m := &X509Cert{
|
||||
Sources: []string{"https://www.influxdata.com:443"},
|
||||
}
|
||||
m.Init()
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := m.Gather(&acc)
|
||||
|
|
Loading…
Reference in New Issue