In this guide, you will learn how to set up password rotation to rotate Cisco IOS XE network credentials.
Prerequisites
KSM Application: Ensure that the Keeper Secrets Manager (KSM) application is set up.
Shared Folder: A shared folder should be set up where all the records will be stored.
PAM Configuration: Ensure that the PAM Configuration is set up and that the Gateway is running and attached to this configuration.
Requests Library: Ensure that the requests library is installed in your Python environment. This library is necessary for making HTTP requests to Cisco devices.
Setting up AnyConnect Cisco VPN: In order to connect to cisco devices, ensure that the machine hosting Keeper Gateway has Cisco AnyConnect VPN installed and properly configured
Test Cisco Device Connectivity
Requests library installation
The Requests library allows you to send HTTP requests easily. Activate a Python virtual environment in your Keeper Gateway environment and install the library using the following command:
pip install requests
Setting up AnyConnect Cisco VPN
Ensure that the machine hosting Keeper Gateway has Cisco AnyConnect VPN installed and properly configured in order to connect to cisco device. This setup is necessary for establishing secure connections to Cisco devices.
Steps to Test Cisco Device
Following these steps will allow you to test the Cisco device and create a new user in the Cisco sandbox environment.
Note: If you want to use a virtual environment, add a shebang line at the top of the script as documented here in the Python Environment Setup.
Enter the VPN connection details provided in the email or from the DevNet Environment.
Connect using the provided username and password.
6. Store Developer Credentials
At this point, you will see Developer Credentials—a host, username, and password. Store these values in a Keeper Security record of type Login named as Cisco Authentication Record. You will need this Keeper Security record name in order to run the post-rotation script.
7. Add Custom Field to Cisco Authentication Record
Add a custom field named host_endpoint to the Cisco Authentication Record and set its value to the host address (e.g., 10.10.20.48).
8. Create a User
Open your terminal or SSH client.
Connect to the Cisco device using the provided IP address and credentials.
9. Follow These Steps to Create a User
Login with Admin User (developer):
ssh developer@<device-ip>
Enable privileged commands:
enable
Enter configuration mode:
configure terminal
Create a new user with a password:
username <user> password <pass>
10. Test the New User
Login with the new user:
ssh <user>@<device-ip>
Note: Replace <user> with the username you created and <device-ip> with the IP address of the Cisco device.
Setting up Rotation in your Vault
Once you have your prerequisites ready, make sure you cover the following:
Make sure you satisfy all the prerequisites
Ensure that the post-rotation script references the Keeper Security record containing your Cisco admin credentials.
Attach the post-rotation script to a Keeper Security PAM user record using the Keeper Security documentation. When this record has its secrets rotated, the post-rotation script will execute and update the password for the specified Cisco device user.
Step 1: Set Up Rotation Record
Create a new PAM User record to store Cisco User details whose password will be rotated.
Set the username to match the Cisco device admin credentials
Set the password to the current password set for the user.
Add a custom field named host_endpoint to the Cisco Authentication Record and set its value to the host address (e.g., 10.10.20.48).
Step 2: Add PAM Script
Attach the below Python script that will perform the password rotation. The script has additional comments inside that describe each line.
Step 3: Add NOOP Custom Field
Enable No-Operation (NOOP) atomic execution:
In the current PAM User record where user's details are stored, create a new custom text field labeled NOOP and set its value to True.
Step 4: Configure Password Rotation Settings
Rotation Type: Set it to "On-Demand" for this example.
Password Complexity: Leave it as default unless you have specific requirements.
Rotation Settings: Point to the PAM Configuration set up earlier.
Administrative Credentials Record: Can should be left empty
Python Script
PAM script to rotate Cisco IOS XE user credentials:
'''
Password rotation script for Cisco user accounts.
This script is designed to rotate the password for a given Cisco user.
It facilitates the automated updating of user password in your Cisco environment.
NOTE: If spaces are present in the path to the python interpreter, the script will fail to execute.
This is a known limitation of the shebang line in Linux and you will need to create a symlink
to the python interpreter in a path that does not contain spaces.
For example: sudo ln -s "/usr/local/bin/my python3.7" /usr/local/bin/pam_rotation_venv_python3
'''
import sys
import base64
import json
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
'''
Optionally display installed packages for debugging. Uncomment if needed.
import pkg_resources
print("# \n# Installed packages for debugging:")
installed_packages = pkg_resources.working_set
installed_packages_list = sorted(["%s==%s" % (i.key, i.version) for i in installed_packages])
for m in installed_packages_list:
print(f" {m}")
'''
# Import the requests package
try:
import requests
except ImportError:
print("# Error: The 'requests' package is not installed. Run 'pip install requests' to install it.")
exit(1)
def get_username_details(cisco_url, cisco_admin_username, cisco_admin_password, cisco_user_name):
"""
Verify the Cisco user.
Args:
- cisco_url (str): The host endpoint of the Cisco account to connect to.
- cisco_admin_username (str): The username of the Cisco admin account.
- cisco_admin_password (str): The password of the Cisco admin account.
- cisco_user_name (str): The name of the Cisco user whose password needs to be rotated.
Returns:
- True if username found.
"""
# Constructs the request URL for the username API endpoint
request_url = f"{cisco_url}username/"
# Sets the headers for the RESTCONF request, specifying that we expect and send YANG data in JSON format
headers = {
'Accept': 'application/yang-data+json',
'Content-Type': 'application/yang-data+json'
}
try:
# Sends a GET request to the Cisco router to fetch user details
response = requests.get(request_url, headers=headers, auth=(cisco_admin_username,cisco_admin_password), verify=False)
response.raise_for_status()
data = response.json()
# Extracts the list of usernames from the response data
usernames = data["Cisco-IOS-XE-native:username"]
# Iterates through the list of usernames to find the specified user
for user in usernames:
if user["name"]==cisco_user_name:
# Returns True if the specified username is found
return True
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred while fetching username details from Cisco router: {http_err}")
except Exception as err:
print(f"An error occurred: {err}")
return False
def rotate(cisco_url, cisco_admin_username, cisco_admin_password, cisco_user_name, new_password):
"""
Rotate the password for a given Cisco user.
Args:
- cisco_url (str): The host endpoint of the Cisco account to connect to.
- cisco_admin_username (str): The username of the Cisco admin account.
- cisco_admin_password (str): The password of the Cisco admin account.
- cisco_user_name (str): The name of the Cisco user whose password needs to be rotated.
- new_password (str): The new password to be set for the Cisco user.
Returns:
- None
"""
# Calls the function get_username_details to check if the specified user exists on the Cisco router
user = get_username_details(cisco_url, cisco_admin_username, cisco_admin_password, cisco_user_name)
# If the user does not exist, print an error message and exit the program
if not user:
print(f"No user found with the username: {cisco_user_name}")
exit(1)
# Sets the headers for the RESTCONF request, specifying that we expect and send YANG data in JSON format
headers = {
'Accept': 'application/yang-data+json',
'Content-Type': 'application/yang-data+json'
}
# Creates the data payload for the PATCH request to update the user's password
data = {
"Cisco-IOS-XE-native:native": {
"username": [
{
"name": cisco_user_name,
"password": {
"password": new_password
}
}
]
}
}
try:
# Sends a PATCH request to the Cisco router to update the user's password
response = requests.patch(cisco_url, headers=headers, auth=(cisco_admin_username,cisco_admin_password), data=json.dumps(data), verify=False)
response.raise_for_status()
print(f"Password updated successfully for user {cisco_user_name}")
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred while updating the password for the given user: {http_err}")
except Exception as err:
print(f"An error occurred: {err}")
def main():
"""
Main function to rotate the password for a Cisco device user.
Reads and decodes input parameters from stdin, including the authentication record details
and the new password. Then, updates the password of the specified Cisco device user.
"""
record_title = 'Cisco Authentication Record' #This should be same as the title of the record containing username, password and host endpoint details.
api_access_token_record = None
params = None
# Read and decode input parameters from stdin
for base64_params in sys.stdin:
params = json.loads(base64.b64decode(base64_params).decode())
# Decode and load records passed in as JSON strings from the PAM Script section as "Rotation Credential" records
records = json.loads(base64.b64decode(params.get('records')).decode())
# Find the record that matches the specified title
api_access_token_record = next((record for record in records if record['title'].lower() == record_title.lower()), None)
break
if api_access_token_record is None:
print(f"# Error: No Record with the access token found. Title: {record_title}")
exit(1)
# Extract Details from the record
# HostName endpoint of the Cisco device endpoint.
cisco_router_endpoint = api_access_token_record.get('host_endpoint')
# Admin username for the Cisco device
cisco_admin_username = api_access_token_record.get('login')
# Admin password for the Cisco device
cisco_admin_password = api_access_token_record.get('password')
# Username of the Cisco device user whose password needs to be rotated
cisco_user_name = params.get('user')
# New password to set for the Cisco device user
new_password = params.get('newPassword')
# Check if all required fields are present
if not all([cisco_router_endpoint, cisco_admin_username, cisco_admin_password, cisco_user_name]):
print("# Error: One or more required fields are missing in the access token record.")
exit(1)
# Construct the Cisco API URL
cisco_url = f"https://{cisco_router_endpoint}/restconf/data/Cisco-IOS-XE-native:native/"
# Rotate the password for the specified Cisco device user
rotate(cisco_url, cisco_admin_username, cisco_admin_password, cisco_user_name, new_password)
if __name__ == "__main__":
main()
The above script for the Cisco Post-Rotation Script can be also found here:
Note: The user whose password is getting rotated should not be an administrator and must be Authorized for Client VPN [While adding the user via user management portal, the authorized option should be selected as 'Yes'].
After successfully setting up Rotation for your Cisco User Credentials on the PAM User Record, clicking on "Run Scripts Only" will rotate the credential: