Remove code to manage BSD Packet Filter.
This commit is contained in:
parent
5865934e1d
commit
824f726129
@ -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
|
||||
|
@ -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.')
|
||||
|
@ -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)
|
@ -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 = []
|
||||
|
@ -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()
|
||||
|
2
setup.py
2
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',
|
||||
]
|
||||
|
@ -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 }
|
||||
"""
|
@ -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))
|
@ -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',
|
||||
]
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user