From 824f72612980da87e784413a51ef98ae5571bee3 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 11 Aug 2014 14:33:06 -0700 Subject: [PATCH] Remove code to manage BSD Packet Filter. --- README.md | 5 +- akanda/router/commands/management.py | 26 -- akanda/router/drivers/pf.py | 120 ------ akanda/router/models.py | 133 ------ scripts/debug-config.py | 1 - setup.py | 2 - .../api/v1/payloads/routerapi_firewall.py | 121 ------ test/unit/drivers/test_pf.py | 45 -- test/unit/test_models.py | 402 ------------------ 9 files changed, 2 insertions(+), 853 deletions(-) delete mode 100644 akanda/router/drivers/pf.py delete mode 100644 test/unit/api/v1/payloads/routerapi_firewall.py delete mode 100644 test/unit/drivers/test_pf.py diff --git a/README.md b/README.md index d936787..5d850fa 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,8 @@ *Part of the [Akanda Project](https://github.com/dreamhost/akanda).* -Router appliance based upon [OpenBSD](http://www.openbsd.org) and [Packet -Filter](http://www.openbsd.org/faq/pf/). Includes a REST API to monitor, -configure, and manage the router. +A Linux-based L3 software router. Includes a REST API to monitor, configure, +and manage the router. Akanda routers are recommended to run with 512 MB of RAM and a single vCPU, and are intended to run within an virtualized L2 overlay to provide complete network diff --git a/akanda/router/commands/management.py b/akanda/router/commands/management.py index 152fc60..a81d625 100644 --- a/akanda/router/commands/management.py +++ b/akanda/router/commands/management.py @@ -82,29 +82,3 @@ def configure_gunicorn(): sys.stderr.write('http configured to listen on %s\n' % listen_ip) except: sys.stderr.write('Unable to write gunicorn configuration file.') - - -def configure_default_pf(): - """ - """ - - mgr = ip.IPManager() - args = {'ifname': mgr.generic_to_host('ge0')} - - config = """ - ge0 = "%(ifname)s" - set skip on lo - match in all scrub (no-df) - block log (all) - pass proto icmp6 all - pass inet proto icmp icmp-type { echoreq, unreach } - pass proto tcp from $ge0:network to $ge0 port { 22, 5000} - """ - - config = textwrap.dedent(config % args).lstrip() - - try: - open('/etc/pf.conf', 'w+').write(config) - sys.stderr.write('Default PF rules configured\n') - except: - sys.stderr.write('Unable to write pf configuration file.') diff --git a/akanda/router/drivers/pf.py b/akanda/router/drivers/pf.py deleted file mode 100644 index d9d0994..0000000 --- a/akanda/router/drivers/pf.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 2014 DreamHost, LLC -# -# Author: DreamHost, LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from akanda.router.drivers import base -from akanda.router.utils import execute, replace_file -from akanda.router import models - - -class PFManager(base.Manager): - """ - """ - EXECUTABLE = '/sbin/pfctl' - - def _show(self, flag, prefix=''): - return self.sudo('-%ss%s' % (prefix, flag)) - - def get_rules(self): - # -sr - return self._show('r') - - def get_states(self): - # -ss - return self._show('s') - - def get_anchors(self): - # -sA - return self._show('A') - - def get_sources(self): - # -sS - return self._show('S') - - def get_info(self): - # -si - return self._show('i') - - def get_tables(self): - # -sT - return self._show('T') - - def get_labels(self, reset=False): - prefix = 'vz' if reset else '' - data = self._show('l', prefix) - return [self._parse_label_line(l) - for l in data.strip().split('\n') if l] - - def get_timeouts(self): - # -st - return self._show('t') - - def get_memory(self): - # -sm - return self._show('m') - - def update_conf(self, conf_data): - replace_file('/tmp/pf.conf', conf_data) - execute(['mv', '/tmp/pf.conf', '/etc/pf.conf'], self.root_helper) - try: - self.sudo('-f', '/etc/pf.conf') - except RuntimeError as e: - raise RuntimeError(unicode(e) + '\n' + conf_data) - - def _parse_label_line(self, line): - parts = line.strip().split() - values = [int(i) for i in parts[1:]] - return {'name': parts[0], - 'total_packets': values[2], - 'total_bytes': values[3], - 'packets_in': values[4], - 'bytes_in': values[5], - 'packets_out': values[6], - 'bytes_out': values[7]} - - -class TableManager(base.Manager): - """ - """ - EXECUTABLE = '/sbin/pfctl' - - def __init__(self, name): - self.name = name - - def add(self, cidr): - self._sudo('-t', self.name, '-T', 'add', str(cidr)) - - def delete(self, cidr): - self._sudo('-t', self.name, '-T', 'delete', str(cidr)) - - def show(self): - return self._sudo('-t', self.name, '-T', self.name) - - -def _parse_pf_rules(data, filters=None): - ''' - Parser for pfctl -sr - ''' - retval = [] - return retval - - -def _parse_pf_rule(line): - ''' - Parser for pfctl -sr - ''' - retval = {} - return models.PFManager.from_dict(retval) diff --git a/akanda/router/models.py b/akanda/router/models.py index 3924ddc..a478938 100644 --- a/akanda/router/models.py +++ b/akanda/router/models.py @@ -16,7 +16,6 @@ import abc -import os import re import netaddr @@ -187,41 +186,6 @@ class FilterRule(ModelBase): super(FilterRule, self).__setattr__(name, value) - @property - def pf_rule(self): - retval = [self.action] - if self.direction: - retval.append(self.direction) - if self.interface: - retval.append('on %s' % self.interface) - if self.family: - retval.append(self.family) - if self.protocol: - retval.append('proto %s' % self.protocol) - if self.source or self.source_port: - retval.append('from') - if self.source: - retval.append(self._format_ip_or_table(self.source)) - if self.source_port: - retval.append('port %s' % self.source_port) - if (self.destination_interface - or self.destination - or self.destination_port): - retval.append('to') - if self.destination_interface: - retval.append(self.destination_interface) - if self.destination: - retval.append(self._format_ip_or_table(self.destination)) - if self.destination_port: - retval.append('port %s' % self.destination_port) - if self.redirect or self.redirect_port: - retval.append('rdr-to') - if self.redirect: - retval.append(str(self.redirect)) - if self.redirect_port: - retval.append('port %s' % self.redirect_port) - return ' '.join(retval) - @classmethod def from_dict(cls, d): return FilterRule(**d) @@ -239,18 +203,6 @@ class Anchor(ModelBase): self.name = name self.rules = rules - @property - def pf_rule(self): - pf_rules = '\n\t'.join([r.pf_rule for r in self.rules]) - return "anchor %s {\n%s\n}" % (self.name, pf_rules) - - def external_pf_rule(self, base_dir): - - path = os.path.abspath(os.path.join(base_dir, self.name)) - return 'anchor %s\nload anchor %s from %s' % (self.name, - self.name, - path) - class AddressBookEntry(ModelBase): def __init__(self, name, cidrs=[]): @@ -265,17 +217,6 @@ class AddressBookEntry(ModelBase): def cidrs(self, values): self._cidrs = [netaddr.IPNetwork(a) for a in values] - @property - def pf_rule(self): - return 'table <%s> persist {%s}' % ( - self.name, ', '.join(map(str, self.cidrs)) - ) - - def external_pf_rule(self, base_dir): - path = os.path.abspath(os.path.join(base_dir, self.name)) - return 'table %s\npersist file "%s"' % (self.name, - path) - def external_table_data(self): return '\n'.join(map(str, self.cidrs)) @@ -323,24 +264,6 @@ class FloatingIP(ModelBase): def fixed_ip(self, value): self._fixed_ip = netaddr.IPAddress(value) - @property - def pf_rule(self): - if self.network is not None: - # There is a bug in Neutron that allows floating IPs with e.g., - # a v6 fixed address and a v4 floating address. Until we get - # a bug fix for this upstream, don't make rules for these, because - # they're invalid. - if self.fixed_ip.version == self.floating_ip.version: - return ( - 'pass on %s from %s to any binat-to %s' % - ( - self.network.interface.ifname, - self.fixed_ip, - self.floating_ip - ) - ) - return '' - @classmethod def from_dict(cls, d): return cls( @@ -387,11 +310,6 @@ class Label(ModelBase): def cidrs(self, values): self._cidrs = [netaddr.IPNetwork(a) for a in values] - @property - def pf_rule(self): - return ('match out on egress to {%s} label "%s"' % - (', '.join(map(str, self.cidrs)), self.name)) - class Subnet(ModelBase): def __init__(self, cidr, gateway_ip, dhcp_enabled=True, @@ -643,57 +561,6 @@ class Configuration(ModelBase): def interfaces(self): return [n.interface for n in self.networks if n.interface] - @property - def pf_config(self): - rv = defaults.BASE_RULES[:] - - # add default deny all external networks and remember 1st for nat - ext_if = None - for n in self.networks: - if n.network_type == Network.TYPE_EXTERNAL: - ext_if = n.interface.ifname - ext_v4_addr = n.interface.first_v4 - break - - # add in nat and management rules - for network in self.networks: - if network.network_type == Network.TYPE_EXTERNAL: - rv.extend(_format_ext_rule(network.interface)) - elif network.network_type == Network.TYPE_INTERNAL: - if ext_if: - rv.extend( - _format_int_to_ext_rule( - ext_if, - ext_v4_addr, - network.interface - ) - ) - elif network.network_type == Network.TYPE_MANAGEMENT: - rv.extend(_format_mgt_rule(network.interface.ifname)) - else: - # isolated and management nets block all between interfaces - rv.extend(_format_isolated_rule(network.interface.ifname)) - - # add address book tables - rv.extend(ab.pf_rule for ab in self.address_book.values()) - - # add anchors and rules - rv.extend(a.pf_rule for a in self.anchors) - - # add counters - rv.extend(l.pf_rule for l in self.labels) - - # add floating ip - for network in self.networks: - rv.extend( - _format_floating_ip( - network.interface.ifname, - network.floating_ips - ) - ) - - return '\n'.join(rv) + '\n' - def _format_ext_rule(interface): retval = [] diff --git a/scripts/debug-config.py b/scripts/debug-config.py index e01eb7f..b1cd67c 100644 --- a/scripts/debug-config.py +++ b/scripts/debug-config.py @@ -28,6 +28,5 @@ if __name__ == '__main__': print '-' * 80 print conf.validate() print '-' * 80 - print conf.pf_config except Exception as e: pdb.set_trace() diff --git a/setup.py b/setup.py index cc375c2..2861422 100644 --- a/setup.py +++ b/setup.py @@ -44,8 +44,6 @@ setup( 'akanda.router.commands.management:configure_ssh', 'akanda-configure-gunicorn = ' 'akanda.router.commands.management:configure_gunicorn', - 'akanda-configure-default-pf = ' - 'akanda.router.commands.management:configure_default_pf', 'akanda-api-dev-server = akanda.router.api.server:main', 'akanda-metadata-proxy = akanda.router.metadata_proxy:main', ] diff --git a/test/unit/api/v1/payloads/routerapi_firewall.py b/test/unit/api/v1/payloads/routerapi_firewall.py deleted file mode 100644 index 23c727c..0000000 --- a/test/unit/api/v1/payloads/routerapi_firewall.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright 2014 DreamHost, LLC -# -# Author: DreamHost, LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -""" -The text output generated by the Firewall API. -""" -sample_firewall_rules = ('pass all flags S/SA block drop in on ! lo0 proto ' - 'tcp from any to any port 6000:6010') - - -sample_pfctl_sr = """ -pass all flags S/SA -block drop in on ! lo0 proto tcp from any to any port 6000:6010 -""" - - -sample_pfctl_ss = """ -all tcp 192.168.229.129:22 <- 192.168.229.1:52130 ESTABLISHED:ESTABLISHED -all udp 192.168.229.255:17500 <- 192.168.229.1:17500 NO_TRAFFIC:SINGLE -all udp 172.16.5.255:17500 <- 172.16.5.1:17500 NO_TRAFFIC:SINGLE -""" - - -sample_pfctl_si = """ -Status: Enabled for 0 days 01:57:48 Debug: err - -State Table Total Rate - current entries 4 - searches 5638 0.8/s - inserts 86 0.0/s - removals 82 0.0/s -Counters - match 86 0.0/s - bad-offset 0 0.0/s - fragment 0 0.0/s - short 0 0.0/s - normalize 0 0.0/s - memory 0 0.0/s - bad-timestamp 0 0.0/s - congestion 0 0.0/s - ip-option 0 0.0/s - proto-cksum 0 0.0/s - state-mismatch 0 0.0/s - state-insert 0 0.0/s - state-limit 0 0.0/s - src-limit 0 0.0/s - synproxy 0 0.0/s -""" - - -sample_pfctl_st = """ -tcp.first 120s -tcp.opening 30s -tcp.established 86400s -tcp.closing 900s -tcp.finwait 45s -tcp.closed 90s -tcp.tsdiff 30s -udp.first 60s -udp.single 30s -udp.multiple 60s -icmp.first 20s -icmp.error 10s -other.first 60s -other.single 30s -other.multiple 60s -frag 30s -interval 10s -adaptive.start 6000 states -adaptive.end 12000 states -src.track 0s -""" - - -sample_pfctl_sm = """ -states hard limit 10000 -src-nodes hard limit 10000 -frags hard limit 5000 -tables hard limit 1000 -table-entries hard limit 200000 -""" - - -sample_pfctl_sl = """ -No ALTQ support in kernel -ALTQ related functions disabled -""" - - -sample_pfctl_sA = """ -dh -dh-ssh -dh-www -goodguys -""" - - -sample_pfctl_sS = """ -No ALTQ support in kernel -ALTQ related functions disabled -""" - - -sample_pfctl_sT = """ -table persist -table const { 10/8, 172.16/12, 192.168/16, 224/8 } -""" diff --git a/test/unit/drivers/test_pf.py b/test/unit/drivers/test_pf.py deleted file mode 100644 index 4e4b2cb..0000000 --- a/test/unit/drivers/test_pf.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2014 DreamHost, LLC -# -# Author: DreamHost, LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -import mock -import unittest2 - -from akanda.router.drivers import pf - -config = mock.Mock() -network = mock.Mock() -alloc = mock.Mock() -network.address_allocations = [alloc] -config.networks = [network] - - -class PFTest(unittest2.TestCase): - - def setUp(self): - self.mgr = pf.PFManager() - - @mock.patch.object(pf, 'execute') - @mock.patch.object(pf, 'replace_file') - def test_update_error_includes_file_contents(self, ex, rf): - # Verify that the error message from pf includes the contents - # of the config file. - with mock.patch.object(self.mgr, 'sudo') as sudo: - sudo.side_effect = RuntimeError('base message') - try: - self.mgr.update_conf('conf data') - except RuntimeError as e: - self.assertIn('conf data', unicode(e)) diff --git a/test/unit/test_models.py b/test/unit/test_models.py index beba0f8..226b2f5 100644 --- a/test/unit/test_models.py +++ b/test/unit/test_models.py @@ -196,106 +196,12 @@ class FilterRuleModelTestCase(TestCase): with self.assertRaises(ValueError): models.FilterRule(action='pass', protocol='made_up_proto') - def _pf_rule_test_helper(self, d, expected): - fr = models.FilterRule(**d) - self.assertEqual(fr.pf_rule, expected) - - def test_pf_rule_basic(self): - self._pf_rule_test_helper(dict(action='pass'), 'pass') - self._pf_rule_test_helper(dict(action='block'), 'block') - - def test_pf_rule_interface(self): - self._pf_rule_test_helper(dict(action='pass', interface='ge0'), - 'pass on ge0') - - def test_pf_rule_family(self): - self._pf_rule_test_helper(dict(action='block', family='inet6'), - 'block inet6') - - def test_pf_rule_protocol(self): - self._pf_rule_test_helper(dict(action='block', protocol='tcp'), - 'block proto tcp') - - def test_pf_rule_source_table(self): - self._pf_rule_test_helper(dict(action='block', source='foo'), - 'block from ') - - def test_pf_rule_source_address(self): - args = dict(action='block', source='192.168.1.0/24') - self._pf_rule_test_helper(args, 'block from 192.168.1.0/24') - - def test_pf_rule_source_port(self): - args = dict(action='block', source_port=22) - self._pf_rule_test_helper(args, 'block from port 22') - - def test_pf_rule_source_address_and_port(self): - args = dict(action='pass', source='192.168.1.1/32', source_port=22) - self._pf_rule_test_helper(args, 'pass from 192.168.1.1/32 port 22') - - def test_pf_rule_destination_interface(self): - args = dict(action='block', destination_interface="ge1") - self._pf_rule_test_helper(args, 'block to ge1') - - def test_pf_rule_destination_table(self): - args = dict(action='block', destination="foo") - self._pf_rule_test_helper(args, 'block to ') - - def test_pf_rule_destination_address(self): - args = dict(action='block', destination="192.168.1.0/24") - self._pf_rule_test_helper(args, 'block to 192.168.1.0/24') - - def test_pf_rule_destination_port(self): - args = dict(action='block', destination_port="23") - self._pf_rule_test_helper(args, 'block to port 23') - - def test_pf_rule_destination_address_and_port(self): - args = dict(action='block', destination='192.168.1.2/32', - destination_port="23") - self._pf_rule_test_helper(args, 'block to 192.168.1.2/32 port 23') - - def test_pf_rule_redirect(self): - args = dict(action='pass', - destination_port="23", - redirect="192.168.1.1") - self._pf_rule_test_helper(args, 'pass to port 23 rdr-to 192.168.1.1') - - def test_pf_rule_redirect_port(self): - args = dict(action='pass', - destination_port="23", - redirect_port="24") - self._pf_rule_test_helper(args, 'pass to port 23 rdr-to port 24') - - def test_pf_rule_from_dict(self): - args = dict(action='pass', - destination_port="23", - redirect="192.168.1.2") - - pr = models.FilterRule.from_dict(args) - self.assertEqual(pr.action, 'pass') - self.assertEqual(pr.destination_port, 23) - self.assertEqual(pr.redirect, netaddr.IPAddress('192.168.1.2')) - - class AnchorTestCase(TestCase): def test_anchor(self): a = models.Anchor('foo', []) self.assertEqual(a.name, 'foo') self.assertEqual(a.rules, []) - def test_anchor_external_pf_rule(self): - a = models.Anchor('foo', []) - self.assertEqual(a.external_pf_rule('/etc/pf'), - 'anchor foo\nload anchor foo from /etc/pf/foo') - - def test_anchor_pf_rule_empty(self): - a = models.Anchor('foo', []) - self.assertEqual(a.pf_rule, 'anchor foo {\n\n}') - - def test_anchor_pf_rule(self): - fr = models.FilterRule(action='block', interface="ge0") - a = models.Anchor('foo', [fr]) - self.assertEqual(a.pf_rule, 'anchor foo {\nblock on ge0\n}') - class AddressBookTestCase(TestCase): def test_entry(self): @@ -303,15 +209,6 @@ class AddressBookTestCase(TestCase): self.assertEqual(ab.name, 'foo') self.assertEqual(ab.cidrs, [netaddr.IPNetwork('192.168.1.0/24')]) - def test_pf_rule(self): - ab = models.AddressBookEntry('foo', ['192.168.1.0/24']) - self.assertEqual(ab.pf_rule, 'table persist {192.168.1.0/24}') - - def test_external_pf_rule(self): - ab = models.AddressBookEntry('foo', ['192.168.1.0/24']) - self.assertEqual(ab.external_pf_rule('/etc'), - 'table foo\npersist file "/etc/foo"') - def test_external_table_data(self): ab = models.AddressBookEntry('foo', ['192.168.1.0/24', '172.16.16.0/16']) @@ -325,11 +222,6 @@ class LabelTestCase(TestCase): self.assertEqual(l.name, 'foo') self.assertEqual(l.cidrs, [netaddr.IPNetwork('192.168.1.0/24')]) - def test_pf_rule(self): - l = models.Label('foo', ['192.168.1.0/24']) - self.assertEqual(l.pf_rule, - 'match out on egress to {192.168.1.0/24} label "foo"') - class AllocationTestCase(TestCase): def test_allocation(self): @@ -357,13 +249,8 @@ class FloatingIPTestCase(TestCase): self.assertEqual(fip.floating_ip, netaddr.IPAddress('9.9.9.9')) self.assertEqual(fip.fixed_ip, netaddr.IPAddress('10.0.0.1')) - self.assertEqual(fip.pf_rule, '') fip.network = network - self.assertEqual( - fip.pf_rule, - 'pass on ge1 from 10.0.0.1 to any binat-to 9.9.9.9' - ) def test_floating_ip_with_different_ip_versions(self): fip = models.FloatingIP( @@ -375,10 +262,6 @@ class FloatingIPTestCase(TestCase): network.interface.ifname = 'ge1' fip.network = network - self.assertEqual( - fip.pf_rule, - '' - ) class StaticRouteTestCase(TestCase): @@ -641,288 +524,3 @@ class ConfigurationTestCase(TestCase): anchors=[]) self.assertEqual(c.to_dict(), expected) - - def _pf_config_test_helper(self, conf_dict, test_expectations): - base = ['block'] - - expected = '\n'.join(base + test_expectations + ['']) - - attrs = dict( - BASE_RULES=base, - MANAGEMENT_PORTS=[22]) - - with mock.patch.multiple('akanda.router.defaults', **attrs) as defs: - c = models.Configuration(conf_dict) - self.assertEqual(c.pf_config, expected) - - def test_pf_config_default(self): - self._pf_config_test_helper({'networks': []}, []) - - def test_pf_config_nat(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0', addresses=['9.9.9.1/24']), - network_type='external') - int_net = dict(network_id='int', - interface=dict(ifname='ge1', addresses=['10.0.0.0/8']), - network_type='internal') - - self._pf_config_test_helper( - {'networks': [ext_net, int_net]}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - ('pass in quick on ge1 proto tcp to 169.254.169.254 port ' - 'http rdr-to 127.0.0.1 port 9601'), - 'pass out on ge0 from ge1:network to any nat-to 9.9.9.1', - 'pass in quick on ge1 proto udp from port 68 to port 67', - 'pass out quick on ge1 proto udp from port 67 to port 68', - 'pass in on ge1 proto tcp to any', - 'pass in on ge1 proto udp to any', - 'pass out quick on ge0 proto udp from ge1 to any port 53', - ] - ) - - def test_pf_config_nat_with_ip6(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0', addresses=['9.9.9.1/24']), - network_type='external') - int_net = dict(network_id='int', - interface=dict(ifname='ge1', addresses=['10.0.0.0/8']), - network_type='internal') - v6_net = dict(network_id='v6_int', - interface=dict(ifname='ge2', addresses=['fe80::1/64']), - network_type='internal') - - self._pf_config_test_helper( - {'networks': [ext_net, int_net, v6_net]}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - ('pass in quick on ge1 proto tcp to 169.254.169.254 port ' - 'http rdr-to 127.0.0.1 port 9601'), - 'pass out on ge0 from ge1:network to any nat-to 9.9.9.1', - 'pass in quick on ge1 proto udp from port 68 to port 67', - 'pass out quick on ge1 proto udp from port 67 to port 68', - 'pass in on ge1 proto tcp to any', - 'pass in on ge1 proto udp to any', - 'pass out quick on ge0 proto udp from ge1 to any port 53', - 'pass in quick on ge2 proto udp from port 546 to port 547', - 'pass out quick on ge2 proto udp from port 547 to port 546', - 'pass out on ge0 inet6 from ge2:network', - 'pass inet6 to ge2:network', - 'pass in on ge2 proto tcp to any', - 'pass in on ge2 proto udp to any', - 'pass out quick on ge0 proto udp from ge2 to any port 53', - ] - ) - - def test_pf_config_isolated(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0'), - network_type='external') - int_net = dict(network_id='int', - interface=dict(ifname='ge1'), - network_type='isolated') - - self._pf_config_test_helper( - {'networks': [ext_net, int_net]}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - ('pass in quick on ge1 proto tcp to 169.254.169.254 port ' - 'http rdr-to 127.0.0.1 port 9601'), - 'block from ge1:network to any' - ] - ) - - def test_pf_config_management(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0'), - network_type='external') - int_net = dict(network_id='int', - interface=dict(ifname='ge1'), - network_type='management') - - self._pf_config_test_helper( - {'networks': [ext_net, int_net]}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - 'pass quick proto tcp from ge1:network to ge1 port { 22 }', - 'pass quick proto tcp from ge1 to ge1:network port 9697', - 'block in quick on !ge1 to ge1:network', - ] - ) - - def test_pf_config_with_addressbook(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0'), - network_type='external') - ab = dict(foo=['192.168.1.1/24']) - - self._pf_config_test_helper( - {'networks': [ext_net], 'address_book': ab}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - 'table persist {192.168.1.1/24}' - ] - ) - - def test_pf_config_with_anchor(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0'), - network_type='external') - anchor = dict(name='foo', - rules=[dict(action='pass', - protocol='tcp', - destination_port=22)]) - self._pf_config_test_helper( - {'networks': [ext_net], 'anchors': [anchor]}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - 'anchor foo {\npass proto tcp to port 22\n}' - ] - ) - - def test_pf_config_with_label(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0'), - network_type='external') - label = dict(foo=['192.168.1.0/24']) - - self._pf_config_test_helper( - {'networks': [ext_net], 'labels': label}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - 'match out on egress to {192.168.1.0/24} label "foo"' - ] - ) - - def test_pf_config_with_floating(self): - ext_net = dict( - network_id='ext', - interface=dict(ifname='ge0', addresses=['9.9.9.1/24']), - network_type='external', - subnets=[ - { - 'cidr': '9.9.9.0/24', - 'gateway_ip': '9.9.9.1', - 'dhcp_enabled': True, - 'dns_nameservers': [], - } - ] - ) - int_net = dict( - network_id='int', - interface=dict(ifname='ge1', addresses=['10.0.0.0/24']), - network_type='internal', - subnets=[ - { - 'cidr': '10.0.0.0/24', - 'gateway_ip': '10.0.0.1', - 'dhcp_enabled': True, - 'dns_nameservers': [], - } - ] - ) - - fip = { - 'floating_ip': '9.9.9.9', - 'fixed_ip': '10.0.0.1' - } - - self._pf_config_test_helper( - {'networks': [ext_net, int_net], 'floating_ips': [fip]}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - ('pass in quick on ge1 proto tcp to 169.254.169.254 port ' - 'http rdr-to 127.0.0.1 port 9601'), - 'pass out on ge0 from ge1:network to any nat-to 9.9.9.1', - 'pass in quick on ge1 proto udp from port 68 to port 67', - 'pass out quick on ge1 proto udp from port 67 to port 68', - 'pass in on ge1 proto tcp to any', - 'pass in on ge1 proto udp to any', - 'pass out quick on ge0 proto udp from ge1 to any port 53', - 'pass on ge0 from 10.0.0.1 to any binat-to 9.9.9.9', - 'pass out on ge1 to 10.0.0.1' - ] - ) - - def test_pf_config_with_floating_different_ip_versions(self): - ext_net = dict( - network_id='ext', - interface=dict(ifname='ge0', addresses=['9.9.9.1/24']), - network_type='external', - subnets=[ - { - 'cidr': '9.9.9.0/24', - 'gateway_ip': '9.9.9.1', - 'dhcp_enabled': True, - 'dns_nameservers': [], - } - ] - ) - int_net = dict( - network_id='int', - interface=dict(ifname='ge1', addresses=['10.0.0.0/24']), - network_type='internal', - subnets=[ - { - 'cidr': '10.0.0.0/24', - 'gateway_ip': '10.0.0.1', - 'dhcp_enabled': True, - 'dns_nameservers': [], - } - ] - ) - - fip = { - 'floating_ip': '9.9.9.9', - 'fixed_ip': 'fe80::1' - } - - self._pf_config_test_helper( - {'networks': [ext_net, int_net], 'floating_ips': [fip]}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - ('pass in quick on ge1 proto tcp to 169.254.169.254 port ' - 'http rdr-to 127.0.0.1 port 9601'), - 'pass out on ge0 from ge1:network to any nat-to 9.9.9.1', - 'pass in quick on ge1 proto udp from port 68 to port 67', - 'pass out quick on ge1 proto udp from port 67 to port 68', - 'pass in on ge1 proto tcp to any', - 'pass in on ge1 proto udp to any', - 'pass out quick on ge0 proto udp from ge1 to any port 53' - ] - ) - - def test_pf_config_external_ipv4(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0'), - network_type='external') - self._pf_config_test_helper( - {'networks': [ext_net]}, - [ - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - ] - ) - - def test_pf_config_external_ipv6(self): - ext_net = dict(network_id='ext', - interface=dict(ifname='ge0', addresses=['fe80::1/64']), - network_type='external') - self._pf_config_test_helper( - {'networks': [ext_net]}, - [ - ('pass on ge0 inet6 proto tcp from ge0:network to ge0:network ' - 'port 179'), - 'pass out quick on ge0 proto udp from ge0 to any port 53', - 'pass out quick on ge0 proto tcp from ge0 to any', - ] - )