Cisco ASA, Automating Access List Search with Python. Part 2

The first part is here https://habr.com/ru/articles/834874/

In the first part the code works, but is not very readable and difficult to understand. Now the code is improved and easy to read, with comments.

After running the 'show access-list' command on Cisco ASA we get:

ciscoasa# show access-list
access-list cached ACL log flows: total 0, denied 0 (deny-flow-max 4096)
            alert-interval 300
access-list gl1; 7 elements; name hash: 0xe79499bb
access-list gl1 line 1 extended permit tcp object-group og1 object-group og12 eq ssh (hitcnt=0) 0xf57a470f
  access-list gl1 line 1 extended permit tcp 10.0.3.0 255.255.255.0 host 10.0.0.3 eq ssh (hitcnt=0) 0x23c38b59
  access-list gl1 line 1 extended permit tcp 10.0.3.0 255.255.255.0 10.0.0.0 255.255.255.0 eq ssh (hitcnt=0) 0x3bc77b3d
  access-list gl1 line 1 extended permit tcp 10.0.41.0 255.255.255.0 host 10.0.0.3 eq ssh (hitcnt=0) 0xed8dff32
  access-list gl1 line 1 extended permit tcp 10.0.41.0 255.255.255.0 10.0.0.0 255.255.255.0 eq ssh (hitcnt=0) 0xcde224d1
access-list gl1 line 2 extended permit tcp host 10.0.0.3 host 100.100.100.1 eq www (hitcnt=0) 0xf80a10d6
access-list gl1 line 3 extended permit tcp object on1 object on2 eq ssh (hitcnt=0) 0x70f8adb4
  access-list gl1 line 3 extended permit tcp host 10.0.0.3 10.0.0.0 255.255.255.0 eq ssh (hitcnt=0) 0x70f8adb4
access-list gl1 line 4 extended permit tcp object on1 host 100.100.100.1 eq www (hitcnt=0) 0x301bd0fd
  access-list gl1 line 4 extended permit tcp host 10.0.0.3 host 100.100.100.1 eq www (hitcnt=0) 0x301bd0fd
access-list gl2; 23 elements; name hash: 0xec1e290
access-list gl2 line 1 extended permit tcp object-group og1 object-group og12 eq https (hitcnt=0) 0xd0d468b5
  access-list gl2 line 1 extended permit tcp 10.0.3.0 255.255.255.0 host 10.0.0.3 eq https (hitcnt=0) 0xae19c8fe
  access-list gl2 line 1 extended permit tcp 10.0.3.0 255.255.255.0 10.0.0.0 255.255.255.0 eq https (hitcnt=0) 0x93b63bc0
  access-list gl2 line 1 extended permit tcp 10.0.41.0 255.255.255.0 host 10.0.0.3 eq https (hitcnt=0) 0x07c7a712
  access-list gl2 line 1 extended permit tcp 10.0.41.0 255.255.255.0 10.0.0.0 255.255.255.0 eq https (hitcnt=0) 0xa7ced8a9
access-list gl2 line 2 extended permit ip object-group og1 object-group og12 (hitcnt=0) 0x516db3da
  access-list gl2 line 2 extended permit ip 10.0.3.0 255.255.255.0 host 10.0.0.3 (hitcnt=0) 0x258b842a
  access-list gl2 line 2 extended permit ip 10.0.3.0 255.255.255.0 10.0.0.0 255.255.255.0 (hitcnt=0) 0xd82afa19
  access-list gl2 line 2 extended permit ip 10.0.41.0 255.255.255.0 host 10.0.0.3 (hitcnt=0) 0x3d4864ae
  access-list gl2 line 2 extended permit ip 10.0.41.0 255.255.255.0 10.0.0.0 255.255.255.0 (hitcnt=0) 0xf4e436ba
access-list gl2 line 3 extended permit tcp object on1 object on2 eq 121 (hitcnt=0) 0xac470d2c
  access-list gl2 line 3 extended permit tcp host 10.0.0.3 10.0.0.0 255.255.255.0 eq 121 (hitcnt=0) 0xac470d2c
access-list gl2 line 4 extended permit tcp host 10.0.0.3 10.0.0.0 255.255.255.0 eq ssh (hitcnt=0) 0x5ce66931
access-list gl2 line 5 extended permit tcp object on4_16 object on4_8 eq https (hitcnt=0) 0xd1faa964
  access-list gl2 line 5 extended permit tcp 10.0.0.0 255.255.0.0 10.0.0.0 255.0.0.0 eq https (hitcnt=0) 0xd1faa964
