My goal here was to make updating an ACL that is applied on multiple devices as easy as possible. Below is a portion of my vars.yml file just to give an idea of how updates are done:
- security_scanner: &ss name: "SS1" ip: "192.168.199.3" running the playbook produces the following: .... ip access-list extended printer_acl_in permit ip 192.168.1.0 0.0.0.255 host 192.168.199.3 ..... ! ip access-list extended printer_acl_out permit ip host 192.168.199.3 192.168.1.0 0.0.0.255 .....
In this case i’m creating a printer subnet acl. Let’s say the security scanning folks changed the IP of their scanner from 199.3 to 199.4. Now, all you have to do is update that in the vars.yml file, and then re-create the config files for the devices which you can then push out. I’ll go ahead and update the IP to 199.4, and then create the configs.
- security_scanner: &ss name: "SS1" ip: "192.168.199.4" ! BOX$ ansible-playbook playbook.yml ..... PLAY RECAP ******************************************************************************************************************************* SWITCH1 : ok=1 changed=1 unreachable=0 failed=0 ..... The config files now show: ..... ip access-list extended printer_acl_in permit ip 192.168.1.0 0.0.0.255 host 192.168.199.4 ..... ! ip access-list extended printer_acl_out permit ip host 192.168.199.4 192.168.1.0 0.0.0.255 .....
Now I’ll show the full vars.yml file which includes variables for all of the access switches. This could easily be updated to include the full configuration for the devices, but I just limited it to the basics to show the ACL portion.
One of the new things I learned about when doing this was the use of anchors. This allows you to remove duplication in the file by referencing a single spot. You can see these below for the domain_controllers and security_scanner. The &[name] creates the anchor point, and you then refer back to it via “<<: *[name]”. This allows you to copy all of the items in the anchor point to any other location in the file. Under the printer_acl section, you can see I included the anchor references under “pieces” which means both “printer_acl_in” and “printer_acl_out” now have copies of those variables which i then use later to build the acls.
- domain_controller1 : &dc1 name: "DC1" ip: "192.168.199.1" - domain_controller2 : &dc2 name: "DC2" ip: "192.168.199.2" - security_scanner: &ss name: "SS1" ip: "192.168.199.4" - printer_acl : &printer_acl acls: { acl_in: [ { name: "printer_acl_in", pieces: [ <<: *ss, <<: *dc1, <<: *dc2 ] } ], acl_out: [ { name: "printer_acl_out", pieces: [ <<: *ss, <<: *dc1, <<: *dc2 ] } ] } - access_switches: switch1 : { type: "switch" , hostname: "SWITCH1", version: "12.2", interfaces: [ { name: "Ethernet0/0", desc: "User Subnet", address: "192.168.0.1", network: "192.168.0.0", mask: "255.255.255.0", wild: "0.0.0.255", type: "user" }, { name: "Ethernet0/1", desc: "Printer Subnet", address: "192.168.1.1", network: "192.168.1.0", mask: "255.255.255.0", wild: "0.0.0.255", type: "printer", <<: *printer_acl } ] } switch2 : { type: "switch", hostname: "SWITCH2", version: "12.2", interfaces: [ { name: "Ethernet0/0", desc: "User Subnet", address: "192.168.0.2", network: "192.168.0.0", mask: "255.255.255.0", wild: "0.0.0.255", type: "user" }, { name: "Ethernet0/1", desc: "Printer Subnet", address: "192.168.1.2", network: "192.168.1.0", mask: "255.255.255.0", wild: "0.0.0.255", type: "printer", <<: *printer_acl } ] } The playbook grabs the vars.yml file, and then runs it against the template:
- name: test connection: local gather_facts: none hosts: SWITCH1 vars_files: - "vars.yml" tasks: - template: src=./template.j2 dest=./CONFIGS/{{item.value.hostname}}.cfg with_dict: "{{ access_switches }}" and the template file that generates the final configs: The template.j2 file
hostname: {{ item.value.hostname }} version: {{ item.value.version }} ! {# Building ACLs needed first #} {# Need to loop through the interfaces to find acls that are needed #} {% for int in item.value.interfaces %} {% if int.acls is defined %} {# Checking to see if an INBOUND acl is needed #} {% for acl in int.acls.acl_in %} ip access-list extended {{ acl.name }} {% for pieces in acl.pieces %} permit ip {{ int.network }} {{ int.wild }} host {{ pieces.ip }} {% endfor %} exit {% endfor %} ! {# Checking to see if an OUTBOUND acl is needed #} {% for acl in int.acls.acl_out %} ip access-list extended {{ acl.name }} {% for pieces in acl.pieces %} permit ip host {{ pieces.ip }} {{ int.network }} {{ int.wild }} {% endfor %} exit {% endfor %} {% endif %} {% endfor %} ! {% for int in item.value.interfaces %} interface {{ int.name }} ip address {{ int.address }} {{ int.mask }} {% if int.acls is defined %} {% for acl in int.acls.acl_in %} ip access-group {{ acl.name }} in {% endfor %} {% for acl in int.acls.acl_out %} ip access-group {{ acl.name }} out {% endfor %} {% endif %} ! {% endfor %}
Here are the full final configs that are generated:
SWITCH1.cfg hostname: SWITCH1 version: 12.2 ! ip access-list extended printer_acl_in permit ip 192.168.1.0 0.0.0.255 host 192.168.199.4 permit ip 192.168.1.0 0.0.0.255 host 192.168.199.1 permit ip 192.168.1.0 0.0.0.255 host 192.168.199.2 exit ! ip access-list extended printer_acl_out permit ip host 192.168.199.4 192.168.1.0 0.0.0.255 permit ip host 192.168.199.1 192.168.1.0 0.0.0.255 permit ip host 192.168.199.2 192.168.1.0 0.0.0.255 exit ! interface Ethernet0/0 ip address 192.168.0.1 255.255.255.0 ! interface Ethernet0/1 ip address 192.168.1.1 255.255.255.0 ip access-group printer_acl_in in ip access-group printer_acl_out out ! ! SWITCH2.cfg hostname: SWITCH2 version: 12.2 ! ip access-list extended printer_acl_in permit ip 192.168.1.0 0.0.0.255 host 192.168.199.4 permit ip 192.168.1.0 0.0.0.255 host 192.168.199.1 permit ip 192.168.1.0 0.0.0.255 host 192.168.199.2 exit ! ip access-list extended printer_acl_out permit ip host 192.168.199.4 192.168.1.0 0.0.0.255 permit ip host 192.168.199.1 192.168.1.0 0.0.0.255 permit ip host 192.168.199.2 192.168.1.0 0.0.0.255 exit ! interface Ethernet0/0 ip address 192.168.0.2 255.255.255.0 ! interface Ethernet0/1 ip address 192.168.1.2 255.255.255.0 ip access-group printer_acl_in in ip access-group printer_acl_out out
Obviously there can be more enhancements that could be made such as including tcp/udp ports or ranges, but I just wanted to show a basic example.
Be First to Comment