sensu-plugins-ssl 1.3.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 38aee874f86178e7ea8be52e0856bd678b05ff57
4
- data.tar.gz: a86b4dadf3d7c19effdf38dca39a8661b9780f0d
2
+ SHA256:
3
+ metadata.gz: 81a09f41b7a0220a938ceee8561f7433321376e5617e4c09fb65454476630d88
4
+ data.tar.gz: b0ecba81c6266494db6a0f8c9ecb50da9f8302bef75e6f5267ca6a79aa2c17b5
5
5
  SHA512:
6
- metadata.gz: cb791eaf20f5af33e4e955de621c3a4f0d77db5a276070c0a39286e33781ef935d058c002093a2b6cb602c61cb8e2e00412af91529c12f7effdeffd9161c802c
7
- data.tar.gz: a89a11c728c9967c5c5f568101948cb23d5fbd42ecd2af0347403130b18a1afd0c7bff97c54c3de48101736455aad7d50313cf059d6f5e0a9d63a538ade4e007
6
+ metadata.gz: 7ba079ecb12548b22f684681ee0c077e965ac04a725623554a74b6fc45f009b1a72c3b1657d3ac69dbf58f213497bfee7b3e830dcc769c13c0eeb57fea9c56b9
7
+ data.tar.gz: 45ecf2194f83c759681b9e167a8c8d08e77a20e4691c2c432dc18b779bcd52d1b089fb148a758dd5a7f9e208cab6de8bf54e41a9c785de9eb5935d2155f3b22e
@@ -1,9 +1,66 @@
1
- #Change Log
1
+ # Change Log
2
2
  This project adheres to [Semantic Versioning](http://semver.org/).
3
3
 
4
- This CHANGELOG follows the format listed at [Keep A Changelog](http://keepachangelog.com/)
4
+ This CHANGELOG follows the format listed [here](https://github.com/sensu-plugins/community/blob/master/HOW_WE_CHANGELOG.md).
5
5
 
6
6
  ## [Unreleased]
7
+
8
+ ## [3.0.0] - 2020-08-27
9
+ ### Breaking Changes
10
+ - Bump `sensu-plugin` dependency from `~> 1.2` to `~> 4.0` you can read the changelog entries for [4.0](https://github.com/sensu-plugins/sensu-plugin/blob/master/CHANGELOG.md#400---2018-02-17), [3.0](https://github.com/sensu-plugins/sensu-plugin/blob/master/CHANGELOG.md#300---2018-12-04), and [2.0](https://github.com/sensu-plugins/sensu-plugin/blob/master/CHANGELOG.md#v200---2017-03-29)
11
+ - Remove ruby-2.3.0. Upgrade bundler. Fix failing tests (@phumpal).
12
+
13
+ ### Added
14
+ - Travis build automation to generate Sensu Asset tarballs that can be used n conjunction with Sensu provided ruby runtime assets and the Bonsai Asset Index
15
+ - Require latest sensu-plugin for [Sensu Go support](https://github.com/sensu-plugins/sensu-plugin#sensu-go-enablement)
16
+ - New option to treat anchor argument as a regexp
17
+ - New Check plugin `check-ssl-root-issuer.rb` with alternative logic for trust anchor verification.
18
+
19
+ ### Changed
20
+ - `check-ssl-anchor.rb` uses regexp to test for present of certificates in cert chain that works with both openssl 1.0 and 1.1 formatting
21
+ - Upgrade rake and rubocop dependencies
22
+ - Remediate rubocop issues
23
+
24
+ ### Fixed
25
+ - ssl-anchor test now uses regexp
26
+
27
+ ## [2.0.1] - 2018-05-30
28
+ ### Fixed
29
+ - `check-ssl-qualys.rb`: Fixed typo and removed timeout `-t` short option replacing it with `--timeout` as per previous changelog. `-t` conflicts with the short option for `--time-between`
30
+ - Fixed typo in changelog
31
+
32
+ ## [2.0.0] - 2018-03-27
33
+ ### Breaking Changes
34
+ - `check-ssl-qualys.rb`: when you submit a request with caching enabled it will return back a response including an eta key. Rather than sleeping for some arbitrary number of time we now use this key when its greater than `--time-between` to wait before attempting the next attempt to query. If it is lower or not present we fall back to `--time-between` (@majormoses)
35
+ - `check-ssl-qualys.rb`: new `--timeout` parameter to short circuit slow apis (@majormoses)
36
+
37
+ ### Changed
38
+ - `check-ssl-qualys.rb`: updated `--api-url` to default to `v3` but remains backwards compatible (@jhoblitt) (@majormoses)
39
+
40
+ ### Added
41
+ `check-ssl-qualys.rb`: option `--debug` to enable debug logging (@majormoses)
42
+
43
+ ### Fixed
44
+ - `check-ssl-hsts-preloadable.rb`: Fixed testing warnings for if a domain can be HSTS preloaded (@rwky)
45
+
46
+ ## [1.5.0] - 2017-09-26
47
+ ### Added
48
+ - Ruby 2.4.1 testing
49
+ - `check-ssl-hsts-preload.rb`: Added check for testing preload status of HSTS (@rwky)
50
+ - `check-ssl-hsts-preloadable.rb`: Added check for testing if a domain can be HSTS preloaded (@rwky)
51
+
52
+ ### Changed
53
+ - updated CHANGELOG guidelines location (@majormoses)
54
+
55
+ ### Fixed
56
+ - `check-java-keystore-cert.rb`: Export cert in PEM format to fix tests that broke going from Precise to Trusty travis workers (@eheydrick)
57
+ - fixed spelling in github pr template (@majormoses)
58
+
59
+ ## [1.4.0] - 2017-06-20
60
+ ### Added
61
+ - `check-ssl-anchor.rb`: Add check for a specific root certificate signature. (@pgporada)
62
+ - `check-ssl-anchor_spec.rb`: Tests for the `check-ssl-anchor.rb` script (@pgporada)
63
+
7
64
  ## [1.3.1] - 2017-05-30
8
65
  ### Fixed
9
66
  - `check-ssl-qualys.rb`: Fix missing `net/http` require that prevented the check from executing (@eheydrick)
@@ -72,7 +129,12 @@ This CHANGELOG follows the format listed at [Keep A Changelog](http://keepachang
72
129
  ### Added
73
130
  - initial release
74
131
 
75
- [Unreleased]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.3.1...HEAD
132
+ [Unreleased]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/3.0.0...HEAD
133
+ [3.0.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/2.0.1...3.0.0
134
+ [2.0.1]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/2.0.0...2.0.1
135
+ [2.0.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.5.0...2.0.0
136
+ [1.5.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.4.0...1.5.0
137
+ [1.4.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.3.1...1.4.0
76
138
  [1.3.1]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.3.0...1.3.1
77
139
  [1.3.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.2.0...1.3.0
78
140
  [1.2.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.1.0...1.2.0
data/README.md CHANGED
@@ -5,18 +5,33 @@
5
5
  [![Code Climate](https://codeclimate.com/github/sensu-plugins/sensu-plugins-ssl/badges/gpa.svg)](https://codeclimate.com/github/sensu-plugins/sensu-plugins-ssl)
6
6
  [![Test Coverage](https://codeclimate.com/github/sensu-plugins/sensu-plugins-ssl/badges/coverage.svg)](https://codeclimate.com/github/sensu-plugins/sensu-plugins-ssl)
7
7
  [![Dependency Status](https://gemnasium.com/sensu-plugins/sensu-plugins-ssl.svg)](https://gemnasium.com/sensu-plugins/sensu-plugins-ssl)
8
+ [![Sensu Bonsai Asset](https://img.shields.io/badge/Bonsai-Download%20Me-brightgreen.svg?colorB=89C967&logo=sensu)](https://bonsai.sensu.io/assets/sensu-plugins/sensu-plugins-ssl)
8
9
 
10
+ ## Sensu Asset
11
+ The Sensu assets packaged from this repository are built against the Sensu Ruby runtime environment. When using these assets as part of a Sensu Go resource (check, mutator or handler), make sure you include the corresponding Sensu Ruby runtime asset in the list of assets needed by the resource. The current ruby-runtime assets can be found [here](https://bonsai.sensu.io/assets/sensu/sensu-ruby-runtime) in the [Bonsai Asset Index](bonsai.sensu.io).
9
12
  ## Functionality
10
13
 
11
14
  ## Files
12
15
  * bin/check-java-keystore-cert.rb
16
+ * bin/check-ssl-anchor.rb
13
17
  * bin/check-ssl-crl.rb
14
18
  * bin/check-ssl-cert.rb
15
19
  * bin/check-ssl-host.rb
20
+ * bin/check-ssl-hsts-preload.rb
21
+ * bin/check-ssl-hsts-preloadable.rb
16
22
  * bin/check-ssl-qualys.rb
23
+ * bin/check-ssl-root-issuer.rb
17
24
 
18
25
  ## Usage
19
26
 
27
+ ### `bin/check-ssl-anchor.rb`
28
+
29
+ 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.
30
+
31
+ ```
32
+ ./bin/check-ssl-anchor.rb -u example.com -a "i:/O=Digital Signature Trust Co./CN=DST Root CA X3"
33
+ ```
34
+
20
35
  ### `bin/check-ssl-crl.rb`
21
36
 
22
37
  Checks a CRL has not or is not expiring by inspecting it's next update value.
@@ -35,8 +50,33 @@ or an online CRL:
35
50
 
36
51
  Critical and Warning thresholds are specified in minutes.
37
52
 
53
+ ### `bin/check-ssl-qualys.rb`
54
+
55
+ 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.
56
+ ```
57
+ ./bin/check-ssl-qualys.rb -d google.com
58
+ ```
59
+
60
+ ### `bin/check-ssl-root-issuer.rb`
61
+
62
+ 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.
63
+
64
+ ```
65
+ ./bin/check-ssl-root-issuer.rb -u example.com -a "CN=DST Root CA X3,O=Digital Signature Trust Co."
66
+ ```
67
+
38
68
  ## Installation
39
69
 
40
70
  [Installation and Setup](http://sensu-plugins.io/docs/installation_instructions.html)
41
71
 
72
+ ## Testing
73
+
74
+ 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.
75
+
76
+ bundle install
77
+ bundle update
78
+ bundle exec rake
79
+
42
80
  ## Notes
81
+
82
+ `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.
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
2
4
  #
3
5
  # check-java-keystore-cert
4
6
  #
@@ -56,8 +58,8 @@ class CheckJavaKeystoreCert < Sensu::Plugin::Check::CLI
56
58
  def certificate_expiration_date
57
59
  result = `keytool -keystore #{Shellwords.escape(config[:path])} \
58
60
  -export -alias #{Shellwords.escape(config[:alias])} \
59
- -storepass #{Shellwords.escape(config[:password])} 2>&1 | \
60
- openssl x509 -enddate -inform der -noout 2>&1`
61
+ -storepass #{Shellwords.escape(config[:password])} -rfc 2>&1 | \
62
+ openssl x509 -enddate -noout 2>&1`
61
63
 
62
64
  # rubocop:disable Style/SpecialGlobalVars
63
65
  unknown 'could not get certificate from keystore' unless $?.success?
@@ -75,7 +77,7 @@ class CheckJavaKeystoreCert < Sensu::Plugin::Check::CLI
75
77
 
76
78
  days_until = (certificate_expiration_date - Date.today).to_i
77
79
 
78
- if days_until < 0
80
+ if days_until < 0 # rubocop: disable Style/NumericPredicate
79
81
  critical "Expired #{days_until.abs} days ago"
80
82
  elsif days_until < config[:critical]
81
83
  critical "#{days_until} days left"
@@ -0,0 +1,122 @@
1
+ #! /usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
4
+ #
5
+ # check-ssl-anchor
6
+ #
7
+ # DESCRIPTION:
8
+ # Check that a certificate is chained to a specific root certificate
9
+ #
10
+ # OUTPUT:
11
+ # plain text
12
+ #
13
+ # PLATFORMS:
14
+ # Linux
15
+ #
16
+ # DEPENDENCIES:
17
+ # gem: sensu-plugin
18
+ #
19
+ # USAGE:
20
+ #
21
+ # Check that a specific website is chained to a specific root certificate (Let's Encrypt for instance)
22
+ # ./check-ssl-anchor.rb \
23
+ # -u example.com \
24
+ # -a "i:/O=Digital Signature Trust Co./CN=DST Root CA X3"
25
+ #
26
+ # NOTES:
27
+ # This is basically a ruby wrapper around the following openssl command.
28
+ #
29
+ # openssl s_client -connect example.com:443 -servername example.com
30
+ #
31
+ #
32
+ #
33
+ # Use the -s flag if you need to override SNI (Server Name Indication). If you
34
+ # are seeing discrepencies between `openssl s_client` and browser, that's a good
35
+ # indication to use this flag.
36
+ #
37
+ # LICENSE:
38
+ # Copyright 2017 Phil Porada <philporada@gmail.com>
39
+ #
40
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
41
+ # for details.
42
+ #
43
+
44
+ require 'sensu-plugin/check/cli'
45
+
46
+ #
47
+ # Check certificate is anchored to a specific root
48
+ #
49
+ class CheckSSLAnchor < Sensu::Plugin::Check::CLI
50
+ option :host,
51
+ description: 'Host to check',
52
+ short: '-h',
53
+ long: '--host HOST',
54
+ required: true
55
+
56
+ option :anchor,
57
+ description: 'An anchor looks something like /O=Digital Signature Trust Co./CN=DST Root CA X3',
58
+ short: '-a',
59
+ long: '--anchor ANCHOR_VAL',
60
+ required: true
61
+
62
+ option :regexp,
63
+ description: 'Treat the anchor as a regexp',
64
+ short: '-r',
65
+ long: '--regexp',
66
+ default: false,
67
+ boolean: true,
68
+ required: false
69
+
70
+ option :servername,
71
+ description: 'Set the TLS SNI (Server Name Indication) extension',
72
+ short: '-s',
73
+ long: '--servername SERVER'
74
+
75
+ option :port,
76
+ description: 'Port on server to check',
77
+ short: '-p',
78
+ long: '--port PORT',
79
+ default: 443
80
+
81
+ def validate_opts
82
+ config[:servername] = config[:host] unless config[:servername]
83
+ end
84
+
85
+ # Do the actual work and massage some data
86
+ def anchor_information
87
+ data = `openssl s_client \
88
+ -connect #{config[:host]}:#{config[:port]} \
89
+ -servername #{config[:servername]} < /dev/null 2>&1`.match(/Certificate chain(.*)---\nServer certificate/m)[1].split(/$/).map(&:strip)
90
+ data = data.reject(&:empty?)
91
+
92
+ unless data[0] =~ /0 s:\/?CN ?=.*/m
93
+ data = 'NOTOK'
94
+ end
95
+ data
96
+ end
97
+
98
+ def run
99
+ validate_opts
100
+ data = anchor_information
101
+ if data == 'NOTOK'
102
+ critical 'An error was encountered while trying to retrieve the certificate chain.'
103
+ end
104
+ puts config[:regexp]
105
+ # rubocop:disable Style/IfInsideElse
106
+ if config[:regexp]
107
+ anchor_regexp = Regexp.new(config[:anchor].to_s)
108
+ if data[-1] =~ anchor_regexp
109
+ ok 'Root anchor has been found.'
110
+ else
111
+ critical 'Root anchor did not match regexp /' + config[:anchor].to_s + "/\nFound \"" + data[-1] + '" instead.'
112
+ end
113
+ else
114
+ if data[-1] == config[:anchor].to_s
115
+ ok 'Root anchor has been found.'
116
+ else
117
+ critical 'Root anchor did not match string "' + config[:anchor].to_s + "\"\nFound \"" + data[-1] + '" instead.'
118
+ end
119
+ end
120
+ # rubocop:enable Style/IfInsideElse
121
+ end
122
+ end
@@ -1,4 +1,6 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
2
4
  #
3
5
  # check-ssl-cert
4
6
  #
@@ -117,7 +119,7 @@ class CheckSSLCert < Sensu::Plugin::Check::CLI
117
119
 
118
120
  days_until = (Date.parse(expiry.to_s) - Date.today).to_i
119
121
 
120
- if days_until < 0
122
+ if days_until < 0 # rubocop:disable Style/NumericPredicate
121
123
  critical "Expired #{days_until.abs} days ago"
122
124
  elsif days_until < config[:critical].to_i
123
125
  critical "#{days_until} days left"
@@ -1,4 +1,6 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
2
4
  #
3
5
  # check-ssl-crl
4
6
  #
@@ -65,10 +67,10 @@ class CheckSSLCRL < Sensu::Plugin::Check::CLI
65
67
  def run
66
68
  validate_opts
67
69
 
68
- next_update = OpenSSL::X509::CRL.new(open(config[:url]).read).next_update
70
+ next_update = OpenSSL::X509::CRL.new(open(config[:url]).read).next_update # rubocop:disable Security/Open
69
71
  minutes_until = seconds_to_minutes(Time.parse(next_update.to_s) - Time.now)
70
72
 
71
- critical "#{config[:url]} - Expired #{minutes_until.abs} minutes ago" if minutes_until < 0
73
+ critical "#{config[:url]} - Expired #{minutes_until.abs} minutes ago" if minutes_until < 0 # rubocop:disable Style/NumericPredicate
72
74
  critical "#{config[:url]} - #{minutes_until} minutes left, next update at #{next_update}" if minutes_until < config[:critical].to_i
73
75
  warning "#{config[:url]} - #{minutes_until} minutes left, next update at #{next_update}" if minutes_until < config[:warning].to_i
74
76
  ok "#{config[:url]} - #{minutes_until} minutes left, next update at #{next_update}"
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: UTF-8
2
+ # frozen_string_literal: false
3
+
3
4
  # check-ssl-host.rb
4
5
  #
5
6
  # DESCRIPTION:
@@ -42,7 +43,7 @@ require 'socket'
42
43
  # Check SSL Host
43
44
  #
44
45
  class CheckSSLHost < Sensu::Plugin::Check::CLI
45
- STARTTLS_PROTOS = %w(smtp imap).freeze
46
+ STARTTLS_PROTOS = %w[smtp imap].freeze
46
47
 
47
48
  check_name 'check_ssl_host'
48
49
 
@@ -104,7 +105,7 @@ class CheckSSLHost < Sensu::Plugin::Check::CLI
104
105
  long: '--starttls PROTO'
105
106
 
106
107
  def get_cert_chain(host, port, address, client_cert, client_key)
107
- tcp_client = TCPSocket.new(address ? address : host, port)
108
+ tcp_client = TCPSocket.new(address ? address : host, port) # rubocop:disable Style/RedundantCondition
108
109
  handle_starttls(config[:starttls], tcp_client) if config[:starttls]
109
110
  ssl_context = OpenSSL::SSL::SSLContext.new
110
111
  ssl_context.cert = OpenSSL::X509::Certificate.new File.read(client_cert) if client_cert
@@ -139,6 +140,7 @@ class CheckSSLHost < Sensu::Plugin::Check::CLI
139
140
 
140
141
  status = socket.readline
141
142
  return if /^220 / =~ status
143
+
142
144
  critical "#{config[:host]} - did not receive SMTP 220 in response to STARTTLS"
143
145
  end
144
146
 
@@ -151,6 +153,7 @@ class CheckSSLHost < Sensu::Plugin::Check::CLI
151
153
 
152
154
  status = socket.readline
153
155
  return if /^a001 OK Begin TLS negotiation now/ =~ status
156
+
154
157
  critical "#{config[:host]} - did not receive OK Begin TLS negotiation now"
155
158
  end
156
159
 
@@ -158,7 +161,7 @@ class CheckSSLHost < Sensu::Plugin::Check::CLI
158
161
  # Expiry check
159
162
  days = (cert.not_after.to_date - Date.today).to_i
160
163
  message = "#{config[:host]} - #{days} days until expiry"
161
- critical "#{config[:host]} - Expired #{days} days ago" if days < 0
164
+ critical "#{config[:host]} - Expired #{days} days ago" if days < 0 # rubocop:disable Style/NumericPredicate
162
165
  critical message if days < config[:critical]
163
166
  warning message if days < config[:warning]
164
167
  ok message
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
4
+ # check-ssl-hsts-preloadable.rb
5
+ #
6
+ # DESCRIPTION:
7
+ # Checks a domain against the chromium HSTS API returning errors/warnings if the domain is preloadable
8
+ #
9
+ # OUTPUT:
10
+ # plain text
11
+ #
12
+ # PLATFORMS:
13
+ # Linux
14
+ #
15
+ # DEPENDENCIES:
16
+ # gem: sensu-plugin
17
+ #
18
+ # USAGE:
19
+ # # Basic usage
20
+ # check-ssl-hsts-preloadable.rb -d <domain_name>
21
+ #
22
+ # LICENSE:
23
+ # Copyright 2017 Rowan Wookey <admin@rwky.net>
24
+ # Released under the same terms as Sensu (the MIT license); see LICENSE for
25
+ # details.
26
+ #
27
+ # Inspired by https://github.com/sensu-plugins/sensu-plugins-ssl/blob/master/bin/check-ssl-qualys.rb Copyright 2015 William Cooke <will@bruisyard.eu>
28
+ #
29
+
30
+ require 'sensu-plugin/check/cli'
31
+ require 'json'
32
+ require 'net/http'
33
+
34
+ class CheckSSLHSTSPreloadable < Sensu::Plugin::Check::CLI
35
+ option :domain,
36
+ description: 'The domain to run the test against',
37
+ short: '-d DOMAIN',
38
+ long: '--domain DOMAIN',
39
+ required: true
40
+
41
+ option :api_url,
42
+ description: 'The URL of the API to run against',
43
+ long: '--api-url URL',
44
+ default: 'https://hstspreload.org/api/v2/preloadable'
45
+
46
+ def fetch(uri, limit = 10)
47
+ if limit == 0 # rubocop:disable Style/NumericPredicate
48
+ return nil
49
+ end
50
+
51
+ response = Net::HTTP.get_response(uri)
52
+
53
+ case response
54
+ when Net::HTTPSuccess
55
+ response
56
+ when Net::HTTPRedirection
57
+ location = URI(response['location'])
58
+ fetch(location, limit - 1)
59
+ end
60
+ end
61
+
62
+ def run
63
+ uri = URI(config[:api_url])
64
+ uri.query = URI.encode_www_form(domain: config[:domain])
65
+ response = fetch(uri)
66
+ if response.nil?
67
+ return warning 'Bad response recieved from API'
68
+ end
69
+
70
+ body = JSON.parse(response.body)
71
+ if !body['errors'].empty?
72
+ critical body['errors'].map { |u| u['summary'] }.join(', ')
73
+ elsif !body['warnings'].empty?
74
+ warning body['warnings'].map { |u| u['summary'] }.join(', ')
75
+ else
76
+ ok
77
+ end
78
+ end
79
+ end
80
+
81
+ # vim: set tabstop=2 shiftwidth=2 expandtab:
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
4
+ # check-ssl-hsts-preload.rb
5
+ #
6
+ # DESCRIPTION:
7
+ # Checks a domain against the chromium HSTS API reporting on the preload status of the domain
8
+ #
9
+ # OUTPUT:
10
+ # plain text
11
+ #
12
+ # PLATFORMS:
13
+ # Linux
14
+ #
15
+ # DEPENDENCIES:
16
+ # gem: sensu-plugin
17
+ #
18
+ # USAGE:
19
+ # # Basic usage
20
+ # check-ssl-hsts-preload.rb -d <domain_name>
21
+ # # Specify the CRITICAL and WARNING alerts to either unknown (not in the database), pending or preloaded
22
+ # check-ssl-hsts-preload.rb -d <domain_name> -c <critical_alert> -w <warning_alert>
23
+ #
24
+ # LICENSE:
25
+ # Copyright 2017 Rowan Wookey <admin@rwky.net>
26
+ # Released under the same terms as Sensu (the MIT license); see LICENSE for
27
+ # details.
28
+ #
29
+ # Inspired by https://github.com/sensu-plugins/sensu-plugins-ssl/blob/master/bin/check-ssl-qualys.rb Copyright 2015 William Cooke <will@bruisyard.eu>
30
+ #
31
+
32
+ require 'sensu-plugin/check/cli'
33
+ require 'json'
34
+ require 'net/http'
35
+
36
+ class CheckSSLHSTSStatus < Sensu::Plugin::Check::CLI
37
+ STATUSES = %w[unknown pending preloaded].freeze
38
+
39
+ option :domain,
40
+ description: 'The domain to run the test against',
41
+ short: '-d DOMAIN',
42
+ long: '--domain DOMAIN',
43
+ required: true
44
+
45
+ option :warn,
46
+ short: '-w STATUS',
47
+ long: '--warn STATUS',
48
+ description: 'WARNING if this status or worse',
49
+ in: STATUSES,
50
+ default: 'pending'
51
+
52
+ option :critical,
53
+ short: '-c STATUS',
54
+ long: '--critical STATUS',
55
+ description: 'CRITICAL if this status or worse',
56
+ in: STATUSES,
57
+ default: 'unknown'
58
+
59
+ option :api_url,
60
+ description: 'The URL of the API to run against',
61
+ long: '--api-url URL',
62
+ default: 'https://hstspreload.org/api/v2/status'
63
+
64
+ def fetch(uri, limit = 10)
65
+ if limit == 0 # rubocop:disable Style/NumericPredicate
66
+ return nil
67
+ end
68
+
69
+ response = Net::HTTP.get_response(uri)
70
+
71
+ case response
72
+ when Net::HTTPSuccess
73
+ response
74
+ when Net::HTTPRedirection
75
+ location = URI(response['location'])
76
+ fetch(location, limit - 1)
77
+ end
78
+ end
79
+
80
+ def run
81
+ uri = URI(config[:api_url])
82
+ uri.query = URI.encode_www_form(domain: config[:domain])
83
+ response = fetch(uri)
84
+ if response.nil?
85
+ return warning 'Bad response recieved from API'
86
+ end
87
+
88
+ body = JSON.parse(response.body)
89
+ unless STATUSES.include? body['status']
90
+ warning 'Invalid status returned ' + body['status']
91
+ end
92
+
93
+ if STATUSES.index(body['status']) <= STATUSES.index(config[:critical])
94
+ critical body['status']
95
+ elsif STATUSES.index(body['status']) <= STATUSES.index(config[:warn])
96
+ warning body['status']
97
+ else
98
+ ok
99
+ end
100
+ end
101
+ end
102
+
103
+ # vim: set tabstop=2 shiftwidth=2 expandtab:
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: UTF-8
2
+ # frozen_string_literal: false
3
+
3
4
  # check-ssl-qualys.rb
4
5
  #
5
6
  # DESCRIPTION:
@@ -41,6 +42,7 @@
41
42
  require 'sensu-plugin/check/cli'
42
43
  require 'json'
43
44
  require 'net/http'
45
+ require 'timeout'
44
46
 
45
47
  # Checks a single DNS entry has a rating above a certain level
46
48
  class CheckSSLQualys < Sensu::Plugin::Check::CLI
@@ -56,7 +58,7 @@ class CheckSSLQualys < Sensu::Plugin::Check::CLI
56
58
  option :api_url,
57
59
  description: 'The URL of the API to run against',
58
60
  long: '--api-url URL',
59
- default: 'https://api.ssllabs.com/api/v2/'
61
+ default: 'https://api.ssllabs.com/api/v3/'
60
62
 
61
63
  option :warn,
62
64
  short: '-w GRADE',
@@ -72,6 +74,12 @@ class CheckSSLQualys < Sensu::Plugin::Check::CLI
72
74
  proc: proc { |g| GRADE_OPTIONS.index(g) },
73
75
  default: 3 # 'B'
74
76
 
77
+ option :debug,
78
+ long: '--debug BOOL',
79
+ description: 'toggles extra debug printing',
80
+ boolean: true,
81
+ default: false
82
+
75
83
  option :num_checks,
76
84
  short: '-n NUM_CHECKS',
77
85
  long: '--number-checks NUM_CHECKS',
@@ -82,17 +90,31 @@ class CheckSSLQualys < Sensu::Plugin::Check::CLI
82
90
  option :between_checks,
83
91
  short: '-t SECONDS',
84
92
  long: '--time-between SECONDS',
85
- description: 'The time between each poll of the API',
93
+ description: 'The fallback time between each poll of the API, when an ETA is given by the previous response and is higher than this value it is used',
86
94
  proc: proc { |t| t.to_i },
87
95
  default: 10
88
96
 
97
+ option :timeout,
98
+ long: '--timeout SECONDS',
99
+ descriptions: 'the amount of seconds that this is allowed to run for',
100
+ proc: proc(&:to_i),
101
+ default: 300
102
+
89
103
  def ssl_api_request(from_cache)
90
104
  params = { host: config[:domain] }
91
- params[:startNew] = 'on' unless from_cache
105
+ params[:startNew] = if from_cache == true
106
+ 'off'
107
+ else
108
+ 'on'
109
+ end
92
110
 
93
111
  uri = URI("#{config[:api_url]}analyze")
94
112
  uri.query = URI.encode_www_form(params)
95
- response = Net::HTTP.get_response(uri)
113
+ begin
114
+ response = Net::HTTP.get_response(uri)
115
+ rescue StandardError => e
116
+ warning e
117
+ end
96
118
 
97
119
  warning 'Bad response recieved from API' unless response.is_a?(Net::HTTPSuccess)
98
120
 
@@ -107,11 +129,38 @@ class CheckSSLQualys < Sensu::Plugin::Check::CLI
107
129
 
108
130
  def ssl_recheck
109
131
  1.upto(config[:num_checks]) do |step|
110
- json = ssl_check(step != 1)
132
+ p "step: #{step}" if config[:debug]
133
+ start_time = Time.now
134
+ p "start_time: #{start_time}" if config[:debug]
135
+ json = if step == 1
136
+ ssl_check(false)
137
+ else
138
+ ssl_check(true)
139
+ end
111
140
  return json if json['status'] == 'READY'
112
- sleep(config[:between_checks])
141
+
142
+ if json['endpoints'] && json['endpoints'].is_a?(Array) # rubocop:disable Style/SafeNavigation
143
+ p "endpoints: #{json['endpoints']}" if config[:debug]
144
+ # The api response sometimes has low eta (which seems unrealistic) from
145
+ # my tests that can be 0 or low numbers which would imply it is done...
146
+ # Basically we check if present and if its higher than the specified
147
+ # time to wait between checks. If so we use the eta from the api get
148
+ # response otherwise we use the time between check values. We have an
149
+ # overall timeout that protects us from the api telling us to wait for
150
+ # insanely long time periods. The highest I have seen the eta go was
151
+ # around 250 seconds but put it in just in case as the api has very
152
+ # erratic response times.
153
+ if json['endpoints'].first.is_a?(Hash) && json['endpoints'].first.key?('eta') && json['endpoints'].first['eta'] > config[:between_checks]
154
+ p "eta: #{json['endpoints'].first['eta']}" if config[:debug]
155
+ sleep(json['endpoints'].first['eta'])
156
+ else
157
+ p "sleeping with default: #{config[:between_checks]}" if config[:debug]
158
+ sleep(config[:between_checks])
159
+ end
160
+ end
161
+ p "elapsed: #{Time.now - start_time}" if config[:debug]
162
+ warning 'Timeout waiting for check to finish' if step == config[:num_checks]
113
163
  end
114
- warning 'Timeout waiting for check to finish'
115
164
  end
116
165
 
117
166
  def ssl_grades
@@ -121,23 +170,25 @@ class CheckSSLQualys < Sensu::Plugin::Check::CLI
121
170
  end
122
171
 
123
172
  def lowest_grade
124
- ssl_grades.sort_by! { |g| GRADE_OPTIONS.index(g) } .reverse![0]
173
+ ssl_grades.sort_by! { |g| GRADE_OPTIONS.index(g) }.reverse![0]
125
174
  end
126
175
 
127
176
  def run
128
- grade = lowest_grade
129
- unless grade
130
- message "#{config[:domain]} not rated"
131
- critical
132
- end
133
- message "#{config[:domain]} rated #{grade}"
134
- grade_rank = GRADE_OPTIONS.index(grade)
135
- if grade_rank > config[:critical]
136
- critical
137
- elsif grade_rank > config[:warn]
138
- warning
139
- else
140
- ok
177
+ Timeout.timeout(config[:timeout]) do
178
+ grade = lowest_grade
179
+ unless grade
180
+ message "#{config[:domain]} not rated"
181
+ critical
182
+ end
183
+ message "#{config[:domain]} rated #{grade}"
184
+ grade_rank = GRADE_OPTIONS.index(grade)
185
+ if grade_rank > config[:critical]
186
+ critical
187
+ elsif grade_rank > config[:warn]
188
+ warning
189
+ else
190
+ ok
191
+ end
141
192
  end
142
193
  end
143
194
  end
@@ -0,0 +1,128 @@
1
+ #! /usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
4
+ #
5
+ # check-ssl-root-issuer
6
+ #
7
+ # DESCRIPTION:
8
+ # Check that a certificate is chained to a specific root certificate issuer
9
+ #
10
+ # OUTPUT:
11
+ # plain text
12
+ #
13
+ # PLATFORMS:
14
+ # Linux
15
+ #
16
+ # DEPENDENCIES:
17
+ # gem: sensu-plugin
18
+ #
19
+ # USAGE:
20
+ #
21
+ # Check that a specific website is chained to a specific root certificate
22
+ # ./check-ssl-root-issuer.rb \
23
+ # -u https://example.com \
24
+ # -i "CN=DST Root CA X3,O=Digital Signature Trust Co."
25
+ #
26
+ # LICENSE:
27
+ # Copyright Jef Spaleta (jspaleta@gmail.com) 2020
28
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
29
+ # for details.
30
+ #
31
+
32
+ require 'sensu-plugin/check/cli'
33
+ require 'openssl'
34
+ require 'uri'
35
+ require 'net/http'
36
+ require 'net/https'
37
+
38
+ #
39
+ # Check root certificate has specified issuer name
40
+ #
41
+ class CheckSSLRootIssuer < Sensu::Plugin::Check::CLI
42
+ option :url,
43
+ description: 'Url to check: Ex "https://google.com"',
44
+ short: '-u',
45
+ long: '--url URL',
46
+ required: true
47
+
48
+ option :issuer,
49
+ description: 'An X509 certificate issuer name, RFC2253 format Ex: "CN=DST Root CA X3,O=Digital Signature Trust Co."',
50
+ short: '-i',
51
+ long: '--issuer ISSUER_NAME',
52
+ required: true
53
+
54
+ option :regexp,
55
+ description: 'Treat the issuer name as a regexp',
56
+ short: '-r',
57
+ long: '--regexp',
58
+ default: false,
59
+ boolean: true,
60
+ required: false
61
+
62
+ option :format,
63
+ description: 'optional issuer name format.',
64
+ short: '-f',
65
+ long: '--format FORMAT_VAL',
66
+ default: 'RFC2253',
67
+ in: %w[RFC2253 ONELINE COMPAT],
68
+ required: false
69
+
70
+ def cert_name_format
71
+ # Note: because format argument is pre-validated by mixin 'in' logic eval is safe to use
72
+ eval "OpenSSL::X509::Name::#{config[:format]}" # rubocop:disable Security/Eval, Style/EvalWithLocation
73
+ end
74
+
75
+ def validate_issuer(cert)
76
+ issuer = cert.issuer.to_s(cert_name_format)
77
+ if config[:regexp]
78
+ issuer_regexp = Regexp.new(config[:issuer].to_s)
79
+ issuer =~ issuer_regexp
80
+ else
81
+ issuer == config[:issuer].to_s
82
+ end
83
+ end
84
+
85
+ def find_root_cert(uri)
86
+ root_cert = nil
87
+ http = Net::HTTP.new(uri.host, uri.port)
88
+ http.open_timeout = 10
89
+ http.read_timeout = 10
90
+ http.use_ssl = true
91
+ http.cert_store = OpenSSL::X509::Store.new
92
+ http.cert_store.set_default_paths
93
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
94
+
95
+ http.verify_callback = lambda { |verify_ok, store_context|
96
+ root_cert = store_context.current_cert unless root_cert # rubocop:disable Style/OrAssignment
97
+ unless verify_ok
98
+ @failed_cert = store_context.current_cert
99
+ @failed_cert_reason = [store_context.error, store_context.error_string] if store_context.error != 0
100
+ end
101
+ verify_ok
102
+ }
103
+ http.start {}
104
+ root_cert
105
+ end
106
+
107
+ # Do the actual work and massage some data
108
+
109
+ def run
110
+ @fail_cert = nil
111
+ @failed_cert_reason = 'Unknown'
112
+ uri = URI.parse(config[:url])
113
+ critical "url protocol must be https, you specified #{url}" if uri.scheme != 'https'
114
+ root_cert = find_root_cert(uri)
115
+ if @failed_cert
116
+ msg = "Certificate verification failed.\n Reason: #{@failed_cert_reason}"
117
+ critical msg
118
+ end
119
+
120
+ if validate_issuer(root_cert)
121
+ msg = 'Root certificate in chain has expected issuer name'
122
+ ok msg
123
+ else
124
+ msg = "Root certificate issuer did not match expected name.\nFound: \"#{root_cert.issuer.to_s(config[:issuer_format])}\""
125
+ critical msg
126
+ end
127
+ end
128
+ end
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sensu-plugins-ssl/version'
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SensuPluginsSSL
2
4
  module Version
3
- MAJOR = 1
4
- MINOR = 3
5
- PATCH = 1
5
+ MAJOR = 3
6
+ MINOR = 0
7
+ PATCH = 0
6
8
 
7
9
  VER_STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sensu-plugins-ssl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sensu-Plugins and contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-31 00:00:00.000000000 Z
11
+ date: 2020-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sensu-plugin
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.2'
19
+ version: '4.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.2'
26
+ version: '4.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.7'
33
+ version: '2.1'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.7'
40
+ version: '2.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: codeclimate-test-reporter
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.3'
61
+ version: '3.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.3'
68
+ version: '3.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: pry
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '10.0'
89
+ version: '13.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '10.0'
96
+ version: '13.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: redcarpet
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -128,52 +128,56 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 0.40.0
131
+ version: 0.89.1
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 0.40.0
138
+ version: 0.89.1
139
139
  - !ruby/object:Gem::Dependency
140
- name: yard
140
+ name: timecop
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '0.8'
145
+ version: 0.8.0
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '0.8'
152
+ version: 0.8.0
153
153
  - !ruby/object:Gem::Dependency
154
- name: timecop
154
+ name: yard
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: 0.8.0
159
+ version: '0.8'
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: 0.8.0
166
+ version: '0.8'
167
167
  description: |-
168
168
  This plugin provides native SSL instrumentation
169
169
  for monitoring, including: hostname and chain
170
170
  verification, cert and crl expiry, and Qualys SSL Labs reporting
171
171
  email: "<sensu-users@googlegroups.com>"
172
172
  executables:
173
- - check-java-keystore-cert.rb
173
+ - check-ssl-hsts-status.rb
174
+ - check-ssl-root-issuer.rb
174
175
  - check-ssl-cert.rb
176
+ - check-java-keystore-cert.rb
175
177
  - check-ssl-crl.rb
178
+ - check-ssl-anchor.rb
176
179
  - check-ssl-host.rb
180
+ - check-ssl-hsts-preloadable.rb
177
181
  - check-ssl-qualys.rb
178
182
  extensions: []
179
183
  extra_rdoc_files: []
@@ -182,10 +186,14 @@ files:
182
186
  - LICENSE
183
187
  - README.md
184
188
  - bin/check-java-keystore-cert.rb
189
+ - bin/check-ssl-anchor.rb
185
190
  - bin/check-ssl-cert.rb
186
191
  - bin/check-ssl-crl.rb
187
192
  - bin/check-ssl-host.rb
193
+ - bin/check-ssl-hsts-preloadable.rb
194
+ - bin/check-ssl-hsts-status.rb
188
195
  - bin/check-ssl-qualys.rb
196
+ - bin/check-ssl-root-issuer.rb
189
197
  - lib/sensu-plugins-ssl.rb
190
198
  - lib/sensu-plugins-ssl/version.rb
191
199
  homepage: https://github.com/sensu-plugins/sensu-plugins-ssl
@@ -206,15 +214,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
206
214
  requirements:
207
215
  - - ">="
208
216
  - !ruby/object:Gem::Version
209
- version: 2.0.0
217
+ version: 2.4.0
210
218
  required_rubygems_version: !ruby/object:Gem::Requirement
211
219
  requirements:
212
220
  - - ">="
213
221
  - !ruby/object:Gem::Version
214
222
  version: '0'
215
223
  requirements: []
216
- rubyforge_project:
217
- rubygems_version: 2.4.5
224
+ rubygems_version: 3.0.8
218
225
  signing_key:
219
226
  specification_version: 4
220
227
  summary: Sensu plugins for SSL