sensu-plugins-ssl-boutetnico 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](https://badge.fury.io/rb/sensu-plugins-ssl-boutetnico.svg)
|
4
|
+
[](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
|