sensu-plugins-ssl 1.4.0 → 3.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4bb08666a55b083ef5b2e8c252152420e943da99
4
- data.tar.gz: c32e3f6fd49c1436073833748c619c571f7d0947
2
+ SHA256:
3
+ metadata.gz: 61a03c614854b22a64ca82592d13705f04a51b4dc564f8659ed0e8ce46eacd9e
4
+ data.tar.gz: 4f15129501ac3878eef485918941e74455eaa9c6245d60f0a2980f70c502785a
5
5
  SHA512:
6
- metadata.gz: 7999f8f52dd451240c33c68c3f8d5ed1b9c2e7fe6777b7462cde2d3aedf7f290121c2761233a48e67205929b26a1a5a25fa2dbde8b0b5e34338059bf78dca699
7
- data.tar.gz: f1e7d24517dcae4ab6ad0098e951a9bed7ab8cdfab4b71f90db73c4c0965595bc3016161a962bc5e865a1736cffb60d45191eed9b06354bf5dbd4b6f6901e311
6
+ metadata.gz: 0ee7fbca09b4a980e1b027b1c59f9bdac98e3a1e4c0ecf7f031aa18a0cb14f068e267cfe4031f05b7ec6b3837cdae5512ff24df1a2aa080ad2261ee50762144b
7
+ data.tar.gz: 89d0f6aa80d3ee579f2204382726a40838bcd1b72163f020a093c9c27993c8d54794d0a3dfb01f19d625429b7fc17e9f6df2b8aa7ca2e8f907e23ea9b26cd758
@@ -1,10 +1,69 @@
1
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
7
 
8
+ ## [3.0.1] - 2020-08-27
9
+ ### Changed
10
+ - Fixed bonsai script in .travis.yml
11
+
12
+ ## [3.0.1] - 2020-08-27
13
+ ### Changed
14
+ - Modified .travis.yml to re-order deploy steps
15
+
16
+ ## [3.0.0] - 2020-08-27
17
+ ### Breaking Changes
18
+ - 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)
19
+ - Remove ruby-2.3.0. Upgrade bundler. Fix failing tests (@phumpal).
20
+
21
+ ### Added
22
+ - 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
23
+ - Require latest sensu-plugin for [Sensu Go support](https://github.com/sensu-plugins/sensu-plugin#sensu-go-enablement)
24
+ - New option to treat anchor argument as a regexp
25
+ - New Check plugin `check-ssl-root-issuer.rb` with alternative logic for trust anchor verification.
26
+
27
+ ### Changed
28
+ - `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
29
+ - Upgrade rake and rubocop dependencies
30
+ - Remediate rubocop issues
31
+
32
+ ### Fixed
33
+ - ssl-anchor test now uses regexp
34
+
35
+ ## [2.0.1] - 2018-05-30
36
+ ### Fixed
37
+ - `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`
38
+ - Fixed typo in changelog
39
+
40
+ ## [2.0.0] - 2018-03-27
41
+ ### Breaking Changes
42
+ - `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)
43
+ - `check-ssl-qualys.rb`: new `--timeout` parameter to short circuit slow apis (@majormoses)
44
+
45
+ ### Changed
46
+ - `check-ssl-qualys.rb`: updated `--api-url` to default to `v3` but remains backwards compatible (@jhoblitt) (@majormoses)
47
+
48
+ ### Added
49
+ `check-ssl-qualys.rb`: option `--debug` to enable debug logging (@majormoses)
50
+
51
+ ### Fixed
52
+ - `check-ssl-hsts-preloadable.rb`: Fixed testing warnings for if a domain can be HSTS preloaded (@rwky)
53
+
54
+ ## [1.5.0] - 2017-09-26
55
+ ### Added
56
+ - Ruby 2.4.1 testing
57
+ - `check-ssl-hsts-preload.rb`: Added check for testing preload status of HSTS (@rwky)
58
+ - `check-ssl-hsts-preloadable.rb`: Added check for testing if a domain can be HSTS preloaded (@rwky)
59
+
60
+ ### Changed
61
+ - updated CHANGELOG guidelines location (@majormoses)
62
+
63
+ ### Fixed
64
+ - `check-java-keystore-cert.rb`: Export cert in PEM format to fix tests that broke going from Precise to Trusty travis workers (@eheydrick)
65
+ - fixed spelling in github pr template (@majormoses)
66
+
8
67
  ## [1.4.0] - 2017-06-20
9
68
  ### Added
10
69
  - `check-ssl-anchor.rb`: Add check for a specific root certificate signature. (@pgporada)
@@ -78,7 +137,13 @@ This CHANGELOG follows the format listed at [Keep A Changelog](http://keepachang
78
137
  ### Added
79
138
  - initial release
80
139
 
81
- [Unreleased]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.4.0...HEAD
140
+ [Unreleased]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/3.0.2...HEAD
141
+ [3.0.2]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/3.0.1...3.0.2
142
+ [3.0.1]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/3.0.0...3.0.1
143
+ [3.0.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/2.0.1...3.0.0
144
+ [2.0.1]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/2.0.0...2.0.1
145
+ [2.0.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.5.0...2.0.0
146
+ [1.5.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.4.0...1.5.0
82
147
  [1.4.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.3.1...1.4.0
83
148
  [1.3.1]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.3.0...1.3.1
84
149
  [1.3.0]: https://github.com/sensu-plugins/sensu-plugins-ssl/compare/1.2.0...1.3.0
data/README.md CHANGED
@@ -5,7 +5,10 @@
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
@@ -14,13 +17,16 @@
14
17
  * bin/check-ssl-crl.rb
15
18
  * bin/check-ssl-cert.rb
16
19
  * bin/check-ssl-host.rb
20
+ * bin/check-ssl-hsts-preload.rb
21
+ * bin/check-ssl-hsts-preloadable.rb
17
22
  * bin/check-ssl-qualys.rb
23
+ * bin/check-ssl-root-issuer.rb
18
24
 
19
25
  ## Usage
20
26
 
21
27
  ### `bin/check-ssl-anchor.rb`
22
28
 
23
- Check that a specific website is chained to a specific root certificate (Let's Encrypt for instance).
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.
24
30
 
25
31
  ```
26
32
  ./bin/check-ssl-anchor.rb -u example.com -a "i:/O=Digital Signature Trust Co./CN=DST Root CA X3"
@@ -44,6 +50,21 @@ or an online CRL:
44
50
 
45
51
  Critical and Warning thresholds are specified in minutes.
46
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
+
47
68
  ## Installation
48
69
 
49
70
  [Installation and Setup](http://sensu-plugins.io/docs/installation_instructions.html)
@@ -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"
@@ -1,4 +1,6 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: false
3
+
2
4
  #
3
5
  # check-ssl-anchor
4
6
  #
@@ -57,6 +59,14 @@ class CheckSSLAnchor < Sensu::Plugin::Check::CLI
57
59
  long: '--anchor ANCHOR_VAL',
58
60
  required: true
59
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
+
60
70
  option :servername,
61
71
  description: 'Set the TLS SNI (Server Name Indication) extension',
62
72
  short: '-s',
@@ -79,7 +89,7 @@ class CheckSSLAnchor < Sensu::Plugin::Check::CLI
79
89
  -servername #{config[:servername]} < /dev/null 2>&1`.match(/Certificate chain(.*)---\nServer certificate/m)[1].split(/$/).map(&:strip)
80
90
  data = data.reject(&:empty?)
81
91
 
82
- unless data[0] =~ /0 s:\/CN=.*/m
92
+ unless data[0] =~ /0 s:\/?CN ?=.*/m
83
93
  data = 'NOTOK'
84
94
  end
85
95
  data
@@ -91,11 +101,22 @@ class CheckSSLAnchor < Sensu::Plugin::Check::CLI
91
101
  if data == 'NOTOK'
92
102
  critical 'An error was encountered while trying to retrieve the certificate chain.'
93
103
  end
94
-
95
- if data[-1] == config[:anchor].to_s
96
- ok 'Root anchor has been found.'
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
97
113
  else
98
- critical 'Root anchor did not match. Found "' + data[-1] + '" instead.'
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
99
119
  end
120
+ # rubocop:enable Style/IfInsideElse
100
121
  end
101
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 = 4
5
- PATCH = 0
5
+ MAJOR = 3
6
+ MINOR = 0
7
+ PATCH = 2
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.4.0
4
+ version: 3.0.2
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-06-21 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,53 +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
174
- - check-ssl-anchor.rb
173
+ - check-ssl-hsts-status.rb
174
+ - check-ssl-root-issuer.rb
175
175
  - check-ssl-cert.rb
176
+ - check-java-keystore-cert.rb
176
177
  - check-ssl-crl.rb
178
+ - check-ssl-anchor.rb
177
179
  - check-ssl-host.rb
180
+ - check-ssl-hsts-preloadable.rb
178
181
  - check-ssl-qualys.rb
179
182
  extensions: []
180
183
  extra_rdoc_files: []
@@ -187,7 +190,10 @@ files:
187
190
  - bin/check-ssl-cert.rb
188
191
  - bin/check-ssl-crl.rb
189
192
  - bin/check-ssl-host.rb
193
+ - bin/check-ssl-hsts-preloadable.rb
194
+ - bin/check-ssl-hsts-status.rb
190
195
  - bin/check-ssl-qualys.rb
196
+ - bin/check-ssl-root-issuer.rb
191
197
  - lib/sensu-plugins-ssl.rb
192
198
  - lib/sensu-plugins-ssl/version.rb
193
199
  homepage: https://github.com/sensu-plugins/sensu-plugins-ssl
@@ -208,15 +214,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
208
214
  requirements:
209
215
  - - ">="
210
216
  - !ruby/object:Gem::Version
211
- version: 2.0.0
217
+ version: 2.4.0
212
218
  required_rubygems_version: !ruby/object:Gem::Requirement
213
219
  requirements:
214
220
  - - ">="
215
221
  - !ruby/object:Gem::Version
216
222
  version: '0'
217
223
  requirements: []
218
- rubyforge_project:
219
- rubygems_version: 2.4.5
224
+ rubygems_version: 3.0.8
220
225
  signing_key:
221
226
  specification_version: 4
222
227
  summary: Sensu plugins for SSL