Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ASA Template: Object-Groups Getting Mixed Up #55

Open
consentfactory opened this issue Aug 11, 2021 · 20 comments
Open

ASA Template: Object-Groups Getting Mixed Up #55

consentfactory opened this issue Aug 11, 2021 · 20 comments

Comments

@consentfactory
Copy link

Have a strange issue with an ASA template that I suspect is likely related to my template, but I'm unsure.

The gist is that I'm trying to get service and network object-groups into json, and generally they work, but I seem to have this issue where network group-objects end up in the service group objects.

Any tips on what I'm doing wrong (and how to improve it) would be great. Thanks in advance.

The code:

Input:
object-group service gokuhead
 service-object tcp-udp destination eq gokurpc 
 service-object tcp destination eq 902 
 service-object tcp destination eq https 
 service-object tcp destination eq nfs 
 service-object tcp destination eq 10025 
object-group network gohan
 network-object object gohan-01
 network-object object gohan-02
 network-object object vlan_944
 network-object object gohan-03
 network-object object gohan-05
 network-object object gohan-06
object-group service sql tcp
 port-object eq 1433
object-group network vegeta
 group-object trunks
 network-object object vegeta-01
object-group network Space-Users
 network-object object ab
 network-object object ac
 network-object object ad
 network-object object ae
 network-object object af
 network-object object ag
 network-object object ah
 network-object object ai
 network-object object aj
object-group network dalmatians
 network-object object dog-01
 group-object trunks
 network-object object vlan_950
 group-object Space-Users
 network-object object Darts-Summary

Template:
<vars>
SVC_PORTS = "tcp-udp|tcp|udp"
RANGE = "range"
OBJECT = "object"
</vars>

<group name="objects">

<group name="service-objects.{{svc_name}}**">
object service {{ svc_name | _line_ | joinmatches | _start_ }}
 description {{ description | ORPHRASE }}
 service {{protocol | re("SVC_PORTS") }} destination eq {{port}}
 service {{protocol | re("SVC_PORTS") }} destination range {{port_begin}} {{port_end}}
</group>

<group name="network-objects.{{net_name}}**">
object network {{ net_name | _line_ | joinmatches | _start_ }}
 description {{ description | ORPHRASE }}
 fqdn v4 {{fqdn}}
 host {{ip | IP}}
 host {{host | WORD}}
 subnet {{ip | IP}} {{ subnet | IP }}
 subnet {{ipv6 | PREFIXV6 }}
</group>

</group>



<group name="object-groups">

<group name="object-network-groups**.{{ network_name }}">
object-group network {{ network_name | _start_ }}
 description {{ description | ORPHRASE }}
 <group name="network-objects" itemize="obj_name">
 network-object object {{ obj_name }}
 network-object {{obj_name | exclude("OBJECT") }}
 </group>
 <group name="network-group-objects" itemize="net_obj_name">
 group-object {{ net_obj_name }}
 </group>
 <group name="network-object-networks*">
 network-object {{ ip | IP }} {{ subnet | IP }}
 </group>
 <group name="host-objects*" itemize="obj_name">
 network-object host {{ obj_name | WORD }}
 network-object host {{ obj_name | IP }}
 </group>
</group>

<group name="object-service-groups**.{{ service_name }}">

object-group service {{ service_name }} {{ protocol | re("SVC_PORTS") | _start_ }}
 description {{ description | ORPHRASE }}
 <group name="service-group-objects" itemize="svc_group_objs">
 group-object {{ svc_group_objs }}
 </group>
 <group name="service-port-objects" itemize="port_obj">
 port-object eq {{ port_obj }}
 </group>
 <group name="service-port-range*">
 port-object range {{ port_begin }} {{ port_end }}
 </group>
 <group name="service-protocol-objects" itemize="protocol_obj">
 protocol-object {{ protocol_obj }}
 </group>
