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

PA: Add support for source port handling #238

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions capirca/lib/aclcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
119 changes: 76 additions & 43 deletions capirca/lib/paloaltofw.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +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):
Expand Down Expand Up @@ -127,27 +135,39 @@ 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 = 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 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:
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):
Expand Down Expand Up @@ -203,25 +223,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")
Expand Down Expand Up @@ -394,6 +422,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:
Expand Down Expand Up @@ -495,6 +524,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)

Expand Down Expand Up @@ -528,16 +558,19 @@ def __str__(self):
service.append(self.INDENT * 5 + "<!-- Services -->")

service.append(self.INDENT * 5 + "<service>")
for k, v in Service.service_map.items():
service.append(self.INDENT * 6 + '<entry name="' + v["name"] + '">')
for (src_ports, dst_ports, protocol), service_dict in Service.service_map.items():
service.append(self.INDENT * 6 + '<entry name="' + service_dict["name"] + '">')
service.append(self.INDENT * 7 + "<protocol>")
service.append(self.INDENT * 8 + "<" + k[1] + ">")
tup = str(k[0])[1:-1]
if tup[-1] == ",":
tup = tup[:-1]
service.append(self.INDENT * 9 + "<port>" + tup.replace("'", "") +
"</port>")
service.append(self.INDENT * 8 + "</" + k[1] + ">")
service.append(self.INDENT * 8 + "<" + protocol + ">")
if src_ports:
service.append(self.INDENT * 9 + "<source-port>" + ','.join(src_ports) + "</source-port>")
else:
service.append(self.INDENT * 9 + "<source-port>any</source-port>")
if dst_ports:
service.append(self.INDENT * 9 + "<port>" + ','.join(dst_ports) + "</port>")
else:
service.append(self.INDENT * 9 + "<port>any</port>")
service.append(self.INDENT * 8 + "</" + protocol + ">")
service.append(self.INDENT * 7 + "</protocol>")
service.append(self.INDENT * 6 + "</entry>")
service.append(self.INDENT * 5 + "</service>")
Expand Down