diff --git a/CHANGELOG.md b/CHANGELOG.md index 2592f6b..55a1ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This CHANGELOG follows the format listed [here](https://github.com/sensu-plugins - Require latest sensu-plugin for [Sensu Go support](https://github.com/sensu-plugins/sensu-plugin#sensu-go-enablement) - New option to treat anchor argument as a regexp - New Check plugin `check-ssl-root-issuer.rb` with alternative logic for trust anchor verification. +- `check-ssl-cert.rb`: Check expiration times for all certs in the chain, not just the leaf cert. Comodo/Sectigo intermediate certs expired recently, causing widespread panic, and so validation of all certs in the chain has become a concern. ### Changed - `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 diff --git a/bin/check-ssl-cert.rb b/bin/check-ssl-cert.rb index b1c3333..b0b07a8 100755 --- a/bin/check-ssl-cert.rb +++ b/bin/check-ssl-cert.rb @@ -77,8 +77,13 @@ class CheckSSLCert < Sensu::Plugin::Check::CLI short: '-S', long: '--pass ' - def ssl_cert_expiry - `openssl s_client -servername #{config[:servername]} -connect #{config[:host]}:#{config[:port]} < /dev/null 2>&1 | openssl x509 -enddate -noout`.split('=').last + def ssl_cert_expiry(certnum) + `openssl s_client -servername #{config[:servername]} -connect #{config[:host]}:#{config[:port]} -showcerts < /dev/null 2> /dev/null | \ + awk 'BEGIN { certnum = -1; in_cert = 0; } + /^-----BEGIN CERTIFICATE-----$/ { certnum++; if (certnum == #{certnum}) { in_cert = 1 } } + in_cert == 1 { print } + /^-----END CERTIFICATE-----$/ { in_cert = 0 }' | openssl x509 -text -noout 2> /dev/null | \ + sed -n -e 's/^[[:space:]]\\+Subject: .*CN[[:space:]]*=[[:space:]]*//p' -e 's/^[[:space:]]\\+Not After[[:space:]]*:[[:space:]]*//p'` end def ssl_pem_expiry @@ -107,24 +112,44 @@ def validate_opts def run validate_opts - expiry = if config[:pem] - ssl_pem_expiry - elsif config[:pkcs12] - ssl_pkcs12_expiry - else - ssl_cert_expiry - end - - days_until = (Date.parse(expiry.to_s) - Date.today).to_i - - if days_until < 0 - critical "Expired #{days_until.abs} days ago" - elsif days_until < config[:critical].to_i - critical "#{days_until} days left" - elsif days_until < config[:warning].to_i - warning "#{days_until} days left" + if !config[:pem] && !config[:pkcs12] + certnum = 0 + loop do + expiry = ssl_cert_expiry(certnum) + + break if expiry == '' + expiry = expiry.split(/\n/) + + days_until = (Date.parse(expiry[0].to_s) - Date.today).to_i + + if days_until < 0 + critical "Cert '#{expiry[1]}' expired #{days_until.abs} days ago" + elsif days_until < config[:critical].to_i + critical "Cert '#{expiry[1]}' expires in #{days_until} days" + elsif days_until < config[:warning].to_i + warning "Cert '#{expiry[1]}' expires in #{days_until} days" + end + certnum += 1 + end + ok 'No certs in chain expiring soon' else - ok "#{days_until} days left" + expiry = if config[:pem] + ssl_pem_expiry + elsif config[:pkcs12] + ssl_pkcs12_expiry + end + + days_until = (Date.parse(expiry.to_s) - Date.today).to_i + + if days_until < 0 + critical "Expired #{days_until.abs} days ago" + elsif days_until < config[:critical].to_i + critical "#{days_until} days left" + elsif days_until < config[:warning].to_i + warning "#{days_until} days left" + else + ok "#{days_until} days left" + end end end end