sensu-plugins-ssl-boutetnico 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE +22 -0
- data/README.md +78 -0
- data/bin/check-java-keystore-cert.rb +88 -0
- data/bin/check-ssl-anchor.rb +120 -0
- data/bin/check-ssl-cert.rb +130 -0
- data/bin/check-ssl-crl.rb +76 -0
- data/bin/check-ssl-host.rb +198 -0
- data/bin/check-ssl-hsts-preloadable.rb +79 -0
- data/bin/check-ssl-hsts-status.rb +101 -0
- data/bin/check-ssl-qualys.rb +193 -0
- data/bin/check-ssl-root-issuer.rb +126 -0
- data/lib/sensu-plugins-ssl.rb +1 -0
- data/lib/sensu-plugins-ssl/version.rb +9 -0
- metadata +229 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 16378e5222efd774d58d33c47dfa46facac0b2cd
|
4
|
+
data.tar.gz: 55dd9684c413e0b36ed44e6a89e83a571a0742d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cebae6d1239bcae312147ce8f4df7acc5bb01698c39da3a4f5ff351031fde0ab2e0fe16f54a49d980db5d80731f8a1020a64a74ce0a230b8283a17323ac5f53a
|
7
|
+
data.tar.gz: f1f9c483b7dc9e4769651de62277b2abaa9d6b1571cdf6f5cf2469acab557746bd344832ad8c2aaecdc654ae9082265304738ad312f8f8da216567c24595b0e5
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Can be found at [https://github.com/boutetnico/sensu-plugins-ssl/releases](https://github.com/boutetnico/sensu-plugins-ssl/releases).
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Sensu-Plugins
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
## Sensu-Plugins-SSL
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/sensu-plugins-ssl-boutetnico.svg)](https://badge.fury.io/rb/sensu-plugins-ssl-boutetnico.svg)
|
4
|
+
[![Sensu Bonsai Asset](https://img.shields.io/badge/Bonsai-Download%20Me-brightgreen.svg?colorB=89C967&logo=sensu)](https://bonsai.sensu.io/assets/boutetnico/sensu-plugins-ssl)
|
5
|
+
|
6
|
+
## This is an unofficial fork
|
7
|
+
|
8
|
+
This fork is automatically tested, built and published to [RubyGems](https://rubygems.org/gems/sensu-plugins-ssl-boutetnico/) and [Bonsai](https://bonsai.sensu.io/assets/boutetnico/sensu-plugins-ssl).
|
9
|
+
|
10
|
+
## Files
|
11
|
+
* bin/check-java-keystore-cert.rb
|
12
|
+
* bin/check-ssl-anchor.rb
|
13
|
+
* bin/check-ssl-crl.rb
|
14
|
+
* bin/check-ssl-cert.rb
|
15
|
+
* bin/check-ssl-host.rb
|
16
|
+
* bin/check-ssl-hsts-preload.rb
|
17
|
+
* bin/check-ssl-hsts-preloadable.rb
|
18
|
+
* bin/check-ssl-qualys.rb
|
19
|
+
* bin/check-ssl-root-issuer.rb
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### `bin/check-ssl-anchor.rb`
|
24
|
+
|
25
|
+
Check that a specific website is chained to a specific root certificate (Let's Encrypt for instance). Requires the `openssl` commandline tool to be available on the system.
|
26
|
+
|
27
|
+
```
|
28
|
+
./bin/check-ssl-anchor.rb -u example.com -a "i:/O=Digital Signature Trust Co./CN=DST Root CA X3"
|
29
|
+
```
|
30
|
+
|
31
|
+
### `bin/check-ssl-crl.rb`
|
32
|
+
|
33
|
+
Checks a CRL has not or is not expiring by inspecting it's next update value.
|
34
|
+
|
35
|
+
You can check against a CRL file on disk:
|
36
|
+
|
37
|
+
```
|
38
|
+
./bin/check-ssl-crl -c 300 -w 600 -u /path/to/crl
|
39
|
+
```
|
40
|
+
|
41
|
+
or an online CRL:
|
42
|
+
|
43
|
+
```
|
44
|
+
./bin/check-ssl-crl -c 300 -w 600 -u http://www.website.com/file.crl
|
45
|
+
```
|
46
|
+
|
47
|
+
Critical and Warning thresholds are specified in minutes.
|
48
|
+
|
49
|
+
### `bin/check-ssl-qualys.rb`
|
50
|
+
|
51
|
+
Checks the ssllabs qualysis api for grade of your server, this check can be quite long so it should not be scheduled with a low interval and will probably need to adjust the check `timeout` options per the [check attributes spec](https://docs.sensu.io/sensu-core/1.2/reference/checks/#check-attributes) based on my tests you should expect this to take around 3 minutes.
|
52
|
+
```
|
53
|
+
./bin/check-ssl-qualys.rb -d google.com
|
54
|
+
```
|
55
|
+
|
56
|
+
### `bin/check-ssl-root-issuer.rb`
|
57
|
+
|
58
|
+
Check that a specific website is chained to a specific root certificate issuer. This is a pure Ruby implementation, does not require the openssl cmdline client tool to be installed.
|
59
|
+
|
60
|
+
```
|
61
|
+
./bin/check-ssl-root-issuer.rb -u example.com -a "CN=DST Root CA X3,O=Digital Signature Trust Co."
|
62
|
+
```
|
63
|
+
|
64
|
+
## Installation
|
65
|
+
|
66
|
+
[Installation and Setup](http://sensu-plugins.io/docs/installation_instructions.html)
|
67
|
+
|
68
|
+
## Testing
|
69
|
+
|
70
|
+
To run the testing suite, you'll need to have a working `ruby` environment, `gem`, and `bundler` installed. We use `rake` to run the `rspec` tests automatically.
|
71
|
+
|
72
|
+
bundle install
|
73
|
+
bundle update
|
74
|
+
bundle exec rake
|
75
|
+
|
76
|
+
## Notes
|
77
|
+
|
78
|
+
`bin/check-ssl-anchor.rb` and `bin/check-ssl-host.rb` would be good to run in combination with each other to test that the chain is anchored to a specific certificate and each certificate in the chain is correctly signed.
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# check-java-keystore-cert
|
4
|
+
#
|
5
|
+
# DESCRIPTION:
|
6
|
+
# Check when a certificate stored in a Java Keystore will expire
|
7
|
+
#
|
8
|
+
# OUTPUT:
|
9
|
+
# plain text
|
10
|
+
#
|
11
|
+
# PLATFORMS:
|
12
|
+
# Linux
|
13
|
+
#
|
14
|
+
# DEPENDENCIES:
|
15
|
+
# gem: sensu-plugin
|
16
|
+
#
|
17
|
+
# USAGE:
|
18
|
+
# example commands
|
19
|
+
#
|
20
|
+
# NOTES:
|
21
|
+
# Does it behave differently on specific platforms, specific use cases, etc
|
22
|
+
#
|
23
|
+
|
24
|
+
require 'date'
|
25
|
+
require 'shellwords'
|
26
|
+
require 'sensu-plugin/check/cli'
|
27
|
+
|
28
|
+
class CheckJavaKeystoreCert < Sensu::Plugin::Check::CLI
|
29
|
+
option :path,
|
30
|
+
long: '--path PATH',
|
31
|
+
description: '',
|
32
|
+
required: true
|
33
|
+
|
34
|
+
option :alias,
|
35
|
+
long: '--alias ALIAS',
|
36
|
+
description: '',
|
37
|
+
required: true
|
38
|
+
|
39
|
+
option :password,
|
40
|
+
long: '--password PASSWORD',
|
41
|
+
description: '',
|
42
|
+
required: true
|
43
|
+
|
44
|
+
option :warning,
|
45
|
+
long: '--warning DAYS',
|
46
|
+
description: '',
|
47
|
+
proc: proc { |v| v.to_i },
|
48
|
+
required: true
|
49
|
+
|
50
|
+
option :critical,
|
51
|
+
long: '--critical DAYS',
|
52
|
+
description: '',
|
53
|
+
proc: proc { |v| v.to_i },
|
54
|
+
required: true
|
55
|
+
|
56
|
+
def certificate_expiration_date
|
57
|
+
result = `keytool -keystore #{Shellwords.escape(config[:path])} \
|
58
|
+
-export -alias #{Shellwords.escape(config[:alias])} \
|
59
|
+
-storepass #{Shellwords.escape(config[:password])} -rfc 2>&1 | \
|
60
|
+
openssl x509 -enddate -noout 2>&1`
|
61
|
+
|
62
|
+
# rubocop:disable Style/SpecialGlobalVars
|
63
|
+
unknown 'could not get certificate from keystore' unless $?.success?
|
64
|
+
# rubocop:enable Style/SpecialGlobalVars
|
65
|
+
|
66
|
+
Date.parse(result.split('=').last)
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_opts
|
70
|
+
unknown 'warning cannot be less than critical' if config[:warning] < config[:critical]
|
71
|
+
end
|
72
|
+
|
73
|
+
def run
|
74
|
+
validate_opts
|
75
|
+
|
76
|
+
days_until = (certificate_expiration_date - Date.today).to_i
|
77
|
+
|
78
|
+
if days_until.negative?
|
79
|
+
critical "Expired #{days_until.abs} days ago"
|
80
|
+
elsif days_until < config[:critical]
|
81
|
+
critical "#{days_until} days left"
|
82
|
+
elsif days_until < config[:warning]
|
83
|
+
warning "#{days_until} days left"
|
84
|
+
end
|
85
|
+
|
86
|
+
ok "#{days_until} days left"
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# check-ssl-anchor
|
4
|
+
#
|
5
|
+
# DESCRIPTION:
|
6
|
+
# Check that a certificate is chained to a specific root certificate
|
7
|
+
#
|
8
|
+
# OUTPUT:
|
9
|
+
# plain text
|
10
|
+
#
|
11
|
+
# PLATFORMS:
|
12
|
+
# Linux
|
13
|
+
#
|
14
|
+
# DEPENDENCIES:
|
15
|
+
# gem: sensu-plugin
|
16
|
+
#
|
17
|
+
# USAGE:
|
18
|
+
#
|
19
|
+
# Check that a specific website is chained to a specific root certificate (Let's Encrypt for instance)
|
20
|
+
# ./check-ssl-anchor.rb \
|
21
|
+
# -u example.com \
|
22
|
+
# -a "i:/O=Digital Signature Trust Co./CN=DST Root CA X3"
|
23
|
+
#
|
24
|
+
# NOTES:
|
25
|
+
# This is basically a ruby wrapper around the following openssl command.
|
26
|
+
#
|
27
|
+
# openssl s_client -connect example.com:443 -servername example.com
|
28
|
+
#
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# Use the -s flag if you need to override SNI (Server Name Indication). If you
|
32
|
+
# are seeing discrepencies between `openssl s_client` and browser, that's a good
|
33
|
+
# indication to use this flag.
|
34
|
+
#
|
35
|
+
# LICENSE:
|
36
|
+
# Copyright 2017 Phil Porada <philporada@gmail.com>
|
37
|
+
#
|
38
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
39
|
+
# for details.
|
40
|
+
#
|
41
|
+
|
42
|
+
require 'sensu-plugin/check/cli'
|
43
|
+
|
44
|
+
#
|
45
|
+
# Check certificate is anchored to a specific root
|
46
|
+
#
|
47
|
+
class CheckSSLAnchor < Sensu::Plugin::Check::CLI
|
48
|
+
option :host,
|
49
|
+
description: 'Host to check',
|
50
|
+
short: '-h',
|
51
|
+
long: '--host HOST',
|
52
|
+
required: true
|
53
|
+
|
54
|
+
option :anchor,
|
55
|
+
description: 'An anchor looks something like /O=Digital Signature Trust Co./CN=DST Root CA X3',
|
56
|
+
short: '-a',
|
57
|
+
long: '--anchor ANCHOR_VAL',
|
58
|
+
required: true
|
59
|
+
|
60
|
+
option :regexp,
|
61
|
+
description: 'Treat the anchor as a regexp',
|
62
|
+
short: '-r',
|
63
|
+
long: '--regexp',
|
64
|
+
default: false,
|
65
|
+
boolean: true,
|
66
|
+
required: false
|
67
|
+
|
68
|
+
option :servername,
|
69
|
+
description: 'Set the TLS SNI (Server Name Indication) extension',
|
70
|
+
short: '-s',
|
71
|
+
long: '--servername SERVER'
|
72
|
+
|
73
|
+
option :port,
|
74
|
+
description: 'Port on server to check',
|
75
|
+
short: '-p',
|
76
|
+
long: '--port PORT',
|
77
|
+
default: 443
|
78
|
+
|
79
|
+
def validate_opts
|
80
|
+
config[:servername] = config[:host] unless config[:servername]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Do the actual work and massage some data
|
84
|
+
def anchor_information
|
85
|
+
data = `openssl s_client \
|
86
|
+
-connect #{config[:host]}:#{config[:port]} \
|
87
|
+
-servername #{config[:servername]} < /dev/null 2>&1`.match(/Certificate chain(.*)---\nServer certificate/m)[1].split(/$/).map(&:strip)
|
88
|
+
data = data.reject(&:empty?)
|
89
|
+
|
90
|
+
unless data[0] =~ /0 s:\/?CN ?=.*/m
|
91
|
+
data = 'NOTOK'
|
92
|
+
end
|
93
|
+
data
|
94
|
+
end
|
95
|
+
|
96
|
+
def run
|
97
|
+
validate_opts
|
98
|
+
data = anchor_information
|
99
|
+
if data == 'NOTOK'
|
100
|
+
critical 'An error was encountered while trying to retrieve the certificate chain.'
|
101
|
+
end
|
102
|
+
puts config[:regexp]
|
103
|
+
# rubocop:disable Style/IfInsideElse
|
104
|
+
if config[:regexp]
|
105
|
+
anchor_regexp = Regexp.new(config[:anchor].to_s)
|
106
|
+
if data[-1] =~ anchor_regexp
|
107
|
+
ok 'Root anchor has been found.'
|
108
|
+
else
|
109
|
+
critical 'Root anchor did not match regexp /' + config[:anchor].to_s + "/\nFound \"" + data[-1] + '" instead.'
|
110
|
+
end
|
111
|
+
else
|
112
|
+
if data[-1] == config[:anchor].to_s
|
113
|
+
ok 'Root anchor has been found.'
|
114
|
+
else
|
115
|
+
critical 'Root anchor did not match string "' + config[:anchor].to_s + "\"\nFound \"" + data[-1] + '" instead.'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
# rubocop:enable Style/IfInsideElse
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# check-ssl-cert
|
4
|
+
#
|
5
|
+
# DESCRIPTION:
|
6
|
+
# Check when a SSL certificate will expire.
|
7
|
+
#
|
8
|
+
# OUTPUT:
|
9
|
+
# plain text
|
10
|
+
#
|
11
|
+
# PLATFORMS:
|
12
|
+
# Linux
|
13
|
+
#
|
14
|
+
# DEPENDENCIES:
|
15
|
+
# gem: sensu-plugin
|
16
|
+
#
|
17
|
+
# USAGE:
|
18
|
+
# example commands
|
19
|
+
#
|
20
|
+
# NOTES:
|
21
|
+
# Does it behave differently on specific platforms, specific use cases, etc
|
22
|
+
#
|
23
|
+
# LICENSE:
|
24
|
+
# Jean-Francois Theroux <me@failshell.io>
|
25
|
+
# Nathan Williams <nath.e.will@gmail.com>
|
26
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
27
|
+
# for details.
|
28
|
+
#
|
29
|
+
|
30
|
+
require 'date'
|
31
|
+
require 'openssl'
|
32
|
+
require 'sensu-plugin/check/cli'
|
33
|
+
|
34
|
+
#
|
35
|
+
# Check SSL Cert
|
36
|
+
#
|
37
|
+
class CheckSSLCert < Sensu::Plugin::Check::CLI
|
38
|
+
option :critical,
|
39
|
+
description: 'Numbers of days left',
|
40
|
+
short: '-c',
|
41
|
+
long: '--critical DAYS',
|
42
|
+
required: true
|
43
|
+
|
44
|
+
option :warning,
|
45
|
+
description: 'Numbers of days left',
|
46
|
+
short: '-w',
|
47
|
+
long: '--warning DAYS',
|
48
|
+
required: true
|
49
|
+
|
50
|
+
option :pem,
|
51
|
+
description: 'Path to PEM file',
|
52
|
+
short: '-P',
|
53
|
+
long: '--pem PEM'
|
54
|
+
|
55
|
+
option :host,
|
56
|
+
description: 'Host to validate',
|
57
|
+
short: '-h',
|
58
|
+
long: '--host HOST'
|
59
|
+
|
60
|
+
option :port,
|
61
|
+
description: 'Port to validate',
|
62
|
+
short: '-p',
|
63
|
+
long: '--port PORT'
|
64
|
+
|
65
|
+
option :servername,
|
66
|
+
description: 'Set the TLS SNI (Server Name Indication) extension',
|
67
|
+
short: '-s',
|
68
|
+
long: '--servername SERVER'
|
69
|
+
|
70
|
+
option :pkcs12,
|
71
|
+
description: 'Path to PKCS#12 certificate',
|
72
|
+
short: '-C',
|
73
|
+
long: '--cert P12'
|
74
|
+
|
75
|
+
option :pass,
|
76
|
+
description: 'Pass phrase for the private key in PKCS#12 certificate',
|
77
|
+
short: '-S',
|
78
|
+
long: '--pass '
|
79
|
+
|
80
|
+
def ssl_cert_expiry
|
81
|
+
`openssl s_client -servername #{config[:servername]} -connect #{config[:host]}:#{config[:port]} < /dev/null 2>&1 | openssl x509 -enddate -noout`.split('=').last
|
82
|
+
end
|
83
|
+
|
84
|
+
def ssl_pem_expiry
|
85
|
+
OpenSSL::X509::Certificate.new(File.read config[:pem]).not_after # rubocop:disable Style/NestedParenthesizedCalls
|
86
|
+
end
|
87
|
+
|
88
|
+
def ssl_pkcs12_expiry
|
89
|
+
`openssl pkcs12 -in #{config[:pkcs12]} -nokeys -nomacver -passin pass:"#{config[:pass]}" | openssl x509 -noout -enddate | grep -v MAC`.split('=').last
|
90
|
+
end
|
91
|
+
|
92
|
+
def validate_opts
|
93
|
+
if !config[:pem] && !config[:pkcs12]
|
94
|
+
unknown 'Host and port required' unless config[:host] && config[:port]
|
95
|
+
elsif config[:pem]
|
96
|
+
unknown 'No such cert' unless File.exist? config[:pem]
|
97
|
+
elsif config[:pkcs12]
|
98
|
+
if !config[:pass]
|
99
|
+
unknown 'No pass phrase specified for PKCS#12 certificate'
|
100
|
+
else
|
101
|
+
unknown 'No such cert' unless File.exist? config[:pkcs12]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
config[:servername] = config[:host] unless config[:servername]
|
105
|
+
end
|
106
|
+
|
107
|
+
def run
|
108
|
+
validate_opts
|
109
|
+
|
110
|
+
expiry = if config[:pem]
|
111
|
+
ssl_pem_expiry
|
112
|
+
elsif config[:pkcs12]
|
113
|
+
ssl_pkcs12_expiry
|
114
|
+
else
|
115
|
+
ssl_cert_expiry
|
116
|
+
end
|
117
|
+
|
118
|
+
days_until = (Date.parse(expiry.to_s) - Date.today).to_i
|
119
|
+
|
120
|
+
if days_until.negative?
|
121
|
+
critical "Expired #{days_until.abs} days ago"
|
122
|
+
elsif days_until < config[:critical].to_i
|
123
|
+
critical "#{days_until} days left"
|
124
|
+
elsif days_until < config[:warning].to_i
|
125
|
+
warning "#{days_until} days left"
|
126
|
+
else
|
127
|
+
ok "#{days_until} days left"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|