Remove code to manage BSD Packet Filter.

This commit is contained in:
Ryan Petrello 2014-08-11 14:33:06 -07:00
parent 5865934e1d
commit 824f726129
9 changed files with 2 additions and 853 deletions

View File

@ -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

View File

@ -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.')

View File

@ -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)

View File

@ -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 = []

View File

@ -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()

View File

@ -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',
]

View File

@ -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 <block_hosts> persist
table <private> const { 10/8, 172.16/12, 192.168/16, 224/8 }
"""

View File

@ -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))

View File

@ -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 <foo>')
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 <foo>')
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 <foo> 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 <foo> 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',
]
)