access-list gl2 line 6 extended permit tcp object on4_16 object-group og5 eq https (hitcnt=0) 0x99c5cc7d
  access-list gl2 line 6 extended permit tcp 10.0.0.0 255.255.0.0 host 1.1.1.1 eq https (hitcnt=0) 0xe802825a
  access-list gl2 line 6 extended permit tcp 10.0.0.0 255.255.0.0 host 5.5.5.1 eq https (hitcnt=0) 0x80a1e5b3
  access-list gl2 line 6 extended permit tcp 10.0.0.0 255.255.0.0 5.5.15.0 255.255.255.0 eq https (hitcnt=0) 0x25de07fd
access-list gl2 line 7 extended permit tcp object on4_8 object-group og5 eq https (hitcnt=0) 0xcd171889
  access-list gl2 line 7 extended permit tcp 10.0.0.0 255.0.0.0 host 1.1.1.1 eq https (hitcnt=0) 0x0cf8371e
  access-list gl2 line 7 extended permit tcp 10.0.0.0 255.0.0.0 host 5.5.5.1 eq https (hitcnt=0) 0xad10cac6
  access-list gl2 line 7 extended permit tcp 10.0.0.0 255.0.0.0 5.5.15.0 255.255.255.0 eq https (hitcnt=0) 0xaeb148b6
access-list gl2 line 8 extended permit tcp any object on2 eq 121 (hitcnt=0) 0x6eb4423f
  access-list gl2 line 8 extended permit tcp any 10.0.0.0 255.255.255.0 eq 121 (hitcnt=0) 0x6eb4423f
access-list gl2 line 9 extended permit tcp object on4_8 any eq https (hitcnt=0) 0xbb42911c
  access-list gl2 line 9 extended permit tcp 10.0.0.0 255.0.0.0 any eq https (hitcnt=0) 0xbb42911c
access-list gl2 line 10 extended permit tcp any object-group og5 eq https (hitcnt=0) 0x61792dc7
  access-list gl2 line 10 extended permit tcp any host 1.1.1.1 eq https (hitcnt=0) 0x7b4af33d
  access-list gl2 line 10 extended permit tcp any host 5.5.5.1 eq https (hitcnt=0) 0x4fa8c459
  access-list gl2 line 10 extended permit tcp any 5.5.15.0 255.255.255.0 eq https (hitcnt=0) 0xd8987a26
access-list gl2 line 11 extended deny ip any any log informational interval 300 (hitcnt=0) 0x22c3cb18
ciscoasa#

Python then converts the access sheet into a CSV file.

It removes leading spaces in lines using the line.lstrip() function.

Removes all unnecessary lines with words: “object”, “remark”, “#”, “denied”, “alert-interval”.

Replaces the word “any” with “0.0.0.0 0.0.0.0”, also replaces the word “host 1.1.1.1” with “1.1.1.1 255.255.255.255”.

#!/usr/bin/python3
#  usage " python asa_acl_to_csv_.py fw_name " it will open file fw_name.conf and convert to CSV file

import csv, sys, re
from sys import argv

args = sys.argv              # read command line arguments

# Set the input and output file names
input_file = args[1] + '.conf'                           # asa show access-list saved to file fw_name.conf
output_file = args[1] + '.csv'                           # access-list  to   fw_name.csv"
with open(input_file, 'r', encoding='utf-8') as file, open(output_file, "w", newline="") as out_file:
    writer = csv.writer(out_file)
    lines = file.readlines()
    for line in lines:                                                                                    # loop each line in input file
        stripped_line = line.lstrip()                                                                     # Strip leading and trailing spaces in line
        if ("#" in stripped_line or "denied" in stripped_line or "alert-interval" in stripped_line):      # Skip lines that contain "#" or "denied" or
            continue                                                                                      # move to the next line in file
        if ("elements" in stripped_line or "object" in stripped_line or "remark" in stripped_line):       # Skip lines that contain "elements" or
            continue
        if stripped_line.strip() == "":                                                                   # Skip empty lines
            continue
        stripped_line = re.sub(r' host ([\w.]+)', r' \1 255.255.255.255', stripped_line)                  # replace 'host 1.1.1.1' with '1.1.1.1 255.255.255.255'
        stripped_line = stripped_line.replace( ' any ', ' 0.0.0.0 0.0.0.0 ')                              # replace 'any' with '0.0.0.0 0.0.0.0'
        stripped_line = stripped_line.replace( ' any ', ' 0.0.0.0 0.0.0.0 ')
        nwords = stripped_line.split()                                                                    # convert line to list
        if nwords[6] == 'ip':
            nwords.insert(11, 'eq')    
            nwords.insert(12, 'ip')                                                                       # add 'ip' as a protocol after destination prefix
        print(nwords)           

        writer.writerow([nwords[1],nwords[3],nwords[5],nwords[7],nwords[8],nwords[9],nwords[10],nwords[11],nwords[12]])   # write a line in output CSV file

