rnwolfe / fmc-tools

A collection of tools for common tasks needed on the Cisco Firepower Management Center using a fork of the fireREST library.
40 stars 13 forks source link

Multiple Source & Destination Networks export to csv as 'any' #6

Closed bradrel closed 2 years ago

bradrel commented 3 years ago

Hello! We are trying to use the export-acp-to-csv.py script but seem to have an issue with the entries that have multiple Source or Destination Network addresses. When these entries are export, they are listed in the csv file as 'any'. In the FMC interface, this is done by adding multiple IP addresses in the "Networks" section. Is there any way to get these entries exported properly with this script? Thanks in advance.

rnwolfe commented 3 years ago

Hey @bradrel it's been a while since I've worked on this; however, the script was written to take any items provided via the API as source/destination networks and put them into a list (example: item1, item2, item3, etc.). This worked in my initial testing, as can be seen in this example.

This could be due to differences in the API's returned data after some FMC upgrades. The relevant code for this is here if you want to mess with it and see if you can get it to work the way you'd like. https://github.com/rnwolfe/fmc-tools/blob/e0ff19cd21e458bb4dbaec98b470d62653947d8e/export-acp-to-csv.py#L76-L92

You could probably put a print(rule['sourceNetworks']) what the data is getting is and then tweak as needed.

Hopefully, this points you in the right direction as I haven't messed with this in a good while, and don't have a readily available test environment for me to work against.

Let me know if you need further help.

Westy-87 commented 2 years ago

I created a GitHub just to show the changes I made to this script.

I examined the JSON output from my FMC API request and these are the elements that I've seen so far:

sourceZones
    objects
        id
        name
        type            #eg SecurityZone

destinationZones
    objects
        id
        name
        type            #eg SecurityZone

sourceNetworks
    objects
        id
        name
        overridable #true;false
        type            #network;Host;NetworkGroup
    literals
        type            #host
        value

destinationNetworks
    objects
        id
        name
        overridable #true;false
        type            #network;NetworkGroup;FQDN;Range
    literals
        type            #host
        value       #eg 10.10.10.10

sourcePorts
    objects
        id
        name        #eg MSRDP, rdp, SCCM-Remote-Assistance
        overridable #true;false
        [protocol]      #(May not be present) eg TCP
        type            #ProtocolPortObject;
    literals
        [port]      #(May not be present) eg 8880
        protocol        ###Represented as a number corresponding to list found here https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
        type        #eg PortLiteral

destinationPorts
    objects
        id
        name
        overridable
        [protocol]
        type
    literals
        port
        protocol
        type

I had to do a bit of change to get mine working correctly. Excuse any weird coding as I have not coded since highschool 20 years ago and this is the first I've done since then:

#Add these as needed from https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
portTypes = {'1': 'ICMP','6': 'TCP','17': 'UDP','50': 'ESP'}
        # Source Networks
        line['sourceNetworks'] = []
        if 'sourceNetworks' in rule:
            for srcNets in rule['sourceNetworks']:
                if 'objects' in srcNets:
                    for item in rule['sourceNetworks']['objects']:
                        # Put each object in a list, will join to str when printing to CSV
                        line['sourceNetworks'].append(item['name'])
                if 'literals' in srcNets:
                    for item in rule['sourceNetworks']['literals']:
                        # Put each object in a list, will join to str when printing to CSV
                        line['sourceNetworks'].append(item['value'])
        else:
            line['sourceNetworks'] = ['any']

        # Destination Networks
        line['destNetworks'] = []
        if 'destinationNetworks' in rule:
            for dstNets in rule['destinationNetworks']:
                if 'objects' in dstNets:
                    for item in rule['destinationNetworks']['objects']:
                        # Put each object in a list, will join to str when printing to CSV
                        line['destNetworks'].append(item['name'])
                if 'literals' in dstNets:
                    for item in rule['destinationNetworks']['literals']:
                        # Put each object in a list, will join to str when printing to CSV
                        line['destNetworks'].append(item['value'])
        else:
            line['destNetworks'] = ['any']

        # Source Ports
        line['sourcePorts'] = []
        if 'sourcePorts' in rule:
            for srcPorts in rule['sourcePorts']:
                if 'objects' in srcPorts:
                    for item in rule['sourcePorts']['objects']:
                        try:
                            # line['sourcePorts'].append(item['protocol']+"_"+item['name'])
                            line['sourcePorts'].append(item['name'])
                        except KeyError:
                            line['sourcePorts'].append(item['name'])
            for srcPorts in rule['sourcePorts']:
                if 'literals' in srcPorts:
                    for item in rule['sourcePorts']['literals']:
                        try:
                            line['sourcePorts'].append(portTypes[item['protocol']]+"_"+item['port'])
                        except KeyError:
                            line['sourcePorts'].append(portTypes[item['protocol']])
        else:
            line['sourcePorts'] = ['any']

        # Destination Ports
        line['destPorts'] = []
        if 'destinationPorts' in rule:
            for dstPorts in rule['destinationPorts']:
                if 'objects' in dstPorts:
                    for item in rule['destinationPorts']['objects']:
                        try:
                            # line['destPorts'].append(item['protocol']+"_"+item['name'])
                            line['destPorts'].append(item['name'])
                        except KeyError:
                            line['destPorts'].append(item['name'])
            for dstPorts in rule['destinationPorts']:
                if 'literals' in dstPorts:
                    for item in rule['destinationPorts']['literals']:
                        try:
                            line['destPorts'].append(portTypes[item['protocol']]+"_"+item['port'])
                        except KeyError:
                            line['destPorts'].append(portTypes[item['protocol']])
        else:
            line['destPorts'] = ['any']

These changes were required because sometimes you have objects, sometimes you have literals, sometimes you have both.

Sometimes source/dest port objects don't have protocol, sometimes the literals don't have port number. I like the readability of the final output like this.

rnwolfe commented 2 years ago

@Westy-87 Thanks for the code extension provided here.

You are right, there can be multiple things provided from/to the API for objects, literals, etc.

It wasn't something I was concerned with at the time I used this, but it will be nice to others to reference this if needed!

Also, welcome to GitHub, @Westy-87!

bradrel commented 2 years ago

@Westy-87 THANK YOU! This worked great.

The only think I changed was to add some more portTypes: portTypes = {'1': 'ICMP','2': 'IGMP','6': 'TCP','17': 'UDP','47': 'GRE','50': 'ESP'}