Check Auth Open and Open Ports in Field Routers
Summary
Credentials were modified to protect the privacy of the organization
Special attention should be paid to the type of Routers firmware that this script should configure - since most field routers were IOS, this script worked fine but some modification of delays and configuration prompts string differ in other device/ servers
The following scripts were created to address security needs in the field routers and the campus access switches. Similarly to to configurating no autostate to field routers, the shut open L2 ports addressed the need to secure open ports in field routers. In this environment, there are roughly +300 field sites that deploy C891 and C1111X routers. While these ports are ISE-secured, it was determined that we should shut down open ports (typically left open due to installation and removal of field endpoints).
Similarly, to address security needs a script was made to secure ISE auth open ports on campus access switches. There were no configuration phrase in this script due to potential impact to the users on campus. It was deemed less risky to find and monitor auth open switches and enforce the shutdown command after reviewing the auth open port manually.
Check Auth Open
from netmiko import ConnectHandler
import paramiko
import re
"""
Function: has_authentication_open(text)
This function checks if the given text contains the string "authentication open".
Args:
text (str): String to search for the pattern.
Returns:
bool: True if the pattern is found, False otherwise.
"""
def has_authentication_open(text):
pattern = r"\bauthentication open\b"
if re.search(pattern, text):
return True
else:
return False
"""
This code block opens a file called "Test Sites.txt" containing a list of router hostnames or IP addresses.
It iterates over each line in the file and attempts to connect to the corresponding router.
"""
with open("Test Sites.txt", "r") as f:
resultOutput = "" # String to store the output
for line in f:
# Create a dictionary to store device connection parameters
router = {
'device_type': 'cisco_ios',
'host': str(line),
'username': 'cisco',
'password': 'cisco123',
}
ip = str(line).rstrip("\n") # Extract and strip newline from IP address
try:
# Connect to the router using Netmiko ConnectHandler
router_connected = ConnectHandler(**router)
prompt = router_connected.find_prompt()
# Extract the hostname from the prompt
hostname = prompt.split()[-1]
# Check for "authentication open" in the running configuration
check = router_connected.send_command("show running | include authentication ")
if has_authentication_open(check):
# Router has "authentication open" configured
resultOutput = resultOutput + "Hostname: " + hostname + "(" + str(line).rstrip("\n") + ")" + " - \n"
resultOutput = resultOutput + "This host has authentication open on at least one of its ports.\n"
# Get list of all interfaces
interfaces = router_connected.send_command("show ip interface brief")
# Get detailed interface information (not used further in this script)
output = router_connected.send_command("show ip interface brief")
# Iterate over the interfaces in the brief output
for interface in interfaces.splitlines():
# The regex searches for strings in the variable interface that follow the format of either "GigabitEthernet" or "FastEthernet" followed by up to three digits separated by optional forward slashes
interfaceFound = re.findall(r"GigabitEthernet\d?\/?\d?\/?\d?|FastEthernet\d?\/?\d?\/?\d?", interface, re.IGNORECASE)
# Check for "authentication open" in the running configuration of the specific interface
checkInterface = router_connected.send_command("show run interface " + interfaceFound[0])
if has_authentication_open(checkInterface):
# Interface has "authentication open" configured
resultOutput = resultOutput + "Found Auth Open on %s\n" %(interfaceFound[0])
else:
# Router doesn't have "authentication open" configured
# print("Checking " + hostname + "(" + str(line).rstrip("\n") + ")" + " (Skipped) ")
continue
except paramiko.ssh_exception.AuthenticationException as e:
print('Error connecting to device ' + str(line).strip('\n') + ': {}'.format(e))
finally:
# Close the connection to the device.
router_connected.disconnect()
continue
# Print the final results
print(resultOutput)
Check Open and Shut L2 Ports in the Field Routers
"""
This script identifies down FastEthernet or GigabitEthernet interfaces on Cisco IOS devices
listed in a file called SITES.txt and shuts them down.
1. Import libraries:
- netmiko: Used for interacting with Cisco IOS devices.
- paramiko: (Optional for netmiko) Provides underlying SSH functionality.
- re: Used for regular expressions for pattern matching.
"""
from netmiko import ConnectHandler
import paramiko
import re
import networkUtil
'''
Function: isPhysicalPort(interface)
This function checks if a given interface string represents a physical FastEthernet or GigabitEthernet port.
Args:
interface (str): String representing the interface name.
Returns:
bool: True if the interface is FastEthernet or GigabitEthernet, False otherwise.
'''
def isPhysicalPort(interface):
physical = False
if re.search(r"FastEthernet", interface, re.IGNORECASE):
physical = True
if re.search(r"GigabitEthernet", interface, re.IGNORECASE):
physical = True
return physical
'''
Function: isDown(interface)
This function checks if the given interface status string indicates an "admin down" or "up" state.
Args:
interface (str): String representing the interface output line.
Returns:
bool: True if the interface state is not "admin down" or "up", False otherwise.
'''
def isDown(interface):
# Create a regular expression object
if re.search(r"admin|up", interface):
return False
else:
return True
'''
This function checks if the given interface string contains an IP address format (xxx.xxx.xxx.xxx).
Args:
line (str): String representing the interface output line.
Returns:
bool: True if the line contains an IP address format, False otherwise.
'''
def contains_ip(line):
ip_regex = r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
match = re.search(ip_regex, line)
return match is not None
'''
Opens the text "SITES.txt" file and iterates over each line.
Each line should contains the IP address of a Cisco IOS device.
'''
with open("SITES.txt", "r") as f:
# Iterate over the lines of the file.
for line in f:
router = {
'device_type': 'cisco_ios',
'host': str(line),
'username': 'cisco',
'password': 'cisco123',
}
try:
'''
This block attempts to connect to the device using the netmiko library.
It creates a connection handler object and uses it to interact with the device.
'''
command = []
router_connected = ConnectHandler(**router)
prompt = router_connected.find_prompt()
# The device's hostname is the last word in the prompt.
hostname = prompt.split()[-1]
# Get a list of all the interfaces on the router with a down state using the "show ip interface brief | include down" command.
interfaces = router_connected.send_command("show ip interface brief | include down")
# Iterate over the interfaces and check if the state is down
for interface in interfaces.splitlines():
'''
This section checks if the interface is down, a physical port, and doesn't have an IP address configured.
If all conditions are met, it builds the shutdown command for that interface.
'''
if isDown(interface) and isPhysicalPort(interface) and not contains_ip(interface):
# Build the command to shutdown the port that is being checked
interfaceFound = re.findall(r"GigabitEthernet\d?\/?\d?\/?\d?|FastEthernet\d?\/?\d?\/?\d?", interface, re.IGNORECASE)
command.append("interface " + interfaceFound[0])
command.append("shutdown ")
router_connected.config_mode()
router_connected.send_config_set(command)
router_connected.exit_config_mode()
output = router_connected.send_command("show ip interface brief")
router_connected.save_config()
except paramiko.ssh_exception.AuthenticationException as e:
print('Error connecting to device ' + str(line).strip('\n') + ': {}'.format(e))
continue
finally:
# Close the connection to the device.
router_connected.disconnect()
continue