object-group service {{ service_name | _start_ }}
 description {{ description | ORPHRASE }}
 <group name="service-group-objects" itemize="group_objs">
 group-object {{ group_objs }}
 </group>
 <group name="service-port-objects" itemize="port_obj">
 port-object eq {{ port_obj }}
 </group>
 <group name="service-port-range*">
 port-object range {{ port_begin }} {{ port_end }}
 </group>
 <group name="service-protocol-objects" itemize="protocol_obj">
 protocol-object {{ protocol_obj }}
 </group>
 <group name="service-objects*" itemize="object">
 service-object object {{ object }}
 service-object {{ object | exclude("OBJECT")}}
 </group>
 <group name="service-object-ports*">
 service-object {{ protocol | re("SVC_PORTS") }} destination eq {{port}}
 </group>
 <group name="service-object-port-ranges*">
 service-object {{ protocol | re("SVC_PORTS") }} destination range {{port_begin}} {{port_end}}
 </group>
</group>

<group name="object-protocol-groups**.{{ protocol_obj-group_name }}">
object-group protocol {{protocol_obj-group_name | _start_}}
 <group name="protocol-objects" itemize="protocol_object">
 protocol-object {{protocol_object}}
 </group>
</group>

</group>

Output:
[
    [
        {
            "object-groups": {
                "object-network-groups": {
                    "Space-Users": {
                        "network-objects": [
                            "ab",
                            "ac",
                            "ad",
                            "ae",
                            "af",
                            "ag",
                            "ah",
                            "ai",
                            "aj"
                        ]
                    },
                    "dalmatians": {
                        "network-objects": [
                            "dog-01",
                            "vlan_950",
                            "Darts-Summary"
                        ]
                    },
                    "gohan": {
                        "network-objects": [
                            "gohan-01",
                            "gohan-02",
                            "vlan_944",
                            "gohan-03",
                            "gohan-05",
                            "gohan-06"
                        ]
                    },
                    "vegeta": {
                        "network-group-objects": [
                            "trunks"
                        ],
                        "network-objects": [
                            "vegeta-01"
                        ]
                    }
                },
                "object-service-groups": {
                    "sql": {
                        "protocol": "tcp",
                        "service-group-objects": [
                            "trunks",
                            "Space-Users"
                        ],
                        "service-port-objects": [
                            "1433"
                        ]
                    }
                }
            }
        }
    ]
]
@dmulyalin
Copy link
Owner

Hi,

Try this template:

<vars>
SVC_PORTS = "tcp-udp|tcp|udp"
</vars>

<group name="object-{{ object_type }}-groups**.{{ object_name }}**">
object-group {{ object_type }} {{ object_name | _start_ }}
object-group {{ object_type }} {{ object_name | _start_ }} {{ protocol | re("SVC_PORTS")}}
 description {{ description | re(".*") }}

 <group name="network-objects" itemize="obj_name" method="table">
 network-object object   {{ obj_name | }}
 network-object host     {{ obj_name | IP }}
 </group> 

 <group name="group-objects" itemize="obj_name" method="table">
 group-object            {{ obj_name }}
 </group>
 
 <group name="group-objects" itemize="obj_name" method="table">
 service-object object   {{ obj_name }}
 service-object          {{ obj_name }}
 </group>

 <group name="service-object-ports*">
 service-object {{ protocol | re("SVC_PORTS") }} destination eq {{port}}
 </group>
 
 <group name="service-object-port-ranges*">
 service-object {{ protocol | re("SVC_PORTS") }} destination range {{port_begin}} {{port_end}}
 </group>

 <group name="service-port-objects" itemize="port_obj">
 port-object eq {{ port_obj }}
 </group>
 
</group>

it gives these results:

[[{'object-network-groups': {'Space-Users': {'network-objects': ['ab',
                                                                 'ac',
                                                                 'ad',
                                                                 'ae',
                                                                 'af',
                                                                 'ag',
                                                                 'ah',
                                                                 'ai',
                                                                 'aj']},
                             'dalmatians': {'group-objects': ['trunks',
                                                              'Space-Users'],
                                            'network-objects': ['dog-01',
                                                                'vlan_950',
                                                                'Darts-Summary']},
                             'gohan': {'network-objects': ['gohan-01',
                                                           'gohan-02',
                                                           'vlan_944',
                                                           'gohan-03',
                                                           'gohan-05',
                                                           'gohan-06']},
                             'vegeta': {'group-objects': ['trunks'],
                                        'network-objects': ['vegeta-01']}},
   'object-service-groups': {'gokuhead': {'service-object-ports': [{'port': 'gokurpc',
                                                                    'protocol': 'tcp-udp'},
                                                                   {'port': '902',
                                                                    'protocol': 'tcp'},
                                                                   {'port': 'https',
                                                                    'protocol': 'tcp'},
                                                                   {'port': 'nfs',
                                                                    'protocol': 'tcp'},
                                                                   {'port': '10025',
                                                                    'protocol': 'tcp'}]},
                             'sql': {'protocol': 'tcp',
                                     'service-port-objects': ['1433']}}}]]

