Last active
April 18, 2018 18:30
-
-
Save jbsmith86/df7d9aad39e5acf23f2caf73d17b1796 to your computer and use it in GitHub Desktop.
This script returns the number of days left for validity of the certificate of a given url. Supports wildcard certs and SNI. Useful for monitoring.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require "optparse" | |
require "socket" | |
require "openssl" | |
require "time" | |
options = { | |
'port' => 443 | |
} | |
arguments = OptionParser.new do |arguments| | |
arguments.banner = "Returns the number of valid days left for a certificate" | |
arguments.separator "" | |
arguments.separator "Usage: cert_vaildation URL [options]" | |
arguments.separator "" | |
arguments.separator "Specific options:" | |
arguments.on("-u", "--url URL", "URL to check certificate validity for") do |url| | |
options['url'] = url | |
end | |
arguments.on("-p", "--port PORT", "Port to connect on") do |port| | |
options['port'] = port | |
end | |
arguments.on("-c", "--ca-cert FILEPATH", "Filepath for certificate authority file if it can't be detected by ruby") do |cert| | |
options['cert_location'] = cert | |
end | |
arguments.on_tail("-h", "--help", "Show this message") do | |
print "\n" | |
puts arguments | |
print "\n" | |
exit | |
end | |
end | |
def options_check(options) | |
if options['url'].to_s.empty? | |
if url = ARGV.pop | |
options['url'] = url | |
else | |
puts "You need to specify a url to check in the command! (i.e. \"cert_vaildation www.example.com\") Please try again." | |
raise OptionParser::MissingArgument | |
end | |
end | |
end | |
def cert_days_remaining(results) | |
(DateTime.parse(results['valid_until'].to_s) - DateTime.parse(Time.now.utc.to_s)).to_i | |
end | |
def connect(options, arguments) | |
arguments.parse!(ARGV) | |
options_check(options) | |
ssl_context = OpenSSL::SSL::SSLContext.new | |
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER | |
cert_store = OpenSSL::X509::Store.new | |
if options['cert_location'] | |
cert_store.add_file options['cert_location'] | |
else | |
cert_store.set_default_paths | |
end | |
ssl_context.cert_store = cert_store | |
tcp_client = TCPSocket.new(options['url'], options['port']) | |
ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_client, ssl_context) | |
ssl_client.hostname = options['url'] | |
ssl_client.connect | |
cert = OpenSSL::X509::Certificate.new(ssl_client.peer_cert) | |
certprops = OpenSSL::X509::Name.new(cert.issuer).to_a | |
issuer = certprops.select { |name, data, type| name == "O" }.first[1] | |
results = { | |
'valid_on' => cert.not_before, | |
'valid_until' => cert.not_after, | |
'issuer' => issuer, | |
'valid' => (ssl_client.verify_result == 0) | |
} | |
if results['valid'] | |
puts cert_days_remaining(results) | |
else | |
puts 0 | |
end | |
ssl_client.sysclose | |
tcp_client.close | |
end | |
connect(options, arguments) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<zabbix_export> | |
<version>1.0</version> | |
<date>2017-05-04T12:00:00Z</date> | |
<groups> | |
<group> | |
<name>Templates</name> | |
</group> | |
</groups> | |
<templates> | |
<template> | |
<template>Template SSL Cert Expire</template> | |
<name>Template SSL Cert Expire</name> | |
<groups> | |
<group> | |
<name>Templates</name> | |
</group> | |
</groups> | |
<applications> | |
<application> | |
<name>SSL certificate</name> | |
</application> | |
</applications> | |
<items> | |
<item> | |
<name>SSL certificate validity</name> | |
<type>10</type> | |
<snmp_community/> | |
<multiplier>0</multiplier> | |
<snmp_oid/> | |
<key>cert_vaildation["{HOST.NAME}"]</key> | |
<delay>86400</delay> | |
<history>14</history> | |
<trends>365</trends> | |
<status>0</status> | |
<value_type>0</value_type> | |
<allowed_hosts/> | |
<units>days</units> | |
<delta>0</delta> | |
<snmpv3_contextname/> | |
<snmpv3_securityname/> | |
<snmpv3_securitylevel>0</snmpv3_securitylevel> | |
<snmpv3_authprotocol>0</snmpv3_authprotocol> | |
<snmpv3_authpassphrase/> | |
<snmpv3_privprotocol>0</snmpv3_privprotocol> | |
<snmpv3_privpassphrase/> | |
<formula>1</formula> | |
<delay_flex/> | |
<params/> | |
<ipmi_sensor/> | |
<data_type>0</data_type> | |
<authtype>0</authtype> | |
<username/> | |
<password/> | |
<publickey/> | |
<privatekey/> | |
<port/> | |
<description/> | |
<inventory_link>0</inventory_link> | |
<applications> | |
<application> | |
<name>SSL certificate</name> | |
</application> | |
</applications> | |
<valuemap/> | |
</item> | |
</items> | |
<discovery_rules/> | |
<macros> | |
<macro> | |
<macro>{$SSL_PORT}</macro> | |
<value>443</value> | |
</macro> | |
</macros> | |
<templates/> | |
<screens/> | |
</template> | |
</templates> | |
<triggers> | |
<trigger> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<1</expression> | |
<name>SSL certificate on {HOSTNAME} expired</name> | |
<url/> | |
<status>0</status> | |
<priority>5</priority> | |
<description/> | |
<type>0</type> | |
<dependencies/> | |
</trigger> | |
<trigger> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<7</expression> | |
<name>SSL certificate on {HOSTNAME} expires in less than 7 days ({ITEM.VALUE} days remaining)</name> | |
<url/> | |
<status>0</status> | |
<priority>4</priority> | |
<description/> | |
<type>0</type> | |
<dependencies> | |
<dependency> | |
<name>SSL certificate on {HOSTNAME} expired</name> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<1</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<15</expression> | |
<name>SSL certificate on {HOSTNAME} expires in less than 15 days ({ITEM.VALUE} days remaining)</name> | |
<url/> | |
<status>0</status> | |
<priority>3</priority> | |
<description/> | |
<type>0</type> | |
<dependencies> | |
<dependency> | |
<name>SSL certificate on {HOSTNAME} expires in less than 7 days ({ITEM.VALUE} days remaining)</name> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<7</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<30</expression> | |
<name>SSL certificate on {HOSTNAME} expires in less than 30 days ({ITEM.VALUE} days remaining)</name> | |
<url/> | |
<status>0</status> | |
<priority>2</priority> | |
<description/> | |
<type>0</type> | |
<dependencies> | |
<dependency> | |
<name>SSL certificate on {HOSTNAME} expires in less than 15 days ({ITEM.VALUE} days remaining)</name> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<15</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<60</expression> | |
<name>SSL certificate on {HOSTNAME} expires in less than 60 days ({ITEM.VALUE} days remaining)</name> | |
<url/> | |
<status>0</status> | |
<priority>1</priority> | |
<description/> | |
<type>0</type> | |
<dependencies> | |
<dependency> | |
<name>SSL certificate on {HOSTNAME} expires in less than 30 days ({ITEM.VALUE} days remaining)</name> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<30</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<90</expression> | |
<name>SSL certificate on {HOSTNAME} expires in less than 90 days ({ITEM.VALUE} days remaining)</name> | |
<url/> | |
<status>0</status> | |
<priority>0</priority> | |
<description/> | |
<type>0</type> | |
<dependencies> | |
<dependency> | |
<name>SSL certificate on {HOSTNAME} expires in less than 60 days ({ITEM.VALUE} days remaining)</name> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].last(0)}<60</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template SSL Cert Expire:cert_vaildation["{HOST.NAME}"].nodata(86500)}=1</expression> | |
<name>SSL certificate on {HOSTNAME} No new data in the last 24h</name> | |
<url/> | |
<status>0</status> | |
<priority>2</priority> | |
<description>No data received for > 24h. Check the script.</description> | |
<type>0</type> | |
<dependencies/> | |
</trigger> | |
</triggers> | |
</zabbix_export> |
Zabbix 3.4.x
Latest data showing exact days remaining
from console executing script also returning current number of days left till expiration.
on a dashboard is always showing SSL certificate on example.com expires in less than 90 days (UNKNOWN days remaining)
How to fix this (UNKNOWN days remaining) issue in a dashboard panel ?
Any clue please?
Founded the fix by myself.
just change trigger name from
SSL certificate on {HOSTNAME} expires in less than 90 days ({ITEM.VALUE} days remaining)
to
SSL certificate on {HOSTNAME} expires in less than 90 days ({ITEM.LASTVALUE} days remaining)
And you will get a correct day count in trigger name :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey! Thx a lot! Works like a charm.
Thank you for posting it on the check-ssl-expire.py Gist!