For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Ansible module to update BIG-IP root/admin passwords

Code is community submitted, community supported, and recognized as ‘Use At Your Own Risk’.

Short Description

Have a large environment with password rotation requirements?  Tired of trying a long list of old passwords to get into an old box?  If you have been working with F5 for a while and in a larger environment, the answer to those questions is probably YES.

This Ansible module aims to provide an automated solution to update all your bigip root/admin passwords to ensure compliance, security and really, peace of mind.

Problem solved by this Code Snippet

I have been in many scenarios where root/admin password management is not easy, especially when working in bigger environments.

As time goes on, making sure all the root/admin passwords are up to date and easily changable can become a very time consuming task.

This ansible playbook will loop through every bigip in the inventory, utilize a list of old/known passwords to gain access and update the root/admin passwords as specified.  

This method has saved me a ton of time over the years.

How to use this Code Snippet

This is a custom Ansible module, so it is mainly a python script.  I've also added an example use.  Add the python script to the appropriate Ansible folder and reference in a playbook

Supporting Documentation

https://github.com/DumpySquare/portable_ansible_env/blob/master/playbooks/dev/f5_fix_pass.yml

https://github.com/DumpySquare/portable_ansible_env/blob/master/playbooks/dev/library/f5_fix_pass.py

https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html

https://docs.ansible.com/ansible/latest/dev_guide/developing_modules.html

https://learning-ocean.com/tutorials/ansible/ansible-custom-module

https://medium.com/codex/how-to-write-an-ansible-module-part-1-3d93bfd4dd7e

https://blog.toast38coza.me/custom-ansible-module-hello-world/

 

Code Snippet Meta Information

  1. Version:  not tracked
  2. Coding Language:  python

Full Code Snippet

 

 

 

#!/usr/bin/python

import re
import paramiko
from ansible.module_utils.basic import *

def main():
    output = [] # create an empty list to log all out output

    # define the fields coming from the ansible module
    fields = {
        "bigip": {"default": True, "type": "str"},
        "local_user": {"default": True, "choices": ['admin', 'root'], "type": "str"},
        "new_password": {"default": True, "no_log": True, "type": "str"},
        "old_password": {"default": True, "no_log": True, "type": "list"}
    }
    
    # pulls in the "fields" above to be used as a dict
    module = AnsibleModule(argument_spec=fields)
    
    i = 0   # counter for number of old passwords

    # assign module params as local variables - makes them easier to use/change
    bigip = module.params['bigip']  
    uname = module.params['local_user']
    passwds = module.params['old_password']
    newpass = module.params['new_password']
    
    while True:
        log = "Trying to connect to %s as %s with %s (%i/%i)" % (bigip,uname, passwds[i], i, len(passwds))
        output.append(log)  # broke into two lines to clean things up

        try:
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(bigip, username=uname, password=passwds[i], look_for_keys=False, allow_agent=False)
            output.append("  ~~~~~~~~~~  Connected  ~~~~~~~~~~  ")
            output.append("    Connected to %s as %s with %s" % (bigip, uname, passwds[i]))
            break
        
        except paramiko.AuthenticationException:
            i += 1      # increment our password counter for this attempt

        if i == len(passwds):
            # no passwords worked - quit with fail
            module.fail_json(msg="No root/admin passwords worked...")


    setadmin = "tmsh modify auth user admin password %s" % newpass
    output.append(" -- Executing:  %s" % setadmin)
    stdin, stdout, stderr = ssh.exec_command(setadmin)
    output.append(stdout.read())
    
    setroot = "echo -e \"%s\\n%s\" | tmsh modify auth password root" % (newpass, newpass)
    output.append(" -- Executing:  %s" % setroot)
    stdin, stdout, stderr = ssh.exec_command(setroot)
    output.append(stdout.read())
    
    output.append(" -- Saving config:  tmsh save sys config -- ")
    stdin, stdout, stderr = ssh.exec_command("tmsh save sys config")
    output.append(stdout.read())
    
    ssh.close   # close ssh session
    module.exit_json(change=True, response=output)

if __name__ == '__main__':
    main()

 

 

 

 

Example usage

 

 

 

---

- hosts: localhost
  connection: local
  gather_facts: no
  vars_prompt:
    - name: "dest"
      prompt: "hostname(inv-fqdn/IP) to update"
      private: no
      #default: "192.168.1.5" # only used for testing
    
  tasks:
  - name: fix root/admin passwords
    f5_fix_pass:
      bigip: "{{ dest }}"
      local_user: "root"  # root or admin
      new_password: "{{ latest_passwd }}" # single string
      old_password: "{{ old_passl }}"   # can be a list
      #  - pass1
      #  - pass2
      #  - pass2
    register: result

  - debug: var=result  

## future enahancements:
# add remote user like radius/tacacs to try first, so we don't have to brut force our way in
# - utilize the "provider" dict like other F5 modules
# - this would also require checking if in tmsh shell and/or getting there if needed
# -- it would require different commands if in tmsh or not
# update the "fields" dict from play definition to "argument_spec" to be inline with f5 standards
# update script output and hide passwords - DONE
# allow script to use rsa keys...?



# - test no passwords working - DONE - set module fail_json with error message
# - test no network connectivity - HALF DONE
# -- if no IP, it won't resolve and the socket fails
# -- 

 

 

 

Updated Aug 01, 2023
Version 2.0

2 Comments

  • Thanks 424694, a Kudos is a good way to mark this content as useful for others.  I'm glad you found it useful.  Thanks.