Key difference is using dynamic path to encode object type as well:

<group name="object-{{ object_type }}-groups**.{{ object_name }}**">

In you original template lines

 group-object trunks
 group-object Space-Users

matched under object-service-groups because both object-service-groups and object-network-groups having this pattern defined:

 group-object {{ net_obj_name }}
 group-object {{ svc_group_objs }}

which translates to same regular expression.

TTP processes top object-service-groups and object-network-groups groups independently collecting all matches for them, combining these groups in a single group using dynamic path allows to obtain better results.

dmulyalin added a commit that referenced this issue Aug 15, 2021
…een words, now instead of matching '\\ +' only TTP accounts for tabs between words as well '[ \t]+'
@consentfactory
Copy link
Author

That makes sense, and I can actually see where I could use more dynamic pathing for some other templates.

Thank you for taking the time to create that and explain it!

@consentfactory
Copy link
Author

consentfactory commented Aug 16, 2021

Quick follow-up: is possible to combine matched items into a new object?

For example, objects in ASA configs can also appear as IPs and subnets:

object-group network Test-Group
 network-object 192.168.168.1 255.255.255.0

So I create a template item under the group like this:

network-object {{ obj_ip | IP }} {{ obj_subnet | to_cidr }}

But what would be really nice for my purposes is to concatenate obj_ip and obj_subnet into a new item called obj_name so they can appear in the list of network-objects.

Ideally, I would be able to add a backslash to have an IP in full CIDR notation like this: 192.168.168.1/24.

Is that possible? I haven't yet found documentation on something like this yet.

@dmulyalin
Copy link
Owner

Have a look at to_ip function

network-object {{ ip | PHRASE | to_ip | with_prefixlen }}

@consentfactory
Copy link
Author

That's perfect. Thank you.

That said, in general is it possible to concatenate matches?

@dmulyalin
Copy link
Owner

There is joinmatches function available that can help in certain cases, also group function sformat can concatenate matches in a string, but in your case you not only need to concatenate but also translate mask to prefix length value, so only to_ip will do the trick.

@consentfactory
Copy link
Author

I'll check that out. Thanks again.

@consentfactory
Copy link
Author

consentfactory commented Aug 18, 2021

I might have closed this early.

I can't seem to make this work.

network-object {{ ip | PHRASE | to_ip | with_prefixlen }}

Using this input:

object-group network gohan
 network-object 192.168.168.1 255.255.255.255
 network-object 192.168.168.2 255.255.255.255
 network-object host 192.168.168.3
 network-object object gohan-01
 network-object object gohan-02
 network-object object vlan_944
 network-object object gohan-03
 network-object object gohan-05
 network-object object gohan-06

I put the above template into this template:

<group name="object-{{ object_type }}-groups**.{{ object_name }}**">
object-group {{ object_type }} {{ object_name | _start_ }}
object-group {{ object_type }} {{ object_name | _start_ }} {{ protocol | re("SVC_PORTS")}}
 description {{ description | re(".*") }}

 <group name="network-objects" itemize="obj_name" method="table">
 network-object object {{ obj_name | WORD }}
 network-object host {{ obj_name | IP }}
 network-object {{ obj_name | PHRASE | to_ip | with_prefixlen }}
 </group>

 <group name="group-objects" itemize="obj_name" method="table">
 group-object            {{ obj_name }}
 </group>
 
 <group name="group-objects" itemize="obj_name" method="table">
 service-object object   {{ obj_name }}
 service-object          {{ obj_name }}
 </group>

 <group name="service-object-ports*">
 service-object {{ protocol | re("SVC_PORTS") }} destination eq {{port}}
 </group>
 
 <group name="service-object-port-ranges*">
 service-object {{ protocol | re("SVC_PORTS") }} destination range {{port_begin}} {{port_end}}
 </group>

 <group name="service-port-objects" itemize="port_obj">
 port-object eq {{ port_obj }}
 </group>
 
</group> 

I get an error from the Python ipaddress module:

 ValueError: 'object/gohan-01' does not appear to be an IPv4 or IPv6 interface

# Also:

 ValueError: 'host/192.168.168.3' does not appear to be an IPv4 or IPv6 interface

What I don't understand is why network-object object {{ obj_name | WORD }} or network-object host {{ obj_name | IP }} isn't processing this. Those are more specific matches than the third template line network-object {{ obj_name | PHRASE | to_ip | with_prefixlen }}.

Once again, appreciate your help with this. Definitely learning a lot about ttp than what I knew before.

@consentfactory
Copy link
Author

consentfactory commented Aug 18, 2021

Was able to get the IP prefix to work by adding a variable with a regex pattern of words to exclude, then using that in the template to exclude. Is this the right approach?

<vars>
OBJECT_EXCLUDE = "host|object"
</vars>

<group name="object-groups">

<group name="object-{{ object_type }}-groups**.{{ object_name }}**">
object-group {{ object_type }} {{ object_name | _start_ }}
object-group {{ object_type }} {{ object_name | _start_ }} {{ protocol | re("SVC_PORTS")}}
 description {{ description | re(".*") }}

 <group name="network-objects" itemize="obj_name" method="table">
 network-object object {{ obj_name | WORD }}
 network-object host {{ obj_name | IP }}
 network-object {{ obj_name | PHRASE | exclude_re('OBJECT_EXCLUDE') | to_ip | with_prefixlen }}
 </group>
</group>

@dmulyalin
Copy link
Owner

Yes, that one of the options. You need to apply match filtering here, either using regexes or pattern check, for instance this should do the trick as well
network-object {{ obj_name | PHRASE | contains(".") | to_ip | with_prefixlen }}

@SudarshanVK
Copy link

I echo what @consentfactory is saying.. i have learnt a lot from this thread.. @consentfactory : are you able to share the final template that you are using?

@dmulyalin
Copy link
Owner

@SudarshanVK would you mind sharing more info on these:

  • sample of data you parsing
  • results structure you expecting to produce with TTP
  • template you tried so far

@consentfactory
Copy link
Author

consentfactory commented Sep 5, 2021

I echo what @consentfactory is saying.. i have learnt a lot from this thread.. @consentfactory : are you able to share the final template that you are using?

Yes, I have a template for the ASA, but I can't share it yet due to a project I'm working on. I'll update this thread with it when I upload the template*.

@SudarshanVK
Copy link

@dmulyalin : i am not able to share the configuration file unfortunately. But, here is the template i am using so far.
I am yet to work out a template for ACL, user groups, OSPF configuration.
I am hoping for a structure that would be easy to output to a spreadsheet for further analysis.

PS: I started looking into TTP only 4 days ago.

<vars>
SVC_PORTS = "tcp-udp|tcp|udp"
OBJECT_EXCLUDE = "host|object"
</vars>

<group name="global">
hostname {{hostname}}
</group>

<group name="interface.PortChannel">
interface Port-channel{{ number | DIGIT }}.{{ subinterface | DIGIT }}
 nameif {{ name }}
 security-level {{ security_level }}
 ip address {{ ip | PHRASE | to_ip | with_prefixlen }}
 ospf network {{ ospf_type }}
</group>

<group name="object-{{ object_type }}-groups**.{{ object_name }}**">
object-group {{ object_type }} {{ object_name | _start_ }}
object-group {{ object_type }} {{ object_name | _start_ }} {{ protocol | re("SVC_PORTS")}}
 description {{ description | re(".*") }}

 <group name="network-objects" itemize="obj_name" method="table">
 network-object object   {{ obj_name | WORD}}
 network-object host     {{ obj_name | IP }}
 network-object {{ obj_name | PHRASE | exclude_re('OBJECT_EXCLUDE') | to_ip | with_prefixlen }}
 </group> 

 <group name="group-objects" itemize="obj_name" method="table">
 group-object            {{ obj_name }}
 </group>
 
 <group name="group-objects" itemize="obj_name" method="table">
 service-object object   {{ obj_name }}
 service-object          {{ obj_name }}
 </group>

 <group name="service-object-ports*">
 service-object {{ protocol | re("SVC_PORTS") }} destination eq {{port}}
 </group>
 
 <group name="service-object-port-ranges*">
 service-object {{ protocol | re("SVC_PORTS") }} destination range {{port_begin}} {{port_end}}
 </group>

 <group name="service-port-objects" itemize="port_obj">
 port-object eq {{ port_obj }}
 </group>
 
</group>


<output
format="excel"
returner="file"
filename="ASA_report.xslx"
url="./"
load="yaml"
>
table:
  - path: interface.PortChannel
    tab_name: PortChannel
</output>

@dmulyalin
Copy link
Owner

@SudarshanVK That template of yours is quiet sophisticated, looks like you picked up quiet a bit of TTP so far.

What is the problem with above template, what exactly does not work in it for you? Also, would strongly advise against sharing any actual devices configs here, good idea is to anonymize them before doing so.

Will need sample data with template and desired results structure to help with building the template, without it cannot do much apart from general advice.

If you Guys finish doing the template for ASA, mind to think about contributing them to TTP Templates repo, if you think that it would be good for other people to benefit from your work.

structure that would be easy to output to a spreadsheet - that structure should be a list of dictionaries in that case, make sure to align your templates toward that format.

@SudarshanVK
Copy link

@dmulyalin i have a 80 context firewall to migrate.. I am still developing the template and absolutely i am keen to share it with everyone once i have everything working.. I might reach-out over here in case i run into issues with the rest of the configuration that I am trying to Parse... Thank you thus far...

@consentfactory
Copy link
Author

If you Guys finish doing the template for ASA, mind to think about contributing them to TTP Templates repo, if you think that it would be good for other people to benefit from your work.

I would be more than happy to help contribute to the templates.

That said, the most difficult problem I face with ASA configs is the access-list configurations. I'm not sure if there are issues with the way my template is written (possible), or if the configs are just that complicated because there are lots, and I mean lots, of variations in how the configuration can be written, and I'm not sure I've captured all of them ( I think I have ~100 lines of variations, not including another set of lines that append these lines with 'log debugging').

Would be great to open-source that for someone else to take a look and improve them.

@consentfactory
Copy link
Author

consentfactory commented Oct 12, 2021

Here is the ASA template I've put together:
https://gist.github.com/consentfactory/85872fc83453d1735b15aed3e47a9763

@showipintbri
Copy link

@consentfactory I'm having some errors trying to load your template. This is even before I pass it any configs to parse.

from ttp import ttp

ttp_template = """
[insert contents of your linked gist from above]
"""

parser = ttp()
parser.add_template(ttp_template)

The resulting error:

Traceback (most recent call last):
  File "C:\Users\my_username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\ttp\ttp.py", line 1324, in construct_etree
    template_ET = ET.XML(template_text)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.2800.0_x64__qbz5n2kfra8p0\lib\xml\etree\ElementTree.py", line 1320, in XML
    parser.feed(text)
xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 2, column 1

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:/Users/my_username/Desktop/Python/New Stuff/ttp_test.py", line 368, in <module>
    parser.add_template(ttp_template)
  File "C:\Users\my_username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\ttp\ttp.py", line 347, in add_template
    template_obj = _template_class(
  File "C:\Users\my_username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\ttp\ttp.py", line 906, in __init__
    self.template = self.handle_extend(template_text)
  File "C:\Users\my_username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\ttp\ttp.py", line 1278, in handle_extend
    template_ET = self.construct_etree(template_text)
  File "C:\Users\my_username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\ttp\ttp.py", line 1331, in construct_etree
    template_ET = ET.XML("<template>\n{}\n</template>".format(template_text))    
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.2800.0_x64__qbz5n2kfra8p0\lib\xml\etree\ElementTree.py", line 1320, in XML
    parser.feed(text)
xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 26, column 9

Any ideas?

@showipintbri
Copy link

^Ignore.

I closed my laptop up for the day and went home.

Once home, I re-did everything and it is all now working. 🤷 Operator error? possibly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants