-
-
Save vchoi/f374b3d4fceb1b975b98 to your computer and use it in GitHub Desktop.
This file contains 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 python | |
# -*- coding: utf-8 -*- | |
__author__ = "Adrien Pujol - http://www.crashdump.fr/" | |
__copyright__ = "Copyright 2013, Adrien Pujol" | |
__license__ = "Mozilla Public License" | |
__version__ = "0.3" | |
__email__ = "[email protected]" | |
__status__ = "Development" | |
__doc__ = "Check a TLS certificate validity." | |
import argparse | |
import socket | |
from datetime import datetime | |
import time | |
try: | |
# Try to load pyOpenSSL first | |
# aptitude install python-dev && pip install pyopenssl | |
from OpenSSL import SSL | |
import OpenSSL | |
if OpenSSL.__version__ >= '0.13': | |
PYOPENSSL = True | |
else: | |
raise ImportError('python-openssl 0.13+ needed') | |
except ImportError: | |
# Else, fallback on standard ssl lib (doesn't support SNI) | |
import ssl | |
PYOPENSSL = False | |
CA_CERTS = "/etc/ssl/certs/ca-certificates.crt" | |
def exit_error(errcode, errtext): | |
print errtext | |
exit(errcode) | |
def pyopenssl_check_callback(connection, x509, errnum, errdepth, ok): | |
''' callback for pyopenssl ssl check''' | |
if x509.get_subject().commonName == HOST: | |
if x509.has_expired(): | |
exit_error(1, 'Error: Certificate has expired!') | |
else: | |
print pyopenssl_check_expiration(x509.get_notAfter()) | |
if not ok: | |
return False | |
return ok | |
def pyopenssl_check_expiration(asn1): | |
''' Return the numbers of day before expiration. False if expired.''' | |
try: | |
expire_date = datetime.strptime(asn1, "%Y%m%d%H%M%SZ") | |
except: | |
exit_error(1, 'Certificate date format unknow.') | |
expire_in = expire_date - datetime.now() | |
if expire_in.days > 0: | |
return expire_in.days | |
else: | |
return False | |
def pyssl_check_hostname(cert, hostname): | |
''' Return True if valid. False is invalid ''' | |
if 'subjectAltName' in cert: | |
for typ, val in cert['subjectAltName']: | |
# Wilcard | |
if typ == 'DNS' and val.startswith('*'): | |
if val[2:] == hostname.split('.', 1)[1]: | |
return True | |
# Normal hostnames | |
elif typ == 'DNS' and val == hostname: | |
return True | |
else: | |
return False | |
def pyssl_check_expiration(cert): | |
''' Return the numbers of day before expiration. False if expired. ''' | |
if 'notAfter' in cert: | |
try: | |
expire_date = datetime.strptime(cert['notAfter'], | |
"%b %d %H:%M:%S %Y %Z") | |
except: | |
exit_error(1, 'Certificate date format unknow.') | |
expire_in = expire_date - datetime.now() | |
if expire_in.days > 0: | |
return expire_in.days | |
else: | |
return False | |
def main(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument('host', help='specify an host to connect to') | |
parser.add_argument('-p', '--port', help='specify a port to connect to', | |
type=int, default=443) | |
args = parser.parse_args() | |
global HOST, PORT | |
HOST = args.host | |
PORT = args.port | |
# Check the DNS name | |
try: | |
socket.getaddrinfo(HOST, PORT)[0][4][0] | |
except socket.gaierror as e: | |
exit_error(1, e) | |
# Connect to the host and get the certificate | |
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
sock.connect((HOST, PORT)) | |
# If handled by python SSL library | |
if not PYOPENSSL: | |
try: | |
ssl_sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_REQUIRED, | |
ca_certs=CA_CERTS, | |
ciphers=("HIGH:-aNULL:-eNULL:" | |
"-PSK:RC4-SHA:RC4-MD5")) | |
cert = ssl_sock.getpeercert() | |
if not pyssl_check_hostname(cert, HOST): | |
print 'Error: Hostname does not match!' | |
print pyssl_check_expiration(cert) | |
sock = ssl_sock.unwrap() | |
except ssl.SSLError as e: | |
exit_error(1, e) | |
# If handled by pyOpenSSL module | |
else: | |
try: | |
ctx = SSL.Context(SSL.TLSv1_METHOD) | |
ctx.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, | |
pyopenssl_check_callback) | |
ctx.load_verify_locations(CA_CERTS) | |
ssl_sock = SSL.Connection(ctx, sock) | |
ssl_sock.set_connect_state() | |
ssl_sock.set_tlsext_host_name(HOST) | |
ssl_sock.do_handshake() | |
x509 = ssl_sock.get_peer_certificate() | |
x509name = x509.get_subject() | |
if x509name.commonName != HOST: | |
print 'Error: Hostname does not match!' | |
ssl_sock.shutdown() | |
except SSL.Error as e: | |
exit_error(1, e) | |
sock.close() | |
if __name__ == "__main__": | |
main() |
This file contains 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>2.0</version> | |
<date>2013-11-15T12:28:00Z</date> | |
<groups> | |
<group> | |
<name>Templates</name> | |
</group> | |
</groups> | |
<templates> | |
<template> | |
<template>Template External Check - SSL Cert Expire</template> | |
<name>Template External Check - 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>check-ssl-expire.py["{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 External Check - SSL Cert Expire:check-ssl-expire.py["{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 External Check - SSL Cert Expire:check-ssl-expire.py["{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 External Check - SSL Cert Expire:check-ssl-expire.py["{HOST.NAME}"].last(0)}<1</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template External Check - SSL Cert Expire:check-ssl-expire.py["{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 External Check - SSL Cert Expire:check-ssl-expire.py["{HOST.NAME}"].last(0)}<7</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template External Check - SSL Cert Expire:check-ssl-expire.py["{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 External Check - SSL Cert Expire:check-ssl-expire.py["{HOST.NAME}"].last(0)}<15</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template External Check - SSL Cert Expire:check-ssl-expire.py["{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 External Check - SSL Cert Expire:check-ssl-expire.py["{HOST.NAME}"].last(0)}<30</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template External Check - SSL Cert Expire:check-ssl-expire.py["{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 External Check - SSL Cert Expire:check-ssl-expire.py["{HOST.NAME}"].last(0)}<60</expression> | |
</dependency> | |
</dependencies> | |
</trigger> | |
<trigger> | |
<expression>{Template External Check - SSL Cert Expire:check-ssl-expire.py["{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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment