Summary: I will try to explain in my first pass but if I am missing some pertinent detail let me know and I will follow up on it. I am ingesting a list of servers from a text file and using pyopenssl to resolve, connect,and retrieve the SSL Certificate information. Based on the expire date I am calculating the number of days before the cert expires. My results are passed through an array, formatted, and then sent in an email. It is working perfectly until one of my servers does not resolve and I get the socket.gaierror.
Problem: When the host does not resolve it blows up my results and I am getting the error below. Although it logs the error correctly I am trying pass along something that will note the exception in my results in the format of host, hostname, expiration date, and days to expire that I will be able to pass to the table and send in an email. I want it to note in the host field "Unable to resolve" and maybe just "0" as a result for expiration date and days to expire. Can anyone point me towards a good way of accomplishing that? Thanks!
Error Message:
Checking certificate for server server.domain.com
--- Logging error ---
Traceback (most recent call last):
File "ssl_nag_script_test.py", line 80, in <module>
s.connect((host, int(port)))
socket.gaierror: [Errno 11001] getaddrinfo failed.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "E:\Python36\lib\logging\__init__.py", line 992, in emit
msg = self.format(record)
File "E:\Python36\lib\logging\__init__.py", line 838, in format
return fmt.format(record)
File "E:\Python36\lib\logging\__init__.py", line 575, in format
record.message = record.getMessage()
File "E:\Python36\lib\logging\__init__.py", line 338, in getMessage
msg = msg % self.args
TypeError: not all arguments converted during string formatting
Call stack:
File "ssl_nag_script_test.py", line 106, in <module>
logger.warning('Error on connection to Server,', str(ip))
Message: 'Error on connection to Server,'
Arguments: ('server.domain.com:443\n',)
--- Logging error ---
Traceback (most recent call last):
File "ssl_nag_script_test.py", line 80, in <module>
s.connect((host, int(port)))
socket.gaierror: [Errno 11001] getaddrinfo failed.
**Then**
WARNING:root:ERROR ENCOUNTERED!
WARNING:root:Traceback (most recent call last):
File "ssl_nag_script - Copy.py", line 114, in <module>
key=lambda k: k[1]['days_to_expire'], reverse=False)
TypeError: '<' not supported between instances of 'str' and 'int'
Python Code
# Import the basic modules to run this script
import ssl
from datetime import datetime
import OpenSSL
import socket
from datetime import timedelta
import datetime
import traceback
import logging
# Import the needed libs for sending smtp emails
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
# Set variables for email
email_sen = 'sen@email.com'
email_rec = 'rec@email.com'
subject = 'SSL Certificate Results'
# Create message container for email
msg = MIMEMultipart('alternative')
msg['From'] = email_sen
msg['To'] = email_rec
msg['Subject'] = subject
# Setup logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.WARNING)
formatter = logging.Formatter('%(asctime)s:%(levelname)s:%(message)s')
file_handler = logging.FileHandler('log/SSLNag.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
try:
# opening file with list of servers and set date and time
ipfile = open('server_ip.txt')
cur_date = datetime.datetime.utcnow()
# Create an array to house the results.
# Array will have sub arrays in the format of [host ip] = {host, hostname, expiration date, days to expire}
ssl_results = {}
except Exception as e:
logger.warning("ERROR ENCOUNTERED! \n\n")
logger.warning(str(traceback.format_exc()))
# scan each host in the ip file and check it's ssl
for ip in ipfile:
# Record an entry in the ssl_resutls array
# Always default to false
ssl_results[str(ip)] = {'host': '', 'server_name': '', 'exp_date': '', 'days_to_expire': ''}
try:
host = ip.strip().split(':')[0]
port = ip.strip().split(':')[1]
print('\nChecking certifcate for server ', host)
# Connect to server using SSL.
ctx = OpenSSL.SSL.Context(ssl.PROTOCOL_TLSv1)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, int(port)))
cnx = OpenSSL.SSL.Connection(ctx, s)
cnx.set_connect_state()
cnx.do_handshake()
# Connection complete get SSL certificate and close connection.
cert = cnx.get_peer_certificate()
s.close()
# From SSL certificate get host name, expiration date and decode.
server_name = cert.get_subject().commonName
print(server_name)
edate = cert.get_notAfter()
edate = edate.decode()
# Get date and format. Calculate number of days until SSL expires.
exp_date = datetime.datetime.strptime(edate, '%Y%m%d%H%M%SZ')
days_to_expire = int((exp_date - cur_date).days)
print(exp_date)
print('day to expire', days_to_expire)
# Update the hosts entry
ssl_results[str(ip)]['host'] = host
ssl_results[str(ip)]['server_name'] = server_name
ssl_results[str(ip)]['exp_date'] = exp_date
ssl_results[str(ip)]['days_to_expire'] = days_to_expire
# Logging for errors
except Exception as e:
logger.warning('Error on connection to Server,', str(ip))
logger.warning("ERROR ENCOUNTERED", host, "\n\n")
logger.warning(str(traceback.format_exc()))
# Loop through the ssl_results entries and generate a email + results file
try:
# Sort the ssl_results
sorted_results = sorted(ssl_results.items(),
key=lambda k: k[1]['days_to_expire'], reverse=False)
# variable to hold html for email
SSLCertificates = """<html>
<head>
<style>
table{width: 1024px;}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 5px;
text-align: left;
}
ul:before{
content:attr(data-header);
font-size:120%;
font-weight:bold;
margin-left:-15px;
}
</style>
</head>
<body>
<p><h2>Hello, </h2>
<h3>SSL Expiration Summary:</h3>
<span style="color:red;"><b>NOTE: If any of the below SSL certificates have less than 90 days remaining please renew.<b></span><br><br>
<table id=\"exp_ssls\"><tr><th>Host</th><th>Hostname</th><th>Expiration Date</th><th>Remaining Days</th></tr>
"""
# Write results in an html table
# Apply this formatting if days to expire are equal to or less than 90 days
for server, data in sorted_results:
if float(str(data["days_to_expire"])) <= 90:
SSLCertificates += "<tr><td bgcolor=yellow><font color=red><b>" + str(server) + "</b></td><td bgcolor=yellow><font color=red><b>" + str(data["server_name"]) + "</b></td><td bgcolor=yellow><font color=red><b>" + str(
data["exp_date"]) + "</b></td><td bgcolor=yellow><font color=red><b>" + str(data["days_to_expire"]) + "</b></td></tr>"
# Apply this formatting if days to expire are equal to or greater than 91 days
if float(str(data["days_to_expire"])) >= 91:
SSLCertificates += "<tr><td>" + str(server) + "</td><td>" + str(data["server_name"]) + "</td><td>" + str(
data["exp_date"]) + "</td><td>" + str(data["days_to_expire"]) + "</td></tr>"
SSLCertificates += """</body>
</html>"""
# Write data to a file and attach it to the email
f = open('SSLCertificates.html', 'w')
f.write(SSLCertificates)
f.close()
filename = 'SSLCertificates.html'
attachment = open(filename, 'rb')
# Setup email attachment
msg.attach(MIMEText(SSLCertificates, 'html'))
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename=" + filename)
# Send email
msg.attach(part)
text = msg.as_string()
server = smtplib.SMTP('smtp.server.com', 25)
server.sendmail(email_sen, email_rec, text)
server.quit()
# Logging for errors
except Exception as e:
logging.warning("ERROR ENCOUNTERED! \n\n")
logging.warning(str(traceback.format_exc()))