Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a new input plugin to check SSL certs #1762

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/inputs/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/snmp"
_ "github.com/influxdata/telegraf/plugins/inputs/snmp_legacy"
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
_ "github.com/influxdata/telegraf/plugins/inputs/ssl"
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
_ "github.com/influxdata/telegraf/plugins/inputs/sysstat"
_ "github.com/influxdata/telegraf/plugins/inputs/system"
Expand Down
32 changes: 32 additions & 0 deletions plugins/inputs/ssl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Check SSL Input Plugin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that ssl is the right name for this, maybe it should be x509 or cert?

Copy link
Author

@egarbi egarbi Jun 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danielnelson So, what exactly should I change? Everything? the directory, file names and the references to them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we need to change all of this.


This input plugin will return how much time (in seconds) left for a SSL cert to expire.
Warning, this check doesnt verify if SSL is valid/secure or not.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should recommend that the user use a increased interval, since 10s default would be too small.


### Configuration:

```
# SSL request given a list of servers (server:port) and a timeout
[[inputs.check_ssl]]
## Servers ( Default [] )
servers = ["github.com:443"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't use a real site as an example, since I'm sure they wouldn't appreciate being polled by Telegraf, you can use example.org instead. Please do this throughout the pull request.

What about other protocols, should this start with tcp://? Then we could support udp and tcp6

## Set timeout (default 5 seconds)
timeout = "5s"
```

### Measurements & Fields:

- ssl_cert
- time_to_expire (int) # seconds left for the SSL cert to expire
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appears to be a float, you can format this like - time_to_expire (int, seconds).

What do you think about naming this expire_seconds or similar to indicate units?


### Tags:

- All measurements have the following tags:
- server

### Example Output:

```
$ ./telegraf -config telegraf.conf -input-filter check_ssl -test
> ssl_cert,server=www.google.com:443 time_to_expire=6185474.476944118 1468864305580596685
```
100 changes: 100 additions & 0 deletions plugins/inputs/ssl/check_ssl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package ssl

import (
"crypto/tls"
"crypto/x509"
"errors"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
"net"
"time"
)

type CheckExpire struct {
// Server to check
Servers []string

// Timeout in seconds. 0 means no timeout
Timeout string
}

// Description returns the plugin Description
func (c *CheckExpire) Description() string {
return "time left until SSL cert is expired"
}

var sampleConfig = `
## server name list default [] )
servers = ["github.com:443"]
## Set timeout (default 5 seconds)
timeout = "5s"
`

// SampleConfig returns the plugin SampleConfig
func (c *CheckExpire) SampleConfig() string {
return sampleConfig
}

// Connect to server and retrieve chain certificates
func (c *CheckExpire) checkHost(server string) ([]*x509.Certificate, error) {

tout, err := time.ParseDuration(c.Timeout)
if err != nil {
return nil, err
}
//Connect network
ipConn, err := net.DialTimeout("tcp", server, tout)
if err != nil {
return nil, err
}
defer ipConn.Close()

// Configure tls to not verify if site is secure
config := tls.Config{ServerName: server, InsecureSkipVerify: true}

// Connect to tls
conn := tls.Client(ipConn, &config)
defer conn.Close()

// Handshake with TLS to get certs
hsErr := conn.Handshake()
if hsErr != nil {
return nil, hsErr
}

certs := conn.ConnectionState().PeerCertificates

if certs == nil || len(certs) < 1 {
return nil, errors.New("Could not get server's certificate from the TLS connection.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include the name of the server that caused this.

}
return certs, nil

}

// Gather gets all metric fields and tags and returns any errors it encounters
func (c *CheckExpire) Gather(acc telegraf.Accumulator) error {
for _, server := range c.Servers {
// Prepare data
tags := map[string]string{"server": server}
// Gather data
var timeToExpire time.Duration
timeNow := time.Now()
certs, err := c.checkHost(server)
acc.AddError(err)
if err != nil {
timeToExpire = 0
} else {
timeToExpire = certs[0].NotAfter.Sub(timeNow)
}
fields := map[string]interface{}{"time_to_expire": timeToExpire.Seconds()}
// Add metrics
acc.AddFields("ssl_cert", fields, tags)
}
return nil
}

func init() {
inputs.Add("check_ssl", func() telegraf.Input {
return &CheckExpire{Timeout: "5s"}
})
}
66 changes: 66 additions & 0 deletions plugins/inputs/ssl/check_ssl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package ssl

import (
"testing"
"time"

"github.com/influxdata/telegraf/testutil"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var servers = []string{"github.com:443"}
var server_of_timeout = []string{"8.8.8.8:443"}
var tout = "1s"

func TestGathering(t *testing.T) {
if testing.Short() {
t.Skip("Skipping network-dependent test in short mode.")
}
var sslConfig = CheckExpire{
Servers: servers,
Timeout: tout,
}
var acc testutil.Accumulator
tags := map[string]string{
"server": "github.com:443",
}
fields := map[string]interface{}{}

err := sslConfig.Gather(&acc)
assert.NoError(t, err)
metric, ok := acc.Get("ssl_cert")
require.True(t, ok)
expireTime, _ := metric.Fields["time_to_expire"]

assert.NotEqual(t, 0, expireTime)
fields["time_to_expire"] = expireTime
acc.AssertContainsTaggedFields(t, "ssl_cert", fields, tags)
}

func TestGatheringTimeout(t *testing.T) {
if testing.Short() {
t.Skip("Skipping network-dependent test in short mode.")
}
var sslConfig = CheckExpire{
Servers: server_of_timeout,
Timeout: tout,
}
var acc testutil.Accumulator
var err error

channel := make(chan error, 1)
go func() {
channel <- sslConfig.Gather(&acc)
}()
select {
case res := <-channel:
err = res
case <-time.After(time.Second * 5):
err = nil
}

assert.Error(t, err)
assert.Contains(t, err.Error(), "i/o timeout")
}