March 17, 2016 - Warren Held

Check RSA modulus length & telnet access on Cisco devices

If you’re following best security practices then you should be using SSH with an RSA modulus length of at least 2048 and have telnet disabled on your devices.  There isn’t a command to view the RSA modulus length in Cisco IOS other than running sh crypto key mypubkey rsa and then calculating the modulus length manually from the key. By using Python and paramiko we can easily check the modulus length. We can also quickly check to see if the device is accepting telnet connections.  If you are working with a large list of devices like I am, and you’re using Solarwinds to manage your devices, we can use the orionsdk Python module to easily import our list of Cisco devices.

Change the npm_server variable to the address of your Solarwinds server. The script will prompt you for your Solarwinds credentials when it is run. It will loop through every Solarwinds node with Vendor ‘Cisco’ and attempt to establish an SSHv2 session and telnet session. It never actually authenticates with the devices, it only establishes the initial connection. The script will save the output in a .csv file in the same folder as the script. If it is unable to connect to a device or if it encounters an error it will save the error message in the comments column of the csv file and continue on to the next device. You will need to install the paramiko, orionsdk, and requests modules. The rest of the modules should come with Python. This was written on Python 3.5.1. I am also not a programmer and this is my first “real” Python program so there’s probably better ways some of this could be written.

Example output:

Device IP RSA Modulus Length Comments
lorem_ipsum_rt1 10.10.10.1 1024 Accepts Telnet
lorem_ipsum_rt2 10.10.10.2 2048
lorem_ipsum_lorem_rt1 10.10.50.1 Accepts Telnet
lorem_ipsum_ipsum_rt2 10.11.50.4 timed out

If there is nothing in RSA Modulus Length then SSH isn’t enabled. If SSH is enabled but paramiko encountered an error then the error message should be under comments. If Accepts Telnet doesn’t appear under Comments and there is no error message then telnet isn’t enabled. If telnetlib encounted an error while attempting to telnet to the device it will also be in the comments column.

import paramiko  # module for working with ssh connections
import telnetlib  # module for working with telnet connections
import socket  # module for low level OS socket control
from orionsdk import SwisClient  # module to make api calls to Solarwinds SWIS
import requests  # module to easily make api requests
import getpass  # module to allow us to hide input while typing passwords from the console
import csv  # csv module for working with csv files
from colors import colors  # contains ANSI escape sequences that will allow us to easily color console output
import atexit  # used to register functions to run at program exit


def close_output():  # function to gracefully close outputFile if it is open
    if not outputFile.closed:
        outputFile.close()

# Set Solarwinds server address and credentials for SWIS query
npm_server = 'solarwinds.server.com'
username = input("Solarwinds username: ")
password = getpass.getpass("Solarwinds password: ")

atexit.register(close_output)  # register close_output to be ran on program exit

# If verify = False then ignore cert errors when making HTTPS calls to SWIS
verify = False
if not verify:
    from requests.packages.urllib3.exceptions import InsecureRequestWarning
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# Create a new SwisClient with the connection details
swis = SwisClient(npm_server, username, password)

# Send a query to SWIS and save the results in results
results = swis.query("SELECT NodeName, IPAddress FROM Orion.Nodes WHERE Vendor = 'Cisco'")

# Open/create the csv file where we will write the output from the script
outputFile = open('SSH and Telnet Status.csv', 'w', newline='')
outputWriter = csv.writer(outputFile)
outputWriter.writerow(['Device', 'IP', 'RSA Modulus Length', 'SSH', 'Telnet', 'Comments'])

# Loop through each device returned from the SWIS query
for device in results['results']:

    # Grab device name and IP address from the results of the Solarwinds API query
    deviceName = "{NodeName}".format(**device)
    deviceIP = "{IPAddress}".format(**device)

    # Calculate the number of spaces needed between device name and IP to neatly print current device to the console, then print
    spaces = ' ' * (55 - (len(deviceName) + 8))
    print(colors.OKGREEN + "Device: " + colors.ENDC + deviceName + spaces + colors.OKGREEN + "IP: " + colors.ENDC + deviceIP)

    # Create a new INET socket that we will use for port 22 (SSH) connection and set the timeout to 3 seconds
    mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    mySocket.settimeout(3)

    # Try to connect to port 22 (SSH), if it fails check if device accepts telnet, if that fails write error message to csv file
    try:
        mySocket.connect((deviceIP, 22))
    except Exception as e:
        try:
            telnetConn = telnetlib.Telnet(deviceIP, 23, 3)
            telnetConn.close()
            mySocket.close()
            outputWriter.writerow([deviceName, deviceIP, '', 'No', 'Yes', ''])
            continue
        except Exception as e:
            mySocket.close()
            outputWriter.writerow([deviceName, deviceIP, '', '', '', e])
            print(colors.FAIL + "Device: {0}".format(deviceName) + spaces + str(e) + colors.ENDC)
            continue

    # Connection to port 22 was successful, setup paramiko transport over our socket
    myTransport = paramiko.Transport(mySocket)

    try:  # Try to start paramiko SSH client on our transport
        myTransport.start_client()
        sshKeyBits = myTransport.get_remote_server_key().get_bits()
        try:  # client start was success, check if telnet is enabled
            telnetConn = telnetlib.Telnet(deviceIP, 23, 3)
            telnetConn.close()
            myTransport.close()
            mySocket.close()
            outputWriter.writerow([deviceName, deviceIP, sshKeyBits, 'Yes', 'Yes', ''])
        except Exception as e:  # telnet failed, write out ssh information
            outputWriter.writerow([deviceName, deviceIP, sshKeyBits, 'Yes', 'No'])
    except paramiko.SSHException as e:  # exception was raised while trying to start paramiko SSH client
        try:  # check if telnet is enabled, write out SSH error and telnet notice
            telnetConn = telnetlib.Telnet(deviceIP, 23, 3)
            telnetConn.close()
            myTransport.close()
            mySocket.close()
            outputWriter.writerow([deviceName, deviceIP, '', 'No', 'Yes', "{0}".format(e)])
            continue
        except Exception as e:  # telnet connection failed (no SSH or telnet), write out error message
            myTransport.close()
            mySocket.close()
            outputWriter.writerow([deviceName, deviceIP, '', '', '', e])
            print(colors.FAIL + "Device: {0}".format(deviceName) + spaces + str(e) + colors.ENDC)
            continue

Cisco / Python