Implement a Linux firewall with iptables and ip6tables.
This commit is contained in:
parent
65c952f5d8
commit
b7636d170b
@ -22,7 +22,7 @@ Blueprint for version 1 of the firewall API.
|
||||
from flask import request
|
||||
|
||||
from akanda.router import utils
|
||||
from akanda.router.drivers import pf
|
||||
from akanda.router.drivers import iptables
|
||||
|
||||
|
||||
blueprint = utils.blueprint_factory(__name__)
|
||||
@ -30,86 +30,12 @@ blueprint = utils.blueprint_factory(__name__)
|
||||
|
||||
@blueprint.before_request
|
||||
def get_manager():
|
||||
request.pf_mgr = pf.PFManager()
|
||||
request.iptables_mgr = iptables.IPTablesManager()
|
||||
|
||||
|
||||
@blueprint.route('/rules')
|
||||
def get_rules():
|
||||
'''
|
||||
Show loaded firewall rules by pfctl
|
||||
Show loaded firewall rules by iptables
|
||||
'''
|
||||
return request.pf_mgr.get_rules()
|
||||
|
||||
|
||||
@blueprint.route('/states')
|
||||
def get_states():
|
||||
'''
|
||||
Show firewall state table
|
||||
'''
|
||||
return request.pf_mgr.get_states()
|
||||
|
||||
|
||||
@blueprint.route('/anchors')
|
||||
def get_anchors():
|
||||
'''
|
||||
Show loaded firewall anchors by pfctl
|
||||
'''
|
||||
return request.pf_mgr.get_anchors()
|
||||
|
||||
|
||||
@blueprint.route('/sources')
|
||||
def get_sources():
|
||||
'''
|
||||
Show loaded firewall sources by pfctl
|
||||
'''
|
||||
return request.pf_mgr.get_sources()
|
||||
|
||||
|
||||
@blueprint.route('/info')
|
||||
def get_info():
|
||||
'''
|
||||
Show verbose running firewall information
|
||||
'''
|
||||
return request.pf_mgr.get_info()
|
||||
|
||||
|
||||
@blueprint.route('/tables')
|
||||
def get_tables():
|
||||
'''
|
||||
Show loaded firewall tables by pfctl
|
||||
'''
|
||||
return request.pf_mgr.get_tables()
|
||||
|
||||
|
||||
@blueprint.route('/labels')
|
||||
@utils.json_response
|
||||
def get_labels():
|
||||
'''
|
||||
Show loaded firewall labels by pfctl
|
||||
'''
|
||||
return dict(labels=request.pf_mgr.get_labels())
|
||||
|
||||
|
||||
@blueprint.route('/labels', methods=['POST'])
|
||||
@utils.json_response
|
||||
def reset_labels():
|
||||
'''
|
||||
Show loaded firewall labels by pfctl and reset the counters
|
||||
'''
|
||||
return dict(labels=request.pf_mgr.get_labels(True))
|
||||
|
||||
|
||||
@blueprint.route('/timeouts')
|
||||
def get_timeouts():
|
||||
'''
|
||||
Show firewall connection timeouts
|
||||
'''
|
||||
return request.pf_mgr.get_timeouts()
|
||||
|
||||
|
||||
@blueprint.route('/memory')
|
||||
def get_memory():
|
||||
'''
|
||||
Show firewall memory
|
||||
'''
|
||||
return request.pf_mgr.get_memory()
|
||||
return request.iptables_mgr.get_rules()
|
||||
|
@ -19,10 +19,14 @@ SSH = 22
|
||||
SMTP = 25
|
||||
DNS = 53
|
||||
HTTP = 80
|
||||
BGP = 179
|
||||
HTTPS = 443
|
||||
HTTP_ALT = 8080
|
||||
API_SERVICE = 5000
|
||||
|
||||
DHCP = 67
|
||||
DHCPV6 = 546
|
||||
|
||||
NFS_DEVELOPMENT = [111, 1110, 2049, 4045]
|
||||
|
||||
MANAGEMENT_PORTS = [SSH, API_SERVICE] # + NFS_DEVELOPMENT
|
||||
@ -46,4 +50,5 @@ RUG_META_PORT = 9697
|
||||
|
||||
|
||||
def internal_metadata_port(ifname):
|
||||
return BASE_METADATA_PORT + int(ifname[2:])
|
||||
import re
|
||||
return BASE_METADATA_PORT + int(re.sub('[a-zA-Z]', '', ifname))
|
||||
|
259
akanda/router/drivers/iptables.py
Executable file
259
akanda/router/drivers/iptables.py
Executable file
@ -0,0 +1,259 @@
|
||||
# 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 re
|
||||
|
||||
|
||||
from akanda.router.drivers import base
|
||||
from akanda.router.models import Network
|
||||
from akanda.router import defaults, utils
|
||||
|
||||
|
||||
class Rule(object):
|
||||
|
||||
def __init__(self, rule, ip_version=None):
|
||||
self.rule = rule
|
||||
self.ip_version = ip_version
|
||||
|
||||
def __str__(self):
|
||||
return self.rule
|
||||
|
||||
@property
|
||||
def for_v4(self):
|
||||
return self.ip_version in (None, 4)
|
||||
|
||||
@property
|
||||
def for_v6(self):
|
||||
return self.ip_version in (None, 6)
|
||||
|
||||
|
||||
class IPTablesManager(base.Manager):
|
||||
"""
|
||||
"""
|
||||
|
||||
def save_config(self, config, interface_map):
|
||||
rules = []
|
||||
|
||||
self._build_filter_table(config, interface_map, rules)
|
||||
self._build_nat_table(config, interface_map, rules)
|
||||
|
||||
v4_data = '\n'.join(map(str, filter(lambda x: x.for_v4, rules)))
|
||||
v6_data = '\n'.join(map(str, filter(lambda x: x.for_v6, rules)))
|
||||
|
||||
real_name = interface_map.get('ge0')[:-1]
|
||||
|
||||
# Map virtual interface names
|
||||
ifname_re = '\-(?P<flag>i|o)(?P<ws>[\s!])(?P<not>!?)(?P<if>ge)(?P<no>\d+)' # noqa
|
||||
ifname_sub = r'-\g<flag>\g<ws>\g<not>%s\g<no>' % real_name
|
||||
v4_data = re.sub(ifname_re, ifname_sub, v4_data) + '\n'
|
||||
v6_data = re.sub(ifname_re, ifname_sub, v6_data) + '\n'
|
||||
|
||||
utils.replace_file('/tmp/iptables.rules', v4_data)
|
||||
utils.replace_file('/tmp/ip6tables.rules', v6_data)
|
||||
|
||||
utils.execute(
|
||||
['mv', '/tmp/iptables.rules', '/etc/iptables/rules.v4'],
|
||||
self.root_helper
|
||||
)
|
||||
utils.execute(
|
||||
['mv', '/tmp/ip6tables.rules', '/etc/iptables/rules.v6'],
|
||||
self.root_helper
|
||||
)
|
||||
|
||||
def restart(self):
|
||||
utils.execute(
|
||||
['/etc/init.d/iptables-persistent', 'restart'],
|
||||
self.root_helper
|
||||
)
|
||||
|
||||
def get_rules(self):
|
||||
v4 = utils.execute(['iptables', '-L', '-n'])
|
||||
v6 = utils.execute(['ip6tables', '-L', '-n'])
|
||||
return v4 + v6
|
||||
|
||||
def get_external_network(self, config):
|
||||
for n in config.networks:
|
||||
if n.network_type == Network.TYPE_EXTERNAL:
|
||||
return n
|
||||
|
||||
def _build_filter_table(self, config, interface_map, rules):
|
||||
ext_if = self.get_external_network(config).interface
|
||||
|
||||
# Drop INPUT/OUTPUT by default
|
||||
rules.extend([
|
||||
Rule('*filter'),
|
||||
Rule(':INPUT DROP [0:0]'),
|
||||
Rule(':FORWARD ACCEPT [0:0]'),
|
||||
Rule(':OUTPUT ACCEPT [0:0]')
|
||||
])
|
||||
|
||||
# Allow ICMP and ICMP6
|
||||
rules.append(Rule(
|
||||
'-A INPUT -p icmp --icmp-type echo-request -j ACCEPT',
|
||||
ip_version=4
|
||||
))
|
||||
rules.append(Rule(
|
||||
'-A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT',
|
||||
ip_version=6
|
||||
))
|
||||
|
||||
for network in [
|
||||
n for n in config.networks
|
||||
if n.network_type == Network.TYPE_MANAGEMENT
|
||||
]:
|
||||
|
||||
# Open SSH, the HTTP API (5000) and the Nova metadata proxy (9697)
|
||||
for port in (
|
||||
defaults.SSH, defaults.API_SERVICE, defaults.RUG_META_PORT
|
||||
):
|
||||
rules.append(Rule(
|
||||
'-A INPUT -i %s -p tcp -m tcp --dport %s -j ACCEPT' % (
|
||||
network.interface.ifname,
|
||||
port
|
||||
), ip_version=6
|
||||
))
|
||||
|
||||
# Disallow any other management network traffic
|
||||
rules.append(Rule('-A INPUT -i !%s -d %s -j DROP' % (
|
||||
network.interface.ifname,
|
||||
network.interface.first_v6
|
||||
), ip_version=6))
|
||||
|
||||
for network in [
|
||||
n for n in config.networks
|
||||
if n.network_type == Network.TYPE_INTERNAL
|
||||
]:
|
||||
|
||||
if network.interface.first_v4:
|
||||
|
||||
# Basic v4 state-matching rules. Allows packets related to a
|
||||
# pre-established session to pass.
|
||||
rules.append(Rule(
|
||||
'-A FORWARD -d %s -o %s -m state '
|
||||
'--state RELATED,ESTABLISHED -j ACCEPT' % (
|
||||
network.interface.first_v4,
|
||||
network.interface.ifname
|
||||
), ip_version=4
|
||||
))
|
||||
|
||||
# Allow v4 DHCP
|
||||
rules.append(Rule(
|
||||
'-A INPUT -i %s -p udp -m udp --dport %s -j ACCEPT' % (
|
||||
network.interface.ifname,
|
||||
defaults.DHCP
|
||||
), ip_version=4
|
||||
))
|
||||
rules.append(Rule(
|
||||
'-A INPUT -i %s -p tcp -m tcp --dport %s -j ACCEPT' % (
|
||||
network.interface.ifname,
|
||||
defaults.DHCP
|
||||
), ip_version=4
|
||||
))
|
||||
|
||||
if network.interface.first_v6:
|
||||
|
||||
# Basic v6 state-matching rules. Allows packets related to a
|
||||
# pre-established session to pass.
|
||||
rules.append(Rule(
|
||||
'-A FORWARD -d %s -o %s -m state '
|
||||
'--state RELATED,ESTABLISHED -j ACCEPT' % (
|
||||
network.interface.first_v6,
|
||||
network.interface.ifname
|
||||
), ip_version=6
|
||||
))
|
||||
|
||||
# Allow v6 DHCP
|
||||
rules.append(Rule(
|
||||
'-A INPUT -i %s -p udp -m udp --dport %s -j ACCEPT' % (
|
||||
network.interface.ifname,
|
||||
defaults.DHCPV6,
|
||||
), ip_version=6
|
||||
))
|
||||
rules.append(Rule(
|
||||
'-A INPUT -i %s -p tcp -m tcp --dport %s -j ACCEPT' % (
|
||||
network.interface.ifname,
|
||||
defaults.DHCPV6,
|
||||
), ip_version=6
|
||||
))
|
||||
|
||||
# Allow pre-established metadata sessions to pass
|
||||
rules.append(Rule(
|
||||
'-A FORWARD -s %s -o %s -m state '
|
||||
'--state RELATED,ESTABLISHED -j ACCEPT' % (
|
||||
defaults.METADATA_DEST_ADDRESS,
|
||||
network.interface.ifname
|
||||
), ip_version=4
|
||||
))
|
||||
|
||||
rules.append(Rule(
|
||||
'-A INPUT -i %s -j ACCEPT' % network.interface.ifname
|
||||
))
|
||||
rules.append(Rule(
|
||||
'-A INPUT -i %s -m state '
|
||||
'--state RELATED,ESTABLISHED -j ACCEPT' % ext_if.ifname
|
||||
))
|
||||
|
||||
rules.append(Rule('COMMIT'))
|
||||
|
||||
def _build_nat_table(self, config, interface_map, rules):
|
||||
ext_if = self.get_external_network(config).interface
|
||||
|
||||
rules.extend([
|
||||
Rule('*nat', ip_version=4),
|
||||
Rule(':PREROUTING ACCEPT [0:0]', ip_version=4),
|
||||
Rule(':INPUT ACCEPT [0:0]', ip_version=4),
|
||||
Rule(':OUTPUT ACCEPT [0:0]', ip_version=4),
|
||||
Rule(':POSTROUTING ACCEPT [0:0]', ip_version=4),
|
||||
])
|
||||
|
||||
for network in config.networks:
|
||||
|
||||
if network.network_type == Network.TYPE_INTERNAL:
|
||||
|
||||
# Forward metadata requests on the management interface
|
||||
rules.append(Rule(
|
||||
'-A PREROUTING -s %s -d %s -p tcp -m tcp '
|
||||
'--dport %s -j DNAT --to-destination 127.0.0.1:%s' % (
|
||||
network.interface.first_v4,
|
||||
defaults.METADATA_DEST_ADDRESS,
|
||||
defaults.HTTP,
|
||||
defaults.internal_metadata_port(
|
||||
network.interface.ifname
|
||||
)
|
||||
), ip_version=4
|
||||
))
|
||||
|
||||
# NAT for IPv4
|
||||
ext_v4 = sorted(
|
||||
a.ip for a in ext_if._addresses if a.version == 4
|
||||
)[0]
|
||||
rules.append(Rule(
|
||||
'-A POSTROUTING -s %s -o %s -j SNAT --to %s' % (
|
||||
network.interface.first_v4,
|
||||
network.interface.ifname,
|
||||
str(ext_v4)
|
||||
), ip_version=4
|
||||
))
|
||||
|
||||
# Route floating IP addresses
|
||||
for fip in self.get_external_network(config).floating_ips:
|
||||
rules.append(Rule('-A POSTROUTING -o %s -s %s -j SNAT --to %s' % (
|
||||
ext_if.ifname,
|
||||
fip.fixed_ip,
|
||||
fip.floating_ip
|
||||
), ip_version=4))
|
||||
|
||||
rules.append(Rule('COMMIT', ip_version=4))
|
@ -19,7 +19,7 @@ import os
|
||||
import re
|
||||
|
||||
from akanda.router import models
|
||||
from akanda.router.drivers import (bird, dnsmasq, ip, metadata, pf, arp)
|
||||
from akanda.router.drivers import (bird, dnsmasq, ip, metadata, iptables, arp)
|
||||
|
||||
|
||||
class Manager(object):
|
||||
@ -49,7 +49,7 @@ class Manager(object):
|
||||
self.update_dhcp()
|
||||
self.update_metadata()
|
||||
self.update_bgp_and_radv()
|
||||
self.update_pf()
|
||||
self.update_firewall()
|
||||
self.update_routes(cache)
|
||||
self.update_arp()
|
||||
|
||||
@ -80,11 +80,10 @@ class Manager(object):
|
||||
mgr.save_config(self.config, self.ip_mgr.generic_mapping)
|
||||
mgr.restart()
|
||||
|
||||
def update_pf(self):
|
||||
rule_data = self.config.pf_config
|
||||
rule_data = self._map_virtual_to_real_interfaces(rule_data)
|
||||
mgr = pf.PFManager()
|
||||
mgr.update_conf(rule_data)
|
||||
def update_firewall(self):
|
||||
mgr = iptables.IPTablesManager()
|
||||
mgr.save_config(self.config, self.ip_mgr.generic_mapping)
|
||||
mgr.restart()
|
||||
|
||||
def update_routes(self, cache):
|
||||
mgr = ip.IPManager()
|
||||
|
@ -24,15 +24,17 @@ import mock
|
||||
from unittest2 import TestCase
|
||||
|
||||
from akanda.router.api import v1
|
||||
from akanda.router.drivers.pf import PFManager
|
||||
|
||||
|
||||
class FirewallAPITestCase(TestCase):
|
||||
"""
|
||||
"""
|
||||
def setUp(self):
|
||||
pf_mgr_patch = mock.patch.object(v1.firewall.pf, 'PFManager')
|
||||
self.pf_mgr = pf_mgr_patch.start().return_value
|
||||
ip_mgr_patch = mock.patch.object(
|
||||
v1.firewall.iptables,
|
||||
'IPTablesManager'
|
||||
)
|
||||
self.iptables_mgr = ip_mgr_patch.start().return_value
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
self.app = flask.Flask('firewall_test')
|
||||
self.app.register_blueprint(v1.firewall.blueprint)
|
||||
@ -40,7 +42,7 @@ class FirewallAPITestCase(TestCase):
|
||||
|
||||
def _test_passthrough_helper(self, resource_name, method_name,
|
||||
response_code=200):
|
||||
mock_method = getattr(self.pf_mgr, method_name)
|
||||
mock_method = getattr(self.iptables_mgr, method_name)
|
||||
mock_method.return_value = 'the_value'
|
||||
result = self.test_app.get('/v1/firewall/%s' % resource_name)
|
||||
self.assertEqual(response_code, result.status_code)
|
||||
@ -49,37 +51,3 @@ class FirewallAPITestCase(TestCase):
|
||||
|
||||
def test_get_rules(self):
|
||||
self._test_passthrough_helper('rules', 'get_rules')
|
||||
|
||||
def test_get_states(self):
|
||||
self._test_passthrough_helper('states', 'get_states')
|
||||
|
||||
def test_get_anchors(self):
|
||||
self._test_passthrough_helper('anchors', 'get_anchors')
|
||||
|
||||
def test_get_sources(self):
|
||||
self._test_passthrough_helper('sources', 'get_sources')
|
||||
|
||||
def test_get_info(self):
|
||||
self._test_passthrough_helper('info', 'get_info')
|
||||
|
||||
def test_get_timeouts(self):
|
||||
self._test_passthrough_helper('timeouts', 'get_timeouts')
|
||||
|
||||
def test_get_tables(self):
|
||||
self._test_passthrough_helper('tables', 'get_tables')
|
||||
|
||||
def test_get_memory(self):
|
||||
self._test_passthrough_helper('memory', 'get_memory')
|
||||
|
||||
def test_get_labels(self, reset_flag=False):
|
||||
expected = {'labels': 'thelabels'}
|
||||
self.pf_mgr.get_labels.return_value = 'thelabels'
|
||||
method = 'post' if reset_flag else 'get'
|
||||
args = (True, ) if reset_flag else ()
|
||||
result = getattr(self.test_app, method)('/v1/firewall/labels')
|
||||
self.assertEqual(result.status_code, 200)
|
||||
self.pf_mgr.get_labels.assert_called_once_with(*args)
|
||||
self.assertEqual(json.loads(result.data), expected)
|
||||
|
||||
def test_get_labels_reset(self):
|
||||
self.test_get_labels(True)
|
||||
|
153
test/unit/drivers/test_iptables.py
Normal file
153
test/unit/drivers/test_iptables.py
Normal file
@ -0,0 +1,153 @@
|
||||
from unittest import TestCase
|
||||
|
||||
import mock
|
||||
|
||||
from akanda.router import models
|
||||
from akanda.router.drivers import iptables
|
||||
|
||||
CONFIG = models.Configuration({
|
||||
'networks': [{
|
||||
'network_id': 'ABC123',
|
||||
'interface': {
|
||||
'ifname': 'eth0',
|
||||
'addresses': [
|
||||
'fdca:3ba5:a17a:acda:f816:3eff:fe66:33b6/64',
|
||||
'fe80::f816:3eff:fe66:33b6/64'
|
||||
]
|
||||
},
|
||||
'name': 'mgt',
|
||||
'network_type': models.Network.TYPE_MANAGEMENT,
|
||||
}, {
|
||||
'network_id': 'ABC456',
|
||||
'interface': {
|
||||
'ifname': 'eth1',
|
||||
'addresses': [
|
||||
'172.16.77.2/24',
|
||||
'fdee:9f85:83be:0:f816:3eff:fe42:a9f/48'
|
||||
]
|
||||
},
|
||||
'name': 'ext',
|
||||
'network_type': models.Network.TYPE_EXTERNAL,
|
||||
'subnets': [{
|
||||
'cidr': '172.16.77.0/24',
|
||||
'gateway_ip': '172.16.77.1',
|
||||
'dhcp_enabled': True,
|
||||
'dns_nameservers': []
|
||||
}]
|
||||
}, {
|
||||
'network_id': 'ABC789',
|
||||
'interface': {
|
||||
'ifname': 'eth2',
|
||||
'addresses': [
|
||||
'192.168.0.1/24',
|
||||
'fdd6:a1fa:cfa8:9df::1/64'
|
||||
]
|
||||
},
|
||||
'name': 'internal',
|
||||
'network_type': models.Network.TYPE_INTERNAL,
|
||||
'subnets': [{
|
||||
'cidr': '192.168.0.0/24',
|
||||
'gateway_ip': '192.168.0.1',
|
||||
'dhcp_enabled': True,
|
||||
'dns_nameservers': []
|
||||
}]
|
||||
}],
|
||||
'floating_ips': [{
|
||||
'fixed_ip': '192.168.0.2',
|
||||
'floating_ip': '172.16.77.50'
|
||||
}]
|
||||
})
|
||||
|
||||
V4_OUTPUT = [
|
||||
'*filter',
|
||||
':INPUT DROP [0:0]',
|
||||
':FORWARD ACCEPT [0:0]',
|
||||
':OUTPUT ACCEPT [0:0]',
|
||||
'-A INPUT -p icmp --icmp-type echo-request -j ACCEPT',
|
||||
'-A FORWARD -d 192.168.0.1 -o eth2 -m state --state RELATED,ESTABLISHED -j ACCEPT', # noqa
|
||||
'-A INPUT -i eth2 -p udp -m udp --dport 67 -j ACCEPT',
|
||||
'-A INPUT -i eth2 -p tcp -m tcp --dport 67 -j ACCEPT',
|
||||
'-A FORWARD -s 169.254.169.254 -o eth2 -m state --state RELATED,ESTABLISHED -j ACCEPT', # noqa
|
||||
'-A INPUT -i eth2 -j ACCEPT',
|
||||
'-A INPUT -i eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT',
|
||||
'COMMIT',
|
||||
'*nat',
|
||||
':PREROUTING ACCEPT [0:0]',
|
||||
':INPUT ACCEPT [0:0]',
|
||||
':OUTPUT ACCEPT [0:0]',
|
||||
':POSTROUTING ACCEPT [0:0]',
|
||||
'-A PREROUTING -s 192.168.0.1 -d 169.254.169.254 -p tcp -m tcp --dport 80 -j DNAT --to-destination 127.0.0.1:9602', # noqa
|
||||
'-A POSTROUTING -s 192.168.0.1 -o eth2 -j SNAT --to 172.16.77.2',
|
||||
'-A POSTROUTING -o eth1 -s 192.168.0.2 -j SNAT --to 172.16.77.50',
|
||||
'COMMIT'
|
||||
]
|
||||
|
||||
V6_OUTPUT = [
|
||||
'*filter',
|
||||
':INPUT DROP [0:0]',
|
||||
':FORWARD ACCEPT [0:0]',
|
||||
':OUTPUT ACCEPT [0:0]',
|
||||
'-A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT',
|
||||
'-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT',
|
||||
'-A INPUT -i eth0 -p tcp -m tcp --dport 5000 -j ACCEPT',
|
||||
'-A INPUT -i eth0 -p tcp -m tcp --dport 9697 -j ACCEPT',
|
||||
'-A INPUT -i !eth0 -d fdca:3ba5:a17a:acda:f816:3eff:fe66:33b6 -j DROP',
|
||||
'-A FORWARD -d fdd6:a1fa:cfa8:9df::1 -o eth2 -m state --state RELATED,ESTABLISHED -j ACCEPT', # noqa
|
||||
'-A INPUT -i eth2 -p udp -m udp --dport 546 -j ACCEPT',
|
||||
'-A INPUT -i eth2 -p tcp -m tcp --dport 546 -j ACCEPT',
|
||||
'-A INPUT -i eth2 -j ACCEPT',
|
||||
'-A INPUT -i eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT',
|
||||
'COMMIT'
|
||||
]
|
||||
|
||||
|
||||
class TestIPTablesConfiguration(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestIPTablesConfiguration, self).setUp()
|
||||
self.execute = mock.patch('akanda.router.utils.execute').start()
|
||||
self.replace = mock.patch('akanda.router.utils.replace_file').start()
|
||||
self.patches = [self.execute, self.replace]
|
||||
|
||||
def tearDown(self):
|
||||
super(TestIPTablesConfiguration, self).tearDown()
|
||||
for p in self.patches:
|
||||
p.stop()
|
||||
|
||||
def test_complete(self):
|
||||
mgr = iptables.IPTablesManager()
|
||||
mgr.save_config(CONFIG, {
|
||||
'ge0': 'eth0',
|
||||
'ge1': 'eth1',
|
||||
'ge2': 'eth2'
|
||||
})
|
||||
|
||||
assert self.replace.call_count == 2
|
||||
|
||||
assert mock.call(
|
||||
'/tmp/iptables.rules',
|
||||
'\n'.join(V4_OUTPUT) + '\n'
|
||||
) in self.replace.call_args_list
|
||||
|
||||
assert mock.call(
|
||||
'/tmp/ip6tables.rules',
|
||||
'\n'.join(V6_OUTPUT) + '\n'
|
||||
) in self.replace.call_args_list
|
||||
|
||||
assert self.execute.call_args_list == [
|
||||
mock.call(
|
||||
['mv', '/tmp/iptables.rules', '/etc/iptables/rules.v4'],
|
||||
'sudo'
|
||||
),
|
||||
mock.call(
|
||||
['mv', '/tmp/ip6tables.rules', '/etc/iptables/rules.v6'],
|
||||
'sudo'
|
||||
)
|
||||
]
|
||||
|
||||
def test_restart(self):
|
||||
mgr = iptables.IPTablesManager()
|
||||
mgr.restart()
|
||||
assert self.execute.call_args_list == [
|
||||
mock.call(['/etc/init.d/iptables-persistent', 'restart'], 'sudo')
|
||||
]
|
Loading…
Reference in New Issue
Block a user