If you’re looking to gain more time for innovation, managing repeatable tasks with automation is incredibly efficient. Automating security-related tasks provides even more value than just efficiencies. It gives you an auditable workflow, which is especially valuable if you are regulated and audited regularly. Documenting your processes is much easier if it is automated. Cisco provides many ways to manage their solutions through third-party software companies or even solutions developed by your internal staff.
5.15 Technologies works with all types of automation tools, but today we will be focusing on the Cisco ASA REST API to address the business challenges below:
Firewall Administrator Turnover: When a firewall administrator leaves an organization, it’s time to change the LOCAL firewall passwords. For a larger organization, this could mean changing passwords on tens or even hundreds of firewalls.
Privileged Accounts and Password Rotation: As a best practice it’s important to change your passwords periodically based on a “password policy”, which is usually determined by your enterprise security, audit, or risk teams. Changing the passwords is a good idea; however, not always easy, or fun to do when you have many devices and little time.
Security and Compliance Requirements: Sometimes periodically changing your password is not just a good idea or best practice, it is required by your organization to maintain compliance with some audit or regulatory mandate.
Limited Time/Staff: Changing passwords on a firewall is not a difficult thing to do, but changing them periodically is a relatively mundane task, and should be automated. Any task you are going to perform often should be automated to minimize human error, and inefficiencies. Think of it this way if you have 100 firewalls and 10 local accounts per firewall. This would equate to roughly 1000+ commands to run across the enterprise. If each command took 1 minute to complete, the task of resetting local passwords on your firewalls would take 1000+ minutes to complete. 1000 minutes/60 minutes=16.7 hours each time the task is required.
Development Process Overview
To address these challenges, I took the following approach to build a tool that will allow for an automated and scheduled process to change Cisco ASA passwords on all devices in your inventory:
Understand Business Case
Build Use Case/Pseudo Code
- Connect
- Login
- Log output
- Backup configuration
- Get a list of users
- Get a list of management interfaces # Optional
- For each LOCAL user with management access and a high privilege level, reset the password with a randomly generated strong password that aligns with the “password policy”
- Log password changes for future use # Maybe later we want to store this information somewhere in the event of “break glass” situation. We could also build functionality to notify the account owner with the new password. If there is interest, we could add a lot more functionality to this tool such as SIEM integration or even integration with a privileged access management (PAM) system.
Design and setup the lab for testing
Explore Cisco ASA REST API: The Cisco ASA REST API is freely available if you have access to Cisco.com. It has been around for a long time and is used by a lot of third-party management tools to interface with your firewalls. The third-party management tool companies will more than likely charge you to use their tool. I like free better 😊. The REST API has many capabilities and is well documented. Resetting passwords is only one of the possibilities. Over time, I am sure I will get more use cases from customers and our team.
Test Cisco ASA REST API with PostMan
Build the script
Test the script
Document and post the scrip
Lab Overview
This script was built and tested in my lab environment. The lab consists of a few simple components as shown in the diagram below:
Workstation: Windows 10
- Python 3.8
- VSCode
VM: EVE-NG (Hosted on VMWare ESXi)
- VM: Cisco-ASAV-01: 192.168.0.102 with REST API Installed
- VM: Cisco-ASAV-02: 192.168.0.105 with REST API Installed
- VM: Cisco-ASAV-03: 192.168.0.106 with REST API Installed
The table below gives you a general idea of what the minimal configuration should be on each firewall if you are going to set this up for yourself. Each of the Cisco ASAv(s) in my lab have the following configuration with some minor differences such as IP Address, and Host Name.
Cisco ASA-V + Configuration (Base Example)
enable password $sha512$5000$... == pbkdf2
passwd Uzp6JyHt8i6QEG34 encrypted
interface GigabitEthernet0/0
nameif inside
security-level 100
ip address 192.168.0.102 255.255.255.0
logging enable
logging timestamp
logging facility 23
logging device-id context-name
aaa authentication ssh console LOCAL
aaa authentication http console LOCAL
aaa authentication login-history
http server enable
http 0.0.0.0 0.0.0.0 inside
http 192.168.0.0 255.255.255.0 inside
ssh scopy enable
ssh stricthostkeycheck
ssh pubkey-chain
server 192.168.0.4
ssh 192.168.0.0 255.255.255.0 inside
ssh timeout 60
ssh version 2
ssh key-exchange group dh-group1-sha1
management-access inside
#Create Test Admin Users
username SecOps1 password Cisco123 privilege 15
username SecOps2 password Cisco123 privilege 15
username SecOps3 password Cisco123 privilege 15
username SecOps4 password Cisco123 privilege 15
username SecOps5 password Cisco123 privilege 15
username SecOps6 password Cisco123 privilege 15
username SecOps7 password Cisco123 privilege 15
rest-api image disk0:/asa-restapi-7141-lfbff-k8.SPA
rest-api agent
Installing the REST API on Cisco ASA-V
If you have never done so before and need to setup your Cisco ASA to host the REST API you will find this document useful: https://www.cisco.com/c/en/us/td/docs/security/asa/api/qsg-asa-api.html
Following the steps outlined in the Cisco ASA REST API Quick Start Guide you will need to install the REST API on your ASA-V if it does not exist already. This will require you to run the following commands at a minimum.
Once you have the REST API installed on your firewalls you can leverage it for many tasks including the password rotation. I may build an Ansible job in my next blog to outline the steps to automate and deploy the REST API if there is interest. You will also need a TFTP server up and running with your downloaded REST API (“SPA File”):
Abbreviated Commands to Install the Cisco ASA REST API
Login to your Cisco ASAv
enable
config t
copy tftp://<YOUR IP e.g. 192.168.0.4>/asa-restapi-7141-lfbff-k8.SPA disk0:
rest-api image disk0:/asa-restapi-7141-lfbff-k8.SPA
http server enable
http 0.0.0.0 0.0.0.0 inside
aaa authentication http console LOCAL
rest-api agent
Exploring the Cisco ASA REST API with /doc/
Before I started writing any code, I needed to get an idea of what is possible and required in my delivery of the proposed solution… I went to the REST API address to see what was possible. It is always a good idea to read the documentation when it comes to REST APIs, as there are often many examples that outline the HTTP request verbs (GET,PUT, PATCH, DELETE, etc..), syntax, payloads, and endpoints:
https://<your asa management ip>/doc/
For example, in my lab, all my firewalls have the same version of the Cisco ASA REST API. You will be prompted to login with your ASA account. You will then see the following page:
The documentation is extensive with examples and even provides you with the ability to export some examples in Python, Perl, and JavaScript. Since most of the examples were written in an older version of Python, I decided not to use them. I was able to quickly find what I needed by exploring the documentation and examples. Many of the examples provided were used in the PostMan testing I will talk about later in this blog.
The management access endpoint gave me what I need to see, which protocols were enabled and configured on my devices. For example, SSH, HTTP/HTTPS, Telnet. The management access enumeration is not required to support the password rotation script, it is only included in this script to add value to any audit requests (completely optional). You will see this function later in the blog.
As an “old school” Cisco engineer I was naturally inclined to use the CLI option in the API (/api/cli) as depicted in the figure below. This was not a good idea because it would have caused me to poke around with a poorly formatted JSON response that included (“\n”) new line characters in the response (“no time for that”). The better option was to use the (/api/objects/localusers) object to get a list of all users and their security privileges.
As I mentioned above, the CLI endpoint in the API is appealing in many ways but not my favorite way to get information from devices; however, a great option for configuring the devices. I will use the CLI endpoint to process the password changes in the script.
Testing the REST API with PostMan
Postman is widely used for building, testing, and supporting REST APIs. It is a great tool to build and validate your requests to the Cisco ASA REST API. The first thing to do is create a collection, followed by a set of requests. I will use these for testing the response for requests (e.g. GET, POST). For this REST API I will receive all responses to each request with the content type application/json, this needs to be configured in the Body of your request.
After the collection is created, I will add some requests. The first request will need you to authenticate for “Authorization” to the REST API. I used Basic Auth, for testing. Set this at the collection level to minimize setting it on every request in Postman. If you look at the body of the sample request/response below you will see what I mean about using the CLI to get user information. Note the “\nusername” string in the standard output from the CLI request for “show run user”, formatting that type of response in JSON (painful) is time in your life you will want refunded.
Once I successfully created my collection, set my content type, connected, and authenticated, I began validating the API endpoints I needed to incorporate into my script. The figure below shows the output of “show run” using the API CLI endpoint (“/api/cli”). There is that ugly JSON response again. For now, this will suffice for the task of getting a backup of the running configuration. I planned to write that data to a log for rollback purposes on each device.
I moved on to testing (“/api/objects/localusers/”) endpoint to ensure I can see the JSON response and verify it has the data I seek for future use with the password reset/rotation automation.
After getting usernames and privileges, I wanted to test building the JSON response body/payload for changing a single user password. For this I used an inline JSON payload as depicted below and the final script. This worked as expected and meets my needs for password reset/rotation for all accounts on all devices using an “unsophisticated bunch of for loops” in my script.
I posted my PostMan collection to GitHub here.
Scripted Workflow Overview
Now that I have dissected and tested the REST API for my purposes it is time to build something useful, so I can automate the process. The process is simple and documented below:
Connect
Login
Backup the Configuration to Log->Screen and Log File
Get Management Interfaces->Screen and Log File
Get LOCAL Usernames and Privilege Levels->Screen and Log File
Change all LOCAL passwords->Screen and Log File
Cisco ASA REST API Bulk Device/Bulk LOCAL User Password Reset
#https://learninglabs.cisco.com
#https://sandboxapicdc.cisco.com/
#https://github.com/CiscoDevNet
#IMPORT PYTHON LIBRARIES
import random
import string
import requests
import urllib3
from pprint import pprint
import json
import getpass
#IGNORE CERTIFICATE WARNINGS. CHANCES ARE YOU DID NOT CREATE A TRUSTED CERT FOR ALL YOUR FIREWALL DEVICES 😊
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
#DEFINE VARIABLES FOR USE IN FUNCTIONS
log_file="fwbackuplog.log"
link_log_file="lnk.log"
user_log="user.log"
headers = {'Content-Type': 'application/json', 'User-Agent':'REST API Agent'}
cli_path = "/api/cli/"
api_path = "/api/mgmtaccess/hosts/"
user_path="/api/objects/localusers/"
openlog=open(log_file, "w")
payload={"commands":["show run user"]}
backup_payload={"commands": ["show run"]}
#PROMPT THE USER FOR USERNAME, PASSWORD, REMOVE: ENABLE PASSWORD
fw_user=input("Username: ")
try:
fw_pwd=getpass.getpass(prompt='Password: ', stream=None)
#en_pwd=getpass.getpass(prompt='Enable Password: ', stream=None)
except Exception as error:
print ('ERROR',error)
#FUNCTION TO BACKUP THE RUNNING CONFIGURATION ON YOUR ASA BEFORE YOU BREAK IT 😊
def get_backups():
openlog=open(log_file, "a")
hosts_file = open('hosts.txt', 'r+')
with open('hosts.txt') as hosts_file:
hosts_array = hosts_file.read().splitlines()
for host in hosts_array:
url = "https://"+ host + cli_path
print(" ")
backupresponse=requests.post(url,auth=(fw_user, fw_pwd),data=json.dumps(backup_payload),headers=headers,verify=False)
backup_data=json.loads(backupresponse.text)
pprint(backup_data)
openlog.write(backupresponse.text)
openlog.write("\n\n")
openlog.close()
print(" ")
pass
#FUNCTION TO GENERATE RANDOM PASSWORDS, THE LENGTH IS CONFIGURABLE.
def get_random_password_string(length):
password_characters = string.ascii_letters + string.digits + string.punctuation
password = ''.join(random.choice(password_characters) for p in range(length))
return password
#FUNCTION TO GET MANAGEMENT INTERFACES SUCH AS HTTP, SSH, TELNET. THIS IS AN OPTION STEP THAT DEMONSTRATES OTHER FUNCTIONS OF THE API
def get_mgmtdata():
openlog=open(link_log_file, "a")
hosts_file = open('hosts.txt', 'r+')
with open('hosts.txt') as hosts_file:
hosts_array = hosts_file.read().splitlines()
for host in hosts_array:
url = "https://"+ host + api_path
print(" ")
mgmtresponse=requests.get(url,auth=(fw_user, fw_pwd),verify=False)
data=json.loads(mgmtresponse.text)
print(data['selfLink'])
for i in data['items']:
print("type : " + i["type"],i["ip"]["value"],i["netmask"]["value"],i["interface"]["name"])
strType=i["type"]
strIP=i["ip"]["value"]
strNM=i["netmask"]["value"]
strInt=i["interface"]["name"]
openlog.write("Type: %s\tIP: %s\tNetmask: %s\tInterface: %s \n" % (strType,strIP,strNM,strInt))
openlog.write("\n")
openlog.close()
print(" ")
#FUNCTION TO UPDATE THE PASSWORD FOR EACH USER ON EVERY DEVICE USING THE RANDOM PASSWORD GENERATOR ABOVE. HERE I AM USING LENGTH OF 16
def update_passwords():
openlog=open(user_log, "a")
hosts_file = open('hosts.txt', 'r+')
with open('hosts.txt') as hosts_file:
hosts_array = hosts_file.read().splitlines()
for host in hosts_array:
url = "https://"+ host + user_path
print("")
print(url)
userreq=requests.get(url,auth=(fw_user, fw_pwd),headers=headers,verify=False)
usernameres = json.loads(userreq.text)
for i in usernameres['items']:
print("Username : " + i["name"],",Privilege Level : ",i["privilegeLevel"])
str_username=i["name"]
str_privilegeLevel=i["privilegeLevel"]
openlog.write("Url: %s \tUsername: %s\tPrivilege Level: %s \n" % (url,str_username,str_privilegeLevel))
print("")
for j in usernameres['items']:
username=j["name"]
privilege=j["privilegeLevel"]
password=get_random_password_string(16)
cmdline=f"username {username} password {password} privilege {privilege}"
newcli='"'+ cmdline + '"'
_jsonpayload="{"+ '"'+"commands"+'"'+':'+"[" + newcli +"]}"
print(_jsonpayload)
openlog.write(_jsonpayload)
for host in hosts_array:
pwdurl = "https://"+ host + cli_path
print(pwdurl)
requests.post(pwdurl,auth=(fw_user, fw_pwd),data=_jsonpayload,headers=headers,verify=False)
openlog.write("\n")
print("")
openlog.close()
print(" ")
#MAIN FUNCTION, ONE FUNCTION TO RULE THEM ALL!
if __name__ == "__main__":
#get_creds()#Get credentials for Login and access to your script to run
get_backups()# Back it up before you start.
get_mgmtdata()#Get Management Access Information (Optional)
update_passwords()#This will change all passwords returned for any local accounts on the ASA!
This prototype script can be added to any job scheduler or automation engine (Jenkins), with some production ready refinements, to run on a periodic basis and allow for password rotation. I would recommend designing your logging and security since the credentials and output of this script could very easily create another audit issue because the passwords are exposed.
Maybe later I will introduce some functionality to send the credentials to a vault for encrypted storage and recovery. For now, this is merely a prototype you can use to get up and running with your security automation. This script will also work well if your favorite Firewall admin just quit! If you need help building a production ready automation tool please contact me (adidonato@515tech.com). This is more of a “shiv” than a samurai sword!
Script/Solution Overview
If you plan on using this script you will want to make sure you have the following:
Install Python
Install and create a Python Virtual Environment
Clone the repository
Setup your HOSTS.txt file with you list of Cisco ASA devices (inventory). This file is required to support managing multiple devices.
Test Functions IN A TESTING ENVIRONMENT. Don’t be that guy/girl!
Disclaimer: While this may go without saying, “Do NOT test this in your production environment.”
Script Execution and Testing
adidonato@ML-UBUNTU:~$python CiscoASARESTAPI-BlogPost.py
NOTE: You will be prompted for <username> <password> <enable password>
Comments