and we get a CSV file:

gl1,1,permit,10.0.3.0,255.255.255.0,10.0.0.3,255.255.255.255,eq,ssh
gl1,1,permit,10.0.3.0,255.255.255.0,10.0.0.0,255.255.255.0,eq,ssh
gl1,1,permit,10.0.41.0,255.255.255.0,10.0.0.3,255.255.255.255,eq,ssh
gl1,1,permit,10.0.41.0,255.255.255.0,10.0.0.0,255.255.255.0,eq,ssh
gl1,2,permit,10.0.0.3,255.255.255.255,100.100.100.1,255.255.255.255,eq,www
gl1,3,permit,10.0.0.3,255.255.255.255,10.0.0.0,255.255.255.0,eq,ssh
gl1,4,permit,10.0.0.3,255.255.255.255,100.100.100.1,255.255.255.255,eq,www
gl2,1,permit,10.0.3.0,255.255.255.0,10.0.0.3,255.255.255.255,eq,https
gl2,1,permit,10.0.3.0,255.255.255.0,10.0.0.0,255.255.255.0,eq,https
gl2,1,permit,10.0.41.0,255.255.255.0,10.0.0.3,255.255.255.255,eq,https
gl2,1,permit,10.0.41.0,255.255.255.0,10.0.0.0,255.255.255.0,eq,https
gl2,2,permit,10.0.3.0,255.255.255.0,10.0.0.3,255.255.255.255,eq,ip
gl2,2,permit,10.0.3.0,255.255.255.0,10.0.0.0,255.255.255.0,eq,ip
gl2,2,permit,10.0.41.0,255.255.255.0,10.0.0.3,255.255.255.255,eq,ip
gl2,2,permit,10.0.41.0,255.255.255.0,10.0.0.0,255.255.255.0,eq,ip
gl2,3,permit,10.0.0.3,255.255.255.255,10.0.0.0,255.255.255.0,eq,121
gl2,4,permit,10.0.0.3,255.255.255.255,10.0.0.0,255.255.255.0,eq,ssh
gl2,5,permit,10.0.0.0,255.255.0.0,10.0.0.0,255.0.0.0,eq,https
gl2,6,permit,10.0.0.0,255.255.0.0,1.1.1.1,255.255.255.255,eq,https
gl2,6,permit,10.0.0.0,255.255.0.0,5.5.5.1,255.255.255.255,eq,https
gl2,6,permit,10.0.0.0,255.255.0.0,5.5.15.0,255.255.255.0,eq,https
gl2,7,permit,10.0.0.0,255.0.0.0,1.1.1.1,255.255.255.255,eq,https
gl2,7,permit,10.0.0.0,255.0.0.0,5.5.5.1,255.255.255.255,eq,https
gl2,7,permit,10.0.0.0,255.0.0.0,5.5.15.0,255.255.255.0,eq,https
gl2,8,permit,0.0.0.0,0.0.0.0,10.0.0.0,255.255.255.0,eq,121
gl2,9,permit,10.0.0.0,255.0.0.0,0.0.0.0,0.0.0.0,eq,https
gl2,10,permit,0.0.0.0,0.0.0.0,1.1.1.1,255.255.255.255,eq,https
gl2,10,permit,0.0.0.0,0.0.0.0,5.5.5.1,255.255.255.255,eq,https
gl2,10,permit,0.0.0.0,0.0.0.0,5.5.15.0,255.255.255.0,eq,https
gl2,11,deny,0.0.0.0,0.0.0.0,0.0.0.0,0.0.0.0,eq,ip

In Cisco ASA, it is not possible to create duplicate rows in an access list, but you can create multiple objects with the same IP address, and you can create multiple duplicate rows in an access list using different objects. Within the CSV file, you can sort the rows by the fields you need and easily find duplicate rows.

Why not use textFSM ? It has a completely different output, not suitable for this task.

Why not use pyats/genie. There is no parser for this command.

Any comments and questions are welcome.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *