From 4198f0cb4f7e3a787e3669fcff540cead0458ccd Mon Sep 17 00:00:00 2001 From: Hannu Kamarainen Date: Fri, 16 Oct 2020 14:33:38 +0200 Subject: [PATCH 1/3] PA: Add support for source port handling --- capirca/lib/aclcheck.py | 4 +- capirca/lib/paloaltofw.py | 103 ++++++++++++++++++++++---------------- 2 files changed, 62 insertions(+), 45 deletions(-) diff --git a/capirca/lib/aclcheck.py b/capirca/lib/aclcheck.py index 6ad67f75..24fdef90 100644 --- a/capirca/lib/aclcheck.py +++ b/capirca/lib/aclcheck.py @@ -184,7 +184,7 @@ def ActionMatch(self, action='any'): for match in self.matches: if match.action: if not match.possibles: - if action is 'any' or action in match.action: + if action == 'any' or action in match.action: match_list.append(match) return match_list @@ -249,7 +249,7 @@ def _AddrInside(self, addr, addresses): Returns: bool: True of false """ - if addr is 'any': return True # always true if we match for any addr + if addr == 'any': return True # always true if we match for any addr if not addresses: return True # always true if term has nothing to match for ip in addresses: # ipaddr can incorrectly report ipv4 as contained with ipv6 addrs diff --git a/capirca/lib/paloaltofw.py b/capirca/lib/paloaltofw.py index 3b09c65f..977991f3 100644 --- a/capirca/lib/paloaltofw.py +++ b/capirca/lib/paloaltofw.py @@ -19,6 +19,8 @@ from capirca.lib import aclgenerator from capirca.lib import nacaddr +from capirca.lib import naming +from capirca.lib import policy class Error(Exception): @@ -127,27 +129,33 @@ def _FormattedGroup(el): class Service(object): """Generate PacketFilter policy terms.""" service_map = {} - - def __init__(self, ports, service_name, - protocol): # ports is a tuple of ports - if (ports, protocol) in self.service_map: - raise PaloAltoFWDuplicateServiceError( - ("You have a duplicate service. " - "A service already exists on port(s): %s") - % str(ports)) - - final_service_name = "service-" + service_name + "-" + protocol - - for unused_k, v in Service.service_map.items(): - if v["name"] == final_service_name: - raise PaloAltoFWDuplicateServiceError( - "You have a duplicate service. A service named %s already exists." % - str(final_service_name)) - - if len(final_service_name) > 63: + definitions = naming.Naming(policy.DEFAULT_DEFINITIONS) + + def __init__(self, src_ports, dst_ports, protocol): + src_service_name = 'ANY' if not src_ports else None + dst_service_name = 'ANY' if not dst_ports else None + + for svc_name, ports in self.definitions.services.items(): + if src_service_name is not None and dst_service_name is not None: + break + if src_ports and src_service_name is None and f'{src_ports[0]}/{protocol}' in ports.items: + src_service_name = svc_name + if dst_ports and dst_service_name is None and f'{dst_ports[0]}/{protocol}' in ports.items: + dst_service_name = svc_name + + service_name = f'{src_service_name}_TO_{dst_service_name}_{protocol.upper()}' + + if len(service_name) > 63: + # combined human-readable service name will be too long. fallback to + # using ports instead of names, which will always be short enough. + src_service_name = 'ANY' if not src_ports or '0-65535' in str(src_ports) else "_".join(src_ports) + dst_service_name = 'ANY' if not dst_ports or '0-65535' in str(dst_ports) else "_".join(dst_ports) + service_name = f'SVC_{src_service_name}_TO_{dst_service_name}_{protocol.upper()}' + + if len(service_name) > 63: raise PaloAltoFWTooLongName("Service name must be 63 characters max: %s" % - str(final_service_name)) - self.service_map[(ports, protocol)] = {"name": final_service_name} + str(service_name)) + self.service_map[(src_ports, dst_ports, protocol)] = {"name": service_name} class Rule(object): @@ -203,25 +211,33 @@ def ModifyOptions(self, terms): for pan_app in term.pan_application: self.options["application"].append(pan_app) + src_ports = [] + if term.source_port: + for tup in term.source_port: + if len(tup) > 1 and tup[0] != tup[1]: + src_ports.append(str(tup[0]) + "-" + str(tup[1])) + else: + src_ports.append(str(tup[0])) + + dst_ports = [] if term.destination_port: - ports = [] for tup in term.destination_port: if len(tup) > 1 and tup[0] != tup[1]: - ports.append(str(tup[0]) + "-" + str(tup[1])) - else: - ports.append(str(tup[0])) - ports = tuple(ports) - - # check to see if this service already exists - for p in term.protocol: - if (ports, p) in Service.service_map: - self.options["service"].append(Service.service_map[(ports, p)][ - "name"]) + dst_ports.append(str(tup[0]) + "-" + str(tup[1])) else: + dst_ports.append(str(tup[0])) + + if src_ports or dst_ports: + # convert to tuple so that they are usable as dict keys + src_ports = tuple(src_ports) + dst_ports = tuple(dst_ports) + for prot in term.protocol: + ports = (src_ports, dst_ports, prot) + if ports not in Service.service_map: # create service - unused_new_service = Service(ports, term.name, p) - self.options["service"].append(Service.service_map[(ports, p)][ - "name"]) + Service(src_ports, dst_ports, prot) + self.options["service"].append(Service.service_map[ports]["name"]) + if term.protocol: if term.protocol[0] == "icmp": self.options["application"].append("ping") @@ -394,6 +410,7 @@ def _TranslatePolicy(self, pol, exp_info): "icmp-type": normalized_icmptype, "timeout": term.timeout }) + self.pafw_policies.append((header, new_terms, filter_options)) # create Palo Alto Firewall Rule object for term in new_terms: @@ -495,6 +512,7 @@ def __str__(self): # sort address books and address sets address_book_groups_dict = collections.OrderedDict( sorted(address_book_groups_dict.items())) + address_book_keys = sorted( list(address_book_names_dict.keys()), key=self._SortAddressBookNumCheck) @@ -528,16 +546,15 @@ def __str__(self): service.append(self.INDENT * 5 + "") service.append(self.INDENT * 5 + "") - for k, v in Service.service_map.items(): - service.append(self.INDENT * 6 + '') + for (src_ports, dst_ports, protocol), service_dict in Service.service_map.items(): + service.append(self.INDENT * 6 + '') service.append(self.INDENT * 7 + "") - service.append(self.INDENT * 8 + "<" + k[1] + ">") - tup = str(k[0])[1:-1] - if tup[-1] == ",": - tup = tup[:-1] - service.append(self.INDENT * 9 + "" + tup.replace("'", "") + - "") - service.append(self.INDENT * 8 + "") + service.append(self.INDENT * 8 + "<" + protocol + ">") + if src_ports: + service.append(self.INDENT * 9 + "" + ','.join(src_ports) + "") + if dst_ports: + service.append(self.INDENT * 9 + "" + ','.join(dst_ports) + "") + service.append(self.INDENT * 8 + "") service.append(self.INDENT * 7 + "") service.append(self.INDENT * 6 + "") service.append(self.INDENT * 5 + "") From 5232c0db8fc91223af6c963617d916d5c37a0f12 Mon Sep 17 00:00:00 2001 From: Hannu Kamarainen Date: Fri, 19 Feb 2021 10:58:13 +0100 Subject: [PATCH 2/3] PA: Destination port can not be omitted in service. For consistency, also use 'any' as value for source-port instead of omitting it, even though source-port is possible to omit completely --- capirca/lib/paloaltofw.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/capirca/lib/paloaltofw.py b/capirca/lib/paloaltofw.py index 977991f3..8fd810b7 100644 --- a/capirca/lib/paloaltofw.py +++ b/capirca/lib/paloaltofw.py @@ -552,8 +552,12 @@ def __str__(self): service.append(self.INDENT * 8 + "<" + protocol + ">") if src_ports: service.append(self.INDENT * 9 + "" + ','.join(src_ports) + "") + else: + service.append(self.INDENT * 9 + "any") if dst_ports: service.append(self.INDENT * 9 + "" + ','.join(dst_ports) + "") + else: + service.append(self.INDENT * 9 + "any") service.append(self.INDENT * 8 + "") service.append(self.INDENT * 7 + "") service.append(self.INDENT * 6 + "") From f05f1c34a6f1734c77b75b747f4b64c0cda1e874 Mon Sep 17 00:00:00 2001 From: Hannu Kamarainen Date: Wed, 24 Feb 2021 11:18:53 +0100 Subject: [PATCH 3/3] PA: Fix case where initialization of class Service crashes due to default directory './def' not existing in current working directory --- capirca/lib/paloaltofw.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/capirca/lib/paloaltofw.py b/capirca/lib/paloaltofw.py index 8fd810b7..5f8ab25b 100644 --- a/capirca/lib/paloaltofw.py +++ b/capirca/lib/paloaltofw.py @@ -17,10 +17,16 @@ import datetime import logging +from absl import flags + from capirca.lib import aclgenerator from capirca.lib import nacaddr from capirca.lib import naming from capirca.lib import policy +from capirca.utils import config + + +FLAGS = flags.FLAGS class Error(Exception): @@ -129,13 +135,19 @@ def _FormattedGroup(el): class Service(object): """Generate PacketFilter policy terms.""" service_map = {} - definitions = naming.Naming(policy.DEFAULT_DEFINITIONS) + definitions = None def __init__(self, src_ports, dst_ports, protocol): + if Service.definitions is None: + # read the definitions again, since the previously parsed + # definitions are not easily accessible through other means. + configs = config.generate_configs(FLAGS) + Service.definitions = naming.Naming(configs.get('definitions_directory')) + src_service_name = 'ANY' if not src_ports else None dst_service_name = 'ANY' if not dst_ports else None - for svc_name, ports in self.definitions.services.items(): + for svc_name, ports in Service.definitions.services.items(): if src_service_name is not None and dst_service_name is not None: break if src_ports and src_service_name is None and f'{src_ports[0]}/{protocol}' in ports.items: