Co-authored-by: Josh Powers <powersj@fastmail.com>
This commit is contained in:
parent
0fd618ef84
commit
54c091977c
2
go.mod
2
go.mod
|
|
@ -173,6 +173,7 @@ require (
|
|||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.35.0
|
||||
go.opentelemetry.io/otel/sdk/metric v0.35.0
|
||||
go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd
|
||||
golang.org/x/crypto v0.5.0
|
||||
golang.org/x/mod v0.6.0
|
||||
golang.org/x/net v0.5.0
|
||||
golang.org/x/oauth2 v0.3.0
|
||||
|
|
@ -425,7 +426,6 @@ require (
|
|||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
|
||||
golang.org/x/time v0.1.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
|||
- issuer_common_name
|
||||
- issuer_serial_number
|
||||
- san
|
||||
- ocsp_stapled
|
||||
- ocsp_status (when ocsp_stapled=yes)
|
||||
- ocsp_verified (when ocsp_stapled=yes)
|
||||
- fields:
|
||||
- verification_code (int)
|
||||
- verification_error (string)
|
||||
|
|
@ -74,12 +77,16 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
|||
- age (int, seconds)
|
||||
- startdate (int, seconds)
|
||||
- enddate (int, seconds)
|
||||
- ocsp_status_code (int)
|
||||
- ocsp_next_update (int, seconds)
|
||||
- ocsp_produced_at (int, seconds)
|
||||
- ocsp_this_update (int, seconds)
|
||||
|
||||
## Example Output
|
||||
|
||||
```shell
|
||||
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
|
||||
x509_cert,common_name=ubuntu,ocsp_stapled=no,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,ocsp_stapled=no,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,ocsp_stapled=no,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,ocsp_stapled=yes,ocsp_status=good,ocsp_verified=yes,source=https://example.org:443,verification=valid age=400465455i,enddate=1952035200i,expiry=388452944i,ocsp_next_update=1676714398i,ocsp_produced_at=1676112480i,ocsp_status_code=0i,ocsp_this_update=1676109600i,startdate=1163116800i,verification_code=0i 1563582256000000000
|
||||
```
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pion/dtls/v2"
|
||||
"golang.org/x/crypto/ocsp"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
|
|
@ -93,7 +94,7 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
|
|||
|
||||
collectedUrls := append(c.locations, c.collectCertURLs()...)
|
||||
for _, location := range collectedUrls {
|
||||
certs, err := c.getCert(location, time.Duration(c.Timeout))
|
||||
certs, ocspresp, err := c.getCert(location, time.Duration(c.Timeout))
|
||||
if err != nil {
|
||||
acc.AddError(fmt.Errorf("cannot get SSL cert '%s': %s", location, err.Error()))
|
||||
}
|
||||
|
|
@ -141,6 +142,55 @@ func (c *X509Cert) Gather(acc telegraf.Accumulator) error {
|
|||
fields["verification_code"] = 1
|
||||
fields["verification_error"] = err.Error()
|
||||
}
|
||||
// OCSPResponse only for leaf cert
|
||||
if i == 0 && ocspresp != nil && len(*ocspresp) > 0 {
|
||||
var ocspissuer *x509.Certificate
|
||||
for _, chaincert := range certs[1:] {
|
||||
if cert.Issuer.CommonName == chaincert.Subject.CommonName &&
|
||||
cert.Issuer.SerialNumber == chaincert.Subject.SerialNumber {
|
||||
ocspissuer = chaincert
|
||||
break
|
||||
}
|
||||
}
|
||||
resp, err := ocsp.ParseResponse(*ocspresp, ocspissuer)
|
||||
if err != nil {
|
||||
if ocspissuer == nil {
|
||||
tags["ocsp_stapled"] = "no"
|
||||
fields["ocsp_error"] = err.Error()
|
||||
} else {
|
||||
ocspissuer = nil // retry parsing w/out issuer cert
|
||||
resp, err = ocsp.ParseResponse(*ocspresp, ocspissuer)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
tags["ocsp_stapled"] = "no"
|
||||
fields["ocsp_error"] = err.Error()
|
||||
} else {
|
||||
tags["ocsp_stapled"] = "yes"
|
||||
if ocspissuer != nil {
|
||||
tags["ocsp_verified"] = "yes"
|
||||
} else {
|
||||
tags["ocsp_verified"] = "no"
|
||||
}
|
||||
// resp.Status: 0=Good 1=Revoked 2=Unknown
|
||||
fields["ocsp_status_code"] = resp.Status
|
||||
switch resp.Status {
|
||||
case 0:
|
||||
tags["ocsp_status"] = "good"
|
||||
case 1:
|
||||
tags["ocsp_status"] = "revoked"
|
||||
// Status=Good: revoked_at always = -62135596800
|
||||
fields["ocsp_revoked_at"] = resp.RevokedAt.Unix()
|
||||
default:
|
||||
tags["ocsp_status"] = "unknown"
|
||||
}
|
||||
fields["ocsp_produced_at"] = resp.ProducedAt.Unix()
|
||||
fields["ocsp_this_update"] = resp.ThisUpdate.Unix()
|
||||
fields["ocsp_next_update"] = resp.NextUpdate.Unix()
|
||||
}
|
||||
} else {
|
||||
tags["ocsp_stapled"] = "no"
|
||||
}
|
||||
|
||||
acc.AddFields("x509_cert", fields, tags)
|
||||
if c.ExcludeRootCerts {
|
||||
|
|
@ -186,13 +236,13 @@ func (c *X509Cert) serverName(u *url.URL) string {
|
|||
return u.Hostname()
|
||||
}
|
||||
|
||||
func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certificate, error) {
|
||||
func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certificate, *[]byte, error) {
|
||||
protocol := u.Scheme
|
||||
switch u.Scheme {
|
||||
case "udp", "udp4", "udp6":
|
||||
ipConn, err := net.DialTimeout(u.Scheme, u.Host, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer ipConn.Close()
|
||||
|
||||
|
|
@ -204,7 +254,7 @@ func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certifica
|
|||
}
|
||||
conn, err := dtls.Client(ipConn, dtlsCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
|
|
@ -213,7 +263,7 @@ func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certifica
|
|||
for _, rawCert := range rawCerts {
|
||||
parsed, err := x509.ParseCertificate(rawCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if parsed != nil {
|
||||
|
|
@ -221,7 +271,7 @@ func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certifica
|
|||
}
|
||||
}
|
||||
|
||||
return certs, nil
|
||||
return certs, nil, nil
|
||||
case "https":
|
||||
protocol = "tcp"
|
||||
if u.Port() == "" {
|
||||
|
|
@ -231,11 +281,11 @@ func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certifica
|
|||
case "tcp", "tcp4", "tcp6":
|
||||
dialer, err := c.Proxy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ipConn, err := dialer.DialTimeout(protocol, u.Host, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer ipConn.Close()
|
||||
|
||||
|
|
@ -248,28 +298,29 @@ func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certifica
|
|||
|
||||
hsErr := conn.Handshake()
|
||||
if hsErr != nil {
|
||||
return nil, hsErr
|
||||
return nil, nil, hsErr
|
||||
}
|
||||
|
||||
certs := conn.ConnectionState().PeerCertificates
|
||||
ocspresp := conn.ConnectionState().OCSPResponse
|
||||
|
||||
return certs, nil
|
||||
return certs, &ocspresp, nil
|
||||
case "file":
|
||||
content, err := os.ReadFile(u.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
var certs []*x509.Certificate
|
||||
for {
|
||||
block, rest := pem.Decode(bytes.TrimSpace(content))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate PEM")
|
||||
return nil, nil, fmt.Errorf("failed to parse certificate PEM")
|
||||
}
|
||||
|
||||
if block.Type == "CERTIFICATE" {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
|
|
@ -278,11 +329,11 @@ func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certifica
|
|||
}
|
||||
content = rest
|
||||
}
|
||||
return certs, nil
|
||||
return certs, nil, nil
|
||||
case "smtp":
|
||||
ipConn, err := net.DialTimeout("tcp", u.Host, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer ipConn.Close()
|
||||
|
||||
|
|
@ -292,24 +343,24 @@ func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certifica
|
|||
|
||||
smtpConn, err := smtp.NewClient(ipConn, u.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = smtpConn.Hello(downloadTLSCfg.ServerName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
id, err := smtpConn.Text.Cmd("STARTTLS")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
smtpConn.Text.StartResponse(id)
|
||||
defer smtpConn.Text.EndResponse(id)
|
||||
_, _, err = smtpConn.Text.ReadResponse(220)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("did not get 220 after STARTTLS: %s", err.Error())
|
||||
return nil, nil, fmt.Errorf("did not get 220 after STARTTLS: %s", err.Error())
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(ipConn, downloadTLSCfg)
|
||||
|
|
@ -317,14 +368,15 @@ func (c *X509Cert) getCert(u *url.URL, timeout time.Duration) ([]*x509.Certifica
|
|||
|
||||
hsErr := tlsConn.Handshake()
|
||||
if hsErr != nil {
|
||||
return nil, hsErr
|
||||
return nil, nil, hsErr
|
||||
}
|
||||
|
||||
certs := tlsConn.ConnectionState().PeerCertificates
|
||||
ocspresp := tlsConn.ConnectionState().OCSPResponse
|
||||
|
||||
return certs, nil
|
||||
return certs, &ocspresp, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported scheme '%s' in location %s", u.Scheme, u.String())
|
||||
return nil, nil, fmt.Errorf("unsupported scheme '%s' in location %s", u.Scheme, u.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ func TestGatherUDPCertIntegration(t *testing.T) {
|
|||
|
||||
require.Len(t, acc.Errors, 0)
|
||||
require.True(t, acc.HasMeasurement("x509_cert"))
|
||||
require.True(t, acc.HasTag("x509_cert", "ocsp_stapled"))
|
||||
}
|
||||
|
||||
func TestGatherTCPCert(t *testing.T) {
|
||||
|
|
@ -361,6 +362,7 @@ func TestGatherCertIntegration(t *testing.T) {
|
|||
require.NoError(t, m.Gather(&acc))
|
||||
|
||||
require.True(t, acc.HasMeasurement("x509_cert"))
|
||||
require.True(t, acc.HasTag("x509_cert", "ocsp_stapled"))
|
||||
}
|
||||
|
||||
func TestGatherCertMustNotTimeoutIntegration(t *testing.T) {
|
||||
|
|
@ -379,6 +381,7 @@ func TestGatherCertMustNotTimeoutIntegration(t *testing.T) {
|
|||
require.NoError(t, m.Gather(&acc))
|
||||
require.Empty(t, acc.Errors)
|
||||
require.True(t, acc.HasMeasurement("x509_cert"))
|
||||
require.True(t, acc.HasTag("x509_cert", "ocsp_stapled"))
|
||||
}
|
||||
|
||||
func TestSourcesToURLs(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue