-1

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()))
kopecs
  • 1,545
  • 10
  • 20
kprober
  • 15
  • 1
  • 5

1 Answers1

0

You have

logger.warning('Error on connection to Server,', str(ip))

but it should be:

logger.warning('Error on connection to Server "%s"', str(ip))

or equivalent.

You are passing an extra parameter str(ip) but there is no placeholder in the message to put it, hence the following part of your error:

  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))
Patrick Mevzek
  • 10,995
  • 16
  • 38
  • 54
  • I am still getting the error when attempting to sort for the email table though.WARNING:root:Traceback (most recent call last): File "ssl_nag_script_test.py", line 115, in key=lambda k: k[1]['days_to_expire'], reverse=False) TypeError: '<' not supported between instances of 'str' and 'int' – kprober Dec 30 '19 at 16:28
  • I think the error message speaks for itself. You are using `<` between one thing that is of type `int` and another of type `str` (not everything in your list seems to be of same type hence....). So you will have to cast one to the other type. Note that it is not expected to handle your errors one by one here in comments. You should work on your side for some time and when you really hit a deadpoint, then post a new question with full details about your new problem (after some search of course, as your problem may already have been discussed) – Patrick Mevzek Dec 30 '19 at 16:38