This is the first of a series of tests that will create the Network Security Audit. In this first test I am going to create a single playbook that will check if SNMP is configured correctly on a router R1.
The desired outcome will be for only a single SNMP read only string to be present. Any other SNMP strings are to be removed if configured.
0 1 2 |
snmp-server community COM_STRING RO |
All of the commands in this post build upon my previous Ansible posts.
The playbooks are available in my Github.
And the project for this can be found here.
Version 2 – Any SNMP Configured?
The playbook will check if SNMP is enabled or not. If SNMP is not enabled in anyway then it will apply an SNMP community read only string to the device and print the SNMP configuration on the router.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
--- - name: Cisco ios Find IP hosts: lab_core gather_facts: false connection: network_cli tasks: - name: Show_SNMP ios_command: commands: - "show snmp" register: pre_snmp_output - name: Configure_SNMP ios_config: lines: - snmp-server community COM_STRING RO when: pre_snmp_output is search("%SNMP agent not enabled") register: snmp_changed - name: Show SNMP Config ios_command: commands: - "show run | i snmp" # when: snmp_changed.changed register: post_snmp_output - name: New SNMP debug: var: post_snmp_output.stdout_lines[0][0] # when: snmp_changed.changed |
When
In this playbook I have made use of the “when” condition. This is used to to decide to run a task or not. I have used it to decide to run the SNMP configuration task. If there is any SNMP config on the router, (correct config or incorrect config it doesn’t matter for this part), the task will not run.
I have also left in, (but commented out) the other “when” conditions on the other two tasks following this as a way to choose to run the show commands and display the output.
I will deal with the correct config in the second version below
Version 2 – Correct SNMP Configured?
This version is going to concentrate on ensuring the correct SNMP string is configured. If it is not, then it will be removed and configured correctly. The final version of the playbook is to work as described in the steps below.
- Run the command show snmp
- If show snmp returns “SNMP agent not enabled”
- SNMP is then configured correctly
- If show snmp returns something, then run the command “show run | snmp”
- With the output, check if each element of the output list matches the correct SNMP config
- If not, remove the conifg,
- If it does match, skip
- If configuration was changed, run the command “show run | i snmp” to check the configuration.
- Print the output
This took some building up of my configuration commands to achieve the correct checking/removal of the SNMP string on the router.
The aim is to have only a single SNMP string, any other SNMP config is to be removed.
0 1 2 |
snmp-server community COM_STRING RO |
Iteration 1
The first iteration of this playbook is to get the basic step 1 and 2 sorted.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
--- - name: Cisco ios Find IP hosts: lab_core gather_facts: false connection: network_cli vars: snmp_string: snmp-server community COM_STRING RO tasks: - name: Show SNMP ios_command: commands: - "show snmp" register: pre_snmp_output # Do not run if SNMP is not Configured - name: Show Running SNMP when: pre_snmp_output is not search("%SNMP agent not enabled") ios_command: commands: - "show run | i snmp" register: misconfigured_SNMP - name: Configure SNMP ios_config: lines: - "{{ snmp_string }}" when: pre_snmp_output is search("%SNMP agent not enabled") register: snmp_changed |
And the output for this playbook, first run without SNMP configured on the router. The Playbook will configure SNMP on the router.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ********************************************************************************************************** TASK [Show SNMP] ****************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ********************************************************************************************************** skipping: [172.16.1.104] TASK [Configure SNMP] ************************************************************************************************************* [WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device changed: [172.16.1.104] PLAY RECAP ************************************************************************************************************************ 172.16.1.104 : ok=2 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |
Now a second run, will not need to configure SNMP again, so will skip that part.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ********************************************************************************************************** TASK [Show SNMP] ****************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ********************************************************************************************************** ok: [172.16.1.104] TASK [Configure SNMP] ************************************************************************************************************* skipping: [172.16.1.104] PLAY RECAP ************************************************************************************************************************ 172.16.1.104 : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |
Iteration 2
This second iteration looks if there is SNMP config on the device, remove a hard set command. This will be “no snmp-server community COM_STRING RO2”
I have configured this line on the router so it can be removed.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
--- - name: Cisco ios Find IP hosts: lab_core gather_facts: false connection: network_cli vars: snmp_string: snmp-server community COM_STRING RO tasks: - name: Show SNMP ios_command: commands: - "show snmp" register: pre_snmp_output # Do not run if SNMP is not Configured - name: Show Running SNMP when: pre_snmp_output is not search("%SNMP agent not enabled") ios_command: commands: - "show run | i snmp" register: misconfigured_SNMP - name: Configure SNMP ios_config: lines: - "{{ snmp_string }}" when: pre_snmp_output is search("%SNMP agent not enabled") register: snmp_changed #2 # Will not run if SNMP is not configured # Will remove a set config line only - name: Remove Misconfigured SNMP when: - misconfigured_SNMP.stdout_lines is defined ios_config: lines: - "no snmp-server community COM_STRING2 RO" register: |
The playbook has removed the hard set SNMP community string line from the router config.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ********************************************************************************************************** TASK [Show SNMP] ****************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ********************************************************************************************************** ok: [172.16.1.104] TASK [Configure SNMP] ************************************************************************************************************* skipping: [172.16.1.104] TASK [Remove Misconfigured SNMP] ************************************************************************************************** [WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device changed: [172.16.1.104] PLAY RECAP ************************************************************************************************************************ 172.16.1.104 : ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |
The same task to remove the config will be run even if it is not present. All that needs to be present is output from “show run | i snmp” for this task to run.
The output has not changed, the router took the command, but didn’t need to do anything.
Iteration 3
This iteration will check for three things;
Does “show run | i snmp” has any output?
If there is output, the length is checked to be greater than 1
Is the second element of the config list not equal to the correct SNMP config?
The test config is below. Ansible turns these into a list of two elements. Element 1 is the wrong SNMP command.
The config of element 1 is removed.
Now this has issues if the config is not set out as in the config below. Or if there are more than 2 SNMP commands present.
0 1 2 3 |
snmp-server community COM_STRING RO snmp-server community COM_STRING2 RO |
This playbook is using output from a previous task to use in another task to configure the device.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
- name: Cisco ios Find IP hosts: lab_core gather_facts: false connection: network_cli vars: snmp_string: snmp-server community COM_STRING RO tasks: - name: Show SNMP ios_command: commands: - "show snmp" register: pre_snmp_output # Do not run if SNMP is not Configured - name: Show Running SNMP when: pre_snmp_output is not search("%SNMP agent not enabled") ios_command: commands: - "show run | i snmp" register: misconfigured_SNMP - name: Configure SNMP ios_config: lines: - "{{ snmp_string }}" when: pre_snmp_output is search("%SNMP agent not enabled") register: snmp_changed # 3 # Will check if snmp config is greater than 1 entry # Will check if the 2nd entry is the desired SNMP string, if not # Will remove the 2nd element from the SNMP config - name: Remove Misconfigured SNMP when: - misconfigured_SNMP.stdout_lines is defined - misconfigured_SNMP.stdout_lines[0] | length > 1 - misconfigured_SNMP.stdout_lines[0][1] is not search ("snmp-server community COM_STRING RO") ios_config: lines: - "no {{ misconfigured_SNMP.stdout_lines[0][1] }}" register: |
The playbook has removed the incorrect SNMP String.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ****************************************************************************************************** TASK [Show SNMP] ************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ****************************************************************************************************** ok: [172.16.1.104] TASK [Configure SNMP] ********************************************************************************************************* skipping: [172.16.1.104] TASK [Remove Misconfigured SNMP] ********************************************************************************************** [WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device changed: [172.16.1.104] PLAY RECAP ******************************************************************************************************************** 172.16.1.104 : ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |
If it runs when there is only the correct SNMP line in the config, the removal task is skipped.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ****************************************************************************************************** TASK [Show SNMP] ************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ****************************************************************************************************** ok: [172.16.1.104] TASK [Configure SNMP] ********************************************************************************************************* skipping: [172.16.1.104] TASK [Remove Misconfigured SNMP] ********************************************************************************************** skipping: [172.16.1.104] PLAY RECAP ******************************************************************************************************************** 172.16.1.104 : ok=2 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 |
Iteration 4
This iteration will takes the previous iteration one step further and uses a loop to remove the configuration that is the second element of this list that is from the “show run | i snmp” output.
The Loops in Ansible I have used is “with_items”. This is the only difference between iteration 3 and 4. The output is the same.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 4 # Will check if snmp config is greater than 1 entry # Will remove second element of SNMP config list looping over a list. Just one in this case - name: Remove Misconfigured SNMP when: - misconfigured_SNMP.stdout_lines is defined - misconfigured_SNMP.stdout_lines[0] | length > 1 ios_config: lines: - "no {{ item }}" with_items: - "{{ misconfigured_SNMP.stdout_lines[0][1] }}" register: |
Iteration 5
This iteration will remove all lines from the SNMP configuration that was found with the “show run | i snmp” command.
A with_items loop is used to pass in the list of the “show run | i snmp” output. Ansible iterates over every element of this list and performs the no command removing the configuration.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
--- - name: Cisco ios Find IP hosts: lab_core gather_facts: false connection: network_cli vars: snmp_string: snmp-server community COM_STRING RO tasks: - name: Show SNMP ios_command: commands: - "show snmp" register: pre_snmp_output # Do not run if SNMP is not Configured - name: Show Running SNMP when: pre_snmp_output is not search("%SNMP agent not enabled") ios_command: commands: - "show run | i snmp" register: misconfigured_SNMP - name: Configure SNMP ios_config: lines: - "{{ snmp_string }}" when: pre_snmp_output is search("%SNMP agent not enabled") register: snmp_changed # 5 # Will check if snmp config is greater than 1 entry # Will remove all SNMP config as a list - name: Remove Misconfigured SNMP when: - misconfigured_SNMP.stdout_lines is defined - misconfigured_SNMP.stdout_lines[0] | length > 1 ios_config: lines: - "no {{ item }}" with_items: - "{{ misconfigured_SNMP.stdout_lines[0] }}" register: |
All of the SNMP configuration is removed by this task.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ****************************************************************************************************** TASK [Show SNMP] ************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ****************************************************************************************************** ok: [172.16.1.104] TASK [Configure SNMP] ********************************************************************************************************* skipping: [172.16.1.104] TASK [Remove Misconfigured SNMP] ********************************************************************************************** changed: [172.16.1.104] => (item=snmp-server community COM_STRING RO) changed: [172.16.1.104] => (item=snmp-server community COM_STRING2 RO) [WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device PLAY RECAP ******************************************************************************************************************** 172.16.1.104 : ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |
Iteration 6
This is the last iteration where everything comes together. Any incorrect SNMP config is removed. All configuration lines are evaluated as they are all in the list.
If the line is the correct SNMP configuration, then it will be skipped. All other lines are removed.
Checking Configuration
The last part to this is to get the output to confirm that only the correct SNMP config is configured.
This is two tasks, one to run the show command and the other to print it. Only if SNMP has been configured from the 6th iteration does the show task run. If it skips then there is no show output. I have used the “.changed” command for this.
Similar applies for printing of the output. Only if the show command is run to check is something printed.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
--- - name: Cisco ios Find IP hosts: lab_core gather_facts: false connection: network_cli vars: snmp_string: snmp-server community COM_STRING RO tasks: - name: Show SNMP ios_command: commands: - "show snmp" register: pre_snmp_output # Do not run if SNMP is not Configured - name: Show Running SNMP when: pre_snmp_output is not search("%SNMP agent not enabled") ios_command: commands: - "show run | i snmp" register: misconfigured_SNMP - name: Configure SNMP ios_config: lines: - "{{ snmp_string }}" when: pre_snmp_output is search("%SNMP agent not enabled") register: snmp_changed # 6 # Will remove SNMP config as a list, except if it is the defined SNMP string - name: Remove Misconfigured SNMP when: - misconfigured_SNMP.stdout_lines is defined - item != "{{ snmp_string }}" ios_config: lines: - "no {{ item }}" with_items: - "{{ misconfigured_SNMP.stdout_lines[0] }}" register: snmp_misconfigure_fix - name: Show Fixed SNMP Config ios_command: commands: - "show run | i snmp" when: snmp_misconfigure_fix.changed register: post_snmp_output - name: New SNMP debug: var: post_snmp_output.stdout_lines[0][0] when: post_snmp_output.stdout_lines is defined |
The first output is when both strings are configured on the router.
0 1 2 3 4 |
R1(config)#do sh run | i snmp snmp-server community COM_STRING RO snmp-server community COM_STRING2 RO |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ****************************************************************************************************** TASK [Show SNMP] ************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ****************************************************************************************************** ok: [172.16.1.104] TASK [Configure SNMP] ********************************************************************************************************* skipping: [172.16.1.104] TASK [Remove Misconfigured SNMP] ********************************************************************************************** [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: item != "{{ snmp_string }}" skipping: [172.16.1.104] => (item=snmp-server community COM_STRING RO) changed: [172.16.1.104] => (item=snmp-server community COM_STRING2 RO) [WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device TASK [Show Fixed SNMP Config] ************************************************************************************************* ok: [172.16.1.104] TASK [New SNMP] *************************************************************************************************************** ok: [172.16.1.104] => { "post_snmp_output.stdout_lines[0][0]": "snmp-server community COM_STRING RO" } PLAY RECAP ******************************************************************************************************************** 172.16.1.104 : ok=5 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |
The second output is a rerun, with only the single correct SNMP string configured.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ****************************************************************************************************** TASK [Show SNMP] ************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ****************************************************************************************************** ok: [172.16.1.104] TASK [Configure SNMP] ********************************************************************************************************* skipping: [172.16.1.104] TASK [Remove Misconfigured SNMP] ********************************************************************************************** [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: item != "{{ snmp_string }}" skipping: [172.16.1.104] => (item=snmp-server community COM_STRING RO) TASK [Show Fixed SNMP Config] ************************************************************************************************* skipping: [172.16.1.104] TASK [New SNMP] *************************************************************************************************************** skipping: [172.16.1.104] PLAY RECAP ******************************************************************************************************************** 172.16.1.104 : ok=2 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0 |
The third output is with more SNMP strings and the correct SNMP string at the end.
0 1 2 3 4 5 6 7 |
R1(config)#do sh run | i snmp snmp-server community WRONG_WRONG1 RO snmp-server community WRONG_WRONG2 RO snmp-server community WRONG_WRONG3 RO snmp-server community WRONG_WRONG4 RO snmp-server community COM_STRING RO |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
stef@stef-VirtualBox:~/Ansible_projects$ ansible-playbook -c paramiko playbooks/pb3_securityaudit2.yml --ask-vault-pass Vault password: PLAY [Cisco ios Find IP] ****************************************************************************************************** TASK [Show SNMP] ************************************************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko ok: [172.16.1.104] TASK [Show Running SNMP] ****************************************************************************************************** ok: [172.16.1.104] TASK [Configure SNMP] ********************************************************************************************************* skipping: [172.16.1.104] TASK [Remove Misconfigured SNMP] ********************************************************************************************** [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: item != "{{ snmp_string }}" changed: [172.16.1.104] => (item=snmp-server community WRONG_WRONG1 RO) changed: [172.16.1.104] => (item=snmp-server community WRONG_WRONG2 RO) changed: [172.16.1.104] => (item=snmp-server community WRONG_WRONG3 RO) changed: [172.16.1.104] => (item=snmp-server community WRONG_WRONG4 RO) skipping: [172.16.1.104] => (item=snmp-server community COM_STRING RO) [WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device TASK [Show Fixed SNMP Config] ************************************************************************************************* ok: [172.16.1.104] TASK [New SNMP] *************************************************************************************************************** ok: [172.16.1.104] => { "post_snmp_output.stdout_lines[0][0]": "snmp-server community COM_STRING RO" } PLAY RECAP ******************************************************************************************************************** 172.16.1.104 : ok=5 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |