Merge "nova-net: Remove firewall support (pt. 3)"

This commit is contained in:
Zuul 2020-01-09 15:56:51 +00:00 committed by Gerrit Code Review
commit 6fd8610da9
9 changed files with 5 additions and 2273 deletions

View File

@ -856,12 +856,6 @@ Related options:
* iptables_top_regex
"""),
# NOTE(sfinucan): While this is predominantly used by nova-network, there
# appears to be a very limited use case where iptables rules are also used
# with neutron. Namely, when neutron's port filtering is disabled, security
# groups are disabled, and the 'firewall_driver' has been set to the
# libvirt IPTables driver. We may wish to remove this functionality in
# favour of neutron in the future.
cfg.StrOpt("iptables_drop_action",
default="DROP",
deprecated_for_removal=True,
@ -879,12 +873,6 @@ Possible values:
* A string representing an iptables chain. The default is DROP.
"""),
# NOTE(sfinucan): While this is predominantly used by nova-network, there
# appears to be a very limited use case where iptables rules are also used
# with neutron. Namely, when neutron's port filtering is disabled, security
# groups are disabled, and the 'firewall_driver' has been set to the
# libvirt IPTables driver. We may wish to remove this functionality in
# favour of neutron in the future.
cfg.BoolOpt('defer_iptables_apply',
default=False,
deprecated_for_removal=True,
@ -1135,66 +1123,6 @@ of Neutron in your deployment.
Related options:
* ``use_neutron``
"""),
cfg.StrOpt('firewall_driver',
deprecated_for_removal=True,
deprecated_since='16.0.0',
deprecated_reason="""
nova-network is deprecated, as are any related configuration options.
""",
default='nova.virt.firewall.NoopFirewallDriver',
help="""
Firewall driver to use with ``nova-network`` service.
This option only applies when using the ``nova-network`` service. When using
another networking services, such as Neutron, this should be to set to the
``nova.virt.firewall.NoopFirewallDriver``.
Possible values:
* ``nova.virt.firewall.IptablesFirewallDriver``
* ``nova.virt.firewall.NoopFirewallDriver``
* ``nova.virt.libvirt.firewall.IptablesFirewallDriver``
* [...]
Related options:
* ``use_neutron``: This must be set to ``False`` to enable ``nova-network``
networking
"""),
cfg.BoolOpt('allow_same_net_traffic',
default=True,
deprecated_for_removal=True,
deprecated_since='16.0.0',
deprecated_reason="""
nova-network is deprecated, as are any related configuration options.
""",
help="""
Determine whether to allow network traffic from same network.
When set to true, hosts on the same subnet are not filtered and are allowed
to pass all types of traffic between them. On a flat network, this allows
all instances from all projects unfiltered communication. With VLAN
networking, this allows access between instances within the same project.
This option only applies when using the ``nova-network`` service. When using
another networking services, such as Neutron, security groups or other
approaches should be used.
Possible values:
* True: Network traffic should be allowed pass between all instances on the
same network, regardless of their tenant and security policies
* False: Network traffic should not be allowed pass between instances unless
it is unblocked in a security group
Related options:
* ``use_neutron``: This must be set to ``False`` to enable ``nova-network``
networking
* ``firewall_driver``: This must be set to
``nova.virt.libvirt.firewall.IptablesFirewallDriver`` to ensure the
libvirt firewall driver is enabled.
"""),
]

View File

@ -212,9 +212,8 @@ def _get_virt_name(regex, data):
return None
driver = m.group(1)
# Ignore things we mis-detect as virt drivers in the regex
if driver in ["test_virt_drivers", "driver", "firewall",
"disk", "api", "imagecache", "cpu", "hardware",
"image"]:
if driver in ["test_virt_drivers", "driver", "disk", "api", "imagecache",
"cpu", "hardware", "image"]:
return None
return driver

View File

@ -67,10 +67,6 @@ class HackingTestCase(test.NoDBTestCase):
"from nova.virt.libvirt import utils as libvirt_utils",
"./nova/virt/libvirt/driver.py"))
self.assertIsNone(checks.import_no_virt_driver_import_deps(
"import nova.virt.firewall",
"./nova/virt/libvirt/firewall.py"))
def test_virt_driver_config_vars(self):
self.assertIsInstance(checks.import_no_virt_driver_config_deps(
"CONF.import_opt('volume_drivers', "

View File

@ -1,764 +0,0 @@
# Copyright 2010 OpenStack Foundation
# Copyright 2012 University Of Minho
#
# 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 xml.dom import minidom
from eventlet import greenthread
from lxml import etree
import mock
from oslo_concurrency.fixture import lockutils as lock_fixture
from oslo_utils.fixture import uuidsentinel as uuids
from oslo_utils import uuidutils
from nova import exception
from nova.network import linux_net
from nova import objects
from nova import test
from nova.tests.unit import fake_network
from nova.tests.unit.virt.libvirt import fakelibvirt
from nova.virt.libvirt import firewall
from nova.virt.libvirt import host
from nova.virt import netutils
_fake_network_info = fake_network.fake_get_instance_nw_info
_fake_stub_out_get_nw_info = fake_network.stub_out_nw_api_get_instance_nw_info
_ipv4_like = fake_network.ipv4_like
class NWFilterFakes(object):
def __init__(self):
self.filters = {}
def nwfilterLookupByName(self, name):
if name in self.filters:
return self.filters[name]
raise fakelibvirt.libvirtError('Filter Not Found')
def filterDefineXMLMock(self, xml):
class FakeNWFilterInternal(object):
def __init__(self, parent, name, u, xml):
self.name = name
self.uuid = u
self.parent = parent
self.xml = xml
def XMLDesc(self, flags):
return self.xml
def undefine(self):
del self.parent.filters[self.name]
tree = etree.fromstring(xml)
name = tree.get('name')
u = tree.find('uuid')
if u is None:
u = uuidutils.generate_uuid(dashed=False)
else:
u = u.text
if name not in self.filters:
self.filters[name] = FakeNWFilterInternal(self, name, u, xml)
else:
if self.filters[name].uuid != u:
raise fakelibvirt.libvirtError(
"Mismatching name '%s' with uuid '%s' vs '%s'"
% (name, self.filters[name].uuid, u))
self.filters[name].xml = xml
return True
class IptablesFirewallTestCase(test.NoDBTestCase):
def setUp(self):
super(IptablesFirewallTestCase, self).setUp()
self.useFixture(lock_fixture.ExternalLockFixture())
self.useFixture(fakelibvirt.FakeLibvirtFixture())
self.fw = firewall.IptablesFirewallDriver(
host=host.Host("qemu:///system"))
in_rules = [
'# Generated by iptables-save v1.4.10 on Sat Feb 19 00:03:19 2011',
'*nat',
':PREROUTING ACCEPT [1170:189210]',
':INPUT ACCEPT [844:71028]',
':OUTPUT ACCEPT [5149:405186]',
':POSTROUTING ACCEPT [5063:386098]',
'# Completed on Tue Dec 18 15:50:25 2012',
'# Generated by iptables-save v1.4.12 on Tue Dec 18 15:50:25 201;',
'*mangle',
':PREROUTING ACCEPT [241:39722]',
':INPUT ACCEPT [230:39282]',
':FORWARD ACCEPT [0:0]',
':OUTPUT ACCEPT [266:26558]',
':POSTROUTING ACCEPT [267:26590]',
'-A POSTROUTING -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM '
'--checksum-fill',
'COMMIT',
'# Completed on Tue Dec 18 15:50:25 2012',
'# Generated by iptables-save v1.4.4 on Mon Dec 6 11:54:13 2010',
'*filter',
':INPUT ACCEPT [969615:281627771]',
':FORWARD ACCEPT [0:0]',
':OUTPUT ACCEPT [915599:63811649]',
':nova-block-ipv4 - [0:0]',
'[0:0] -A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT ',
'[0:0] -A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED'
',ESTABLISHED -j ACCEPT ',
'[0:0] -A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT ',
'[0:0] -A FORWARD -i virbr0 -o virbr0 -j ACCEPT ',
'[0:0] -A FORWARD -o virbr0 -j REJECT '
'--reject-with icmp-port-unreachable ',
'[0:0] -A FORWARD -i virbr0 -j REJECT '
'--reject-with icmp-port-unreachable ',
'COMMIT',
'# Completed on Mon Dec 6 11:54:13 2010',
]
in6_filter_rules = [
'# Generated by ip6tables-save v1.4.4 on Tue Jan 18 23:47:56 2011',
'*filter',
':INPUT ACCEPT [349155:75810423]',
':FORWARD ACCEPT [0:0]',
':OUTPUT ACCEPT [349256:75777230]',
'COMMIT',
'# Completed on Tue Jan 18 23:47:56 2011',
]
def _create_instance_ref(self,
uuid="74526555-9166-4893-a203-126bdcab0d67"):
inst = objects.Instance(
id=7,
uuid=uuid,
user_id="fake",
project_id="fake",
image_ref='155d900f-4e14-4e4c-a73d-069cbf4541e6',
instance_type_id=1)
inst.info_cache = objects.InstanceInfoCache()
inst.info_cache.deleted = False
return inst
@mock.patch.object(objects.InstanceList, "get_by_security_group_id")
@mock.patch.object(objects.SecurityGroupRuleList, "get_by_instance")
@mock.patch('nova.privsep.linux_net.iptables_get_rules',
return_value=('', ''))
@mock.patch('nova.privsep.linux_net.iptables_set_rules',
return_value=('', ''))
def test_static_filters(self, mock_iptables_set_rules,
mock_iptables_get_rules, mock_secrule,
mock_instlist):
UUID = "2674993b-6adb-4733-abd9-a7c10cc1f146"
SRC_UUID = "0e0a76b2-7c52-4bc0-9a60-d83017e42c1a"
instance_ref = self._create_instance_ref(UUID)
src_instance_ref = self._create_instance_ref(SRC_UUID)
secgroup = objects.SecurityGroup(id=1,
user_id='fake',
project_id='fake',
name='testgroup',
description='test group')
src_secgroup = objects.SecurityGroup(id=2,
user_id='fake',
project_id='fake',
name='testsourcegroup',
description='src group')
r1 = objects.SecurityGroupRule(parent_group_id=secgroup.id,
protocol='icmp',
from_port=-1,
to_port=-1,
cidr='192.168.11.0/24',
grantee_group=None)
r2 = objects.SecurityGroupRule(parent_group_id=secgroup.id,
protocol='icmp',
from_port=8,
to_port=-1,
cidr='192.168.11.0/24',
grantee_group=None)
r3 = objects.SecurityGroupRule(parent_group_id=secgroup.id,
protocol='tcp',
from_port=80,
to_port=81,
cidr='192.168.10.0/24',
grantee_group=None)
r4 = objects.SecurityGroupRule(parent_group_id=secgroup.id,
protocol='tcp',
from_port=80,
to_port=81,
cidr=None,
grantee_group=src_secgroup,
group_id=src_secgroup.id)
r5 = objects.SecurityGroupRule(parent_group_id=secgroup.id,
protocol=None,
cidr=None,
grantee_group=src_secgroup,
group_id=src_secgroup.id)
secgroup_list = objects.SecurityGroupList()
secgroup_list.objects.append(secgroup)
src_secgroup_list = objects.SecurityGroupList()
src_secgroup_list.objects.append(src_secgroup)
instance_ref.security_groups = secgroup_list
src_instance_ref.security_groups = src_secgroup_list
mock_secrule.return_value = objects.SecurityGroupRuleList(
objects=[r1, r2, r3, r4, r5])
def fake_instlist(ctxt, id):
if id == src_secgroup.id:
insts = objects.InstanceList()
insts.objects.append(src_instance_ref)
return insts
else:
insts = objects.InstanceList()
insts.objects.append(instance_ref)
return insts
mock_instlist.side_effect = fake_instlist
def fake_iptables_get(ipv4=True):
if ipv4:
return '\n'.join(self.in_rules), None
else:
return '\n'.join(self.in6_filter_rules), None
mock_iptables_get_rules.side_effect = fake_iptables_get
def fake_iptables_set(rules, ipv4=True):
if '*filter' in rules:
if ipv4:
self.out_rules = rules
else:
self.out6_rules = rules
return '', ''
mock_iptables_set_rules.side_effect = fake_iptables_set
network_model = _fake_network_info(self, 1)
self.stub_out('nova.objects.Instance.get_network_info',
lambda instance: network_model)
self.fw.prepare_instance_filter(instance_ref, network_model)
self.fw.apply_instance_filter(instance_ref, network_model)
in_rules = [l for l in self.in_rules if not l.startswith('#')]
for rule in in_rules:
if 'nova' not in rule:
self.assertIn(rule, self.out_rules,
'Rule went missing: %s' % rule)
instance_chain = None
for rule in self.out_rules:
# This is pretty crude, but it'll do for now
# last two octets change
if re.search('-d 192.168.[0-9]{1,3}.[0-9]{1,3} -j', rule):
instance_chain = rule.split(' ')[-1]
break
self.assertTrue(instance_chain, "The instance chain wasn't added")
security_group_chain = None
for rule in self.out_rules:
# This is pretty crude, but it'll do for now
if '-A %s -j' % instance_chain in rule:
security_group_chain = rule.split(' ')[-1]
break
self.assertTrue(security_group_chain,
"The security group chain wasn't added")
regex = re.compile(r'\[0\:0\] -A .* -j ACCEPT -p icmp '
'-s 192.168.11.0/24')
match_rules = [rule for rule in self.out_rules if regex.match(rule)]
self.assertGreater(len(match_rules), 0,
"ICMP acceptance rule wasn't added")
regex = re.compile(r'\[0\:0\] -A .* -j ACCEPT -p icmp -m icmp '
'--icmp-type 8 -s 192.168.11.0/24')
match_rules = [rule for rule in self.out_rules if regex.match(rule)]
self.assertGreater(len(match_rules), 0,
"ICMP Echo Request acceptance rule wasn't added")
for ip in network_model.fixed_ips():
if ip['version'] != 4:
continue
regex = re.compile(r'\[0\:0\] -A .* -j ACCEPT -p tcp -m multiport '
'--dports 80:81 -s %s' % ip['address'])
match_rules = [rule for rule in self.out_rules
if regex.match(rule)]
self.assertGreater(len(match_rules), 0,
"TCP port 80/81 acceptance rule wasn't added")
regex = re.compile(r'\[0\:0\] -A .* -j ACCEPT -s '
'%s' % ip['address'])
match_rules = [rule for rule in self.out_rules
if regex.match(rule)]
self.assertGreater(len(match_rules), 0,
"Protocol/port-less acceptance rule"
" wasn't added")
regex = re.compile(r'\[0\:0\] -A .* -j ACCEPT -p tcp '
'-m multiport --dports 80:81 -s 192.168.10.0/24')
match_rules = [rule for rule in self.out_rules if regex.match(rule)]
self.assertGreater(len(match_rules), 0,
"TCP port 80/81 acceptance rule wasn't added")
def test_filters_for_instance(self):
network_info = _fake_network_info(self, 1)
rulesv4, rulesv6 = self.fw._filters_for_instance("fake", network_info)
self.assertEqual(len(rulesv4), 2)
self.assertEqual(len(rulesv6), 1)
@mock.patch.object(objects.SecurityGroupRuleList, "get_by_instance")
@mock.patch('nova.privsep.linux_net.iptables_get_rules',
return_value=('', ''))
@mock.patch('nova.privsep.linux_net.iptables_set_rules',
return_value=('', ''))
def test_multinic_iptables(self, mock_iptables_set_rules,
mock_iptables_get_rules, mock_secrule):
mock_secrule.return_value = objects.SecurityGroupRuleList()
ipv4_rules_per_addr = 1
ipv4_addr_per_network = 2
ipv6_rules_per_addr = 1
ipv6_addr_per_network = 1
networks_count = 5
instance_ref = self._create_instance_ref()
instance_ref.security_groups = objects.SecurityGroupList()
network_info = _fake_network_info(self, networks_count,
ipv4_addr_per_network)
network_info[0]['network']['subnets'][0]['meta']['dhcp_server'] = \
'1.1.1.1'
ipv4_len = len(self.fw.iptables.ipv4['filter'].rules)
ipv6_len = len(self.fw.iptables.ipv6['filter'].rules)
inst_ipv4, inst_ipv6 = self.fw.instance_rules(instance_ref,
network_info)
self.fw.prepare_instance_filter(instance_ref, network_info)
ipv4 = self.fw.iptables.ipv4['filter'].rules
ipv6 = self.fw.iptables.ipv6['filter'].rules
ipv4_network_rules = len(ipv4) - len(inst_ipv4) - ipv4_len
ipv6_network_rules = len(ipv6) - len(inst_ipv6) - ipv6_len
# Extra rules are for the DHCP request
rules = (ipv4_rules_per_addr * ipv4_addr_per_network *
networks_count) + 2
self.assertEqual(ipv4_network_rules, rules)
self.assertEqual(ipv6_network_rules,
ipv6_rules_per_addr * ipv6_addr_per_network * networks_count)
@mock.patch.object(firewall.IptablesFirewallDriver, 'instance_rules')
@mock.patch.object(firewall.IptablesFirewallDriver,
'add_filters_for_instance')
@mock.patch.object(linux_net.IptablesTable, 'has_chain')
@mock.patch('nova.privsep.linux_net.iptables_get_rules',
return_value=('', ''))
@mock.patch('nova.privsep.linux_net.iptables_set_rules',
return_value=('', ''))
def test_do_refresh_security_group_rules(
self, mock_iptables_set_rules,
mock_iptables_get_rules, mock_has_chain,
mock_add_filters, mock_instance_rules):
instance_ref = self._create_instance_ref()
mock_instance_rules.return_value = (None, None)
mock_has_chain.return_value = True
self.fw.prepare_instance_filter(instance_ref, mock.ANY)
self.fw.instance_info[instance_ref['id']] = (instance_ref, None)
self.fw.do_refresh_security_group_rules("fake")
expected_rules_calls = [mock.call(instance_ref, None),
mock.call(instance_ref, None)]
expected_filter_calls = [mock.call(instance_ref, mock.ANY, mock.ANY,
mock.ANY),
mock.call(instance_ref, mock.ANY, mock.ANY,
mock.ANY)]
self.assertEqual(mock_instance_rules.mock_calls,
expected_rules_calls)
self.assertEqual(mock_add_filters.mock_calls, expected_filter_calls)
mock_has_chain.assert_called_once_with(mock.ANY)
def test_do_refresh_security_group_rules_instance_gone(self):
instance1 = objects.Instance(None, id=1, uuid=uuids.instance_1)
instance2 = objects.Instance(None, id=2, uuid=uuids.instance_2)
self.fw.instance_info = {1: (instance1, 'netinfo1'),
2: (instance2, 'netinfo2')}
mock_filter = mock.MagicMock()
with mock.patch.dict(self.fw.iptables.ipv4, {'filter': mock_filter}):
mock_filter.has_chain.return_value = False
with mock.patch.object(self.fw, 'instance_rules') as mock_ir:
mock_ir.return_value = (None, None)
self.fw.do_refresh_security_group_rules('secgroup')
self.assertEqual(2, mock_ir.call_count)
# NOTE(danms): Make sure that it is checking has_chain each time,
# continuing to process all the instances, and never adding the
# new chains back if has_chain() is False
mock_filter.has_chain.assert_has_calls([mock.call('inst-1'),
mock.call('inst-2')],
any_order=True)
self.assertEqual(0, mock_filter.add_chain.call_count)
@mock.patch.object(fakelibvirt.virConnect, "nwfilterLookupByName")
@mock.patch.object(fakelibvirt.virConnect, "nwfilterDefineXML")
@mock.patch.object(objects.InstanceList, "get_by_security_group_id")
@mock.patch.object(objects.SecurityGroupRuleList, "get_by_instance")
@mock.patch('nova.privsep.linux_net.iptables_get_rules',
return_value=('', ''))
@mock.patch('nova.privsep.linux_net.iptables_set_rules',
return_value=('', ''))
def test_unfilter_instance_undefines_nwfilter(
self, mock_iptables_set_rules, mock_iptables_get_rules,
mock_secrule, mock_instlist, mock_define, mock_lookup):
fakefilter = NWFilterFakes()
mock_lookup.side_effect = fakefilter.nwfilterLookupByName
mock_define.side_effect = fakefilter.filterDefineXMLMock
instance_ref = self._create_instance_ref()
instance_ref.security_groups = objects.SecurityGroupList()
mock_secrule.return_value = objects.SecurityGroupRuleList()
network_info = _fake_network_info(self, 1)
self.fw.setup_basic_filtering(instance_ref, network_info)
self.fw.prepare_instance_filter(instance_ref, network_info)
self.fw.apply_instance_filter(instance_ref, network_info)
original_filter_count = len(fakefilter.filters)
self.fw.unfilter_instance(instance_ref, network_info)
# should undefine just the instance filter
self.assertEqual(original_filter_count - len(fakefilter.filters), 1)
@mock.patch.object(firewall, 'libvirt', fakelibvirt)
class NWFilterTestCase(test.NoDBTestCase):
def setUp(self):
super(NWFilterTestCase, self).setUp()
self.useFixture(fakelibvirt.FakeLibvirtFixture())
self.fw = firewall.NWFilterFirewall(host=host.Host("qemu:///system"))
def _create_security_group(self, instance_ref):
secgroup = objects.SecurityGroup(id=1,
user_id='fake',
project_id='fake',
name='testgroup',
description='test group description')
secgroup_list = objects.SecurityGroupList()
secgroup_list.objects.append(secgroup)
instance_ref.security_groups = secgroup_list
return secgroup
def _create_instance(self):
inst = objects.Instance(
id=7,
uuid="74526555-9166-4893-a203-126bdcab0d67",
user_id="fake",
project_id="fake",
image_ref='155d900f-4e14-4e4c-a73d-069cbf4541e6',
instance_type_id=1)
inst.info_cache = objects.InstanceInfoCache()
inst.info_cache.deleted = False
return inst
@mock.patch.object(fakelibvirt.virConnect, "nwfilterDefineXML")
def test_creates_base_rule_first(self, mock_define):
# These come pre-defined by libvirt
self.defined_filters = ['no-mac-spoofing',
'no-ip-spoofing',
'no-arp-spoofing',
'allow-dhcp-server']
self.recursive_depends = {}
for f in self.defined_filters:
self.recursive_depends[f] = []
def fake_define(xml):
dom = minidom.parseString(xml)
name = dom.firstChild.getAttribute('name')
self.recursive_depends[name] = []
for f in dom.getElementsByTagName('filterref'):
ref = f.getAttribute('filter')
self.assertIn(ref, self.defined_filters,
('%s referenced filter that does ' +
'not yet exist: %s') % (name, ref))
dependencies = [ref] + self.recursive_depends[ref]
self.recursive_depends[name] += dependencies
self.defined_filters.append(name)
return True
mock_define.side_effect = fake_define
instance_ref = self._create_instance()
self._create_security_group(instance_ref)
def _ensure_all_called(mac, allow_dhcp):
instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'],
mac.translate({ord(':'): None}))
requiredlist = ['no-arp-spoofing', 'no-ip-spoofing',
'no-mac-spoofing']
required_not_list = []
if allow_dhcp:
requiredlist.append('allow-dhcp-server')
else:
required_not_list.append('allow-dhcp-server')
for required in requiredlist:
self.assertIn(required,
self.recursive_depends[instance_filter],
"Instance's filter does not include %s" %
required)
for required_not in required_not_list:
self.assertNotIn(required_not,
self.recursive_depends[instance_filter],
"Instance filter includes %s" % required_not)
network_info = _fake_network_info(self, 1)
# since there is one (network_info) there is one vif
# pass this vif's mac to _ensure_all_called()
# to set the instance_filter properly
mac = network_info[0]['address']
network_info[0]['network']['subnets'][0]['meta']['dhcp_server'] = \
'1.1.1.1'
self.fw.setup_basic_filtering(instance_ref, network_info)
allow_dhcp = True
_ensure_all_called(mac, allow_dhcp)
network_info[0]['network']['subnets'][0]['meta']['dhcp_server'] = None
self.fw.setup_basic_filtering(instance_ref, network_info)
allow_dhcp = False
_ensure_all_called(mac, allow_dhcp)
@mock.patch.object(fakelibvirt.virConnect, "nwfilterLookupByName")
@mock.patch.object(fakelibvirt.virConnect, "nwfilterDefineXML")
def test_unfilter_instance_undefines_nwfilters(self,
mock_define,
mock_lookup):
fakefilter = NWFilterFakes()
mock_lookup.side_effect = fakefilter.nwfilterLookupByName
mock_define.side_effect = fakefilter.filterDefineXMLMock
instance_ref = self._create_instance()
self._create_security_group(instance_ref)
network_info = _fake_network_info(self, 1)
self.fw.setup_basic_filtering(instance_ref, network_info)
original_filter_count = len(fakefilter.filters)
self.fw.unfilter_instance(instance_ref, network_info)
self.assertEqual(original_filter_count - len(fakefilter.filters), 1)
@mock.patch.object(fakelibvirt.virConnect, "nwfilterLookupByName")
@mock.patch.object(greenthread, 'sleep')
def test_unfilter_instance_retry_and_error(self, mock_sleep, mock_lookup):
# Tests that we try to undefine the network filter when it's in use
# until we hit a timeout. We try two times and sleep once in between.
self.flags(live_migration_retry_count=2)
in_use = fakelibvirt.libvirtError('nwfilter is in use')
in_use.err = (fakelibvirt.VIR_ERR_OPERATION_INVALID,)
mock_undefine = mock.Mock(side_effect=in_use)
fakefilter = mock.MagicMock(undefine=mock_undefine)
mock_lookup.return_value = fakefilter
instance_ref = self._create_instance()
network_info = _fake_network_info(self, 1)
self.assertRaises(fakelibvirt.libvirtError, self.fw.unfilter_instance,
instance_ref, network_info)
self.assertEqual(2, mock_lookup.call_count)
self.assertEqual(2, mock_undefine.call_count)
mock_sleep.assert_called_once_with(1)
@mock.patch.object(fakelibvirt.virConnect, "nwfilterLookupByName")
@mock.patch.object(greenthread, 'sleep')
def test_unfilter_instance_retry_not_found(self, mock_sleep, mock_lookup):
# Tests that we exit if the nw filter is not found.
in_use = fakelibvirt.libvirtError('nwfilter is in use')
in_use.err = (fakelibvirt.VIR_ERR_OPERATION_INVALID,)
not_found = fakelibvirt.libvirtError('no nwfilter with matching name')
not_found.err = (fakelibvirt.VIR_ERR_NO_NWFILTER,)
mock_undefine = mock.Mock(side_effect=(in_use, not_found))
fakefilter = mock.MagicMock(undefine=mock_undefine)
mock_lookup.return_value = fakefilter
instance_ref = self._create_instance()
network_info = _fake_network_info(self, 1)
self.fw.unfilter_instance(instance_ref, network_info)
self.assertEqual(2, mock_lookup.call_count)
self.assertEqual(2, mock_undefine.call_count)
mock_sleep.assert_called_once_with(1)
@mock.patch.object(fakelibvirt.virConnect, "nwfilterLookupByName")
@mock.patch.object(greenthread, 'sleep')
def test_unfilter_instance_retry_and_pass(self, mock_sleep, mock_lookup):
# Tests that we retry on in-use error but pass if undefine() works
# while looping.
in_use = fakelibvirt.libvirtError('nwfilter is in use')
in_use.err = (fakelibvirt.VIR_ERR_OPERATION_INVALID,)
mock_undefine = mock.Mock(side_effect=(in_use, None))
fakefilter = mock.MagicMock(undefine=mock_undefine)
mock_lookup.return_value = fakefilter
instance_ref = self._create_instance()
network_info = _fake_network_info(self, 1)
self.fw.unfilter_instance(instance_ref, network_info)
self.assertEqual(2, mock_lookup.call_count)
self.assertEqual(2, mock_undefine.call_count)
mock_sleep.assert_called_once_with(1)
def test_redefining_nwfilters(self):
fakefilter = NWFilterFakes()
self.fw._conn.nwfilterDefineXML = fakefilter.filterDefineXMLMock
self.fw._conn.nwfilterLookupByName = fakefilter.nwfilterLookupByName
instance_ref = self._create_instance()
self._create_security_group(instance_ref)
network_info = _fake_network_info(self, 1)
self.fw.setup_basic_filtering(instance_ref, network_info)
self.fw.setup_basic_filtering(instance_ref, network_info)
@mock.patch.object(fakelibvirt.virConnect, "nwfilterLookupByName")
@mock.patch.object(fakelibvirt.virConnect, "nwfilterDefineXML")
def test_nwfilter_parameters(self,
mock_define,
mock_lookup):
fakefilter = NWFilterFakes()
mock_lookup.side_effect = fakefilter.nwfilterLookupByName
mock_define.side_effect = fakefilter.filterDefineXMLMock
instance_ref = self._create_instance()
self._create_security_group(instance_ref)
network_info = _fake_network_info(self, 1)
self.fw.setup_basic_filtering(instance_ref, network_info)
vif = network_info[0]
nic_id = vif['address'].replace(':', '')
instance_filter_name = self.fw._instance_filter_name(instance_ref,
nic_id)
f = fakefilter.nwfilterLookupByName(instance_filter_name)
tree = etree.fromstring(f.xml)
for fref in tree.findall('filterref'):
parameters = fref.findall('./parameter')
for parameter in parameters:
subnet_v4, subnet_v6 = vif['network']['subnets']
if parameter.get('name') == 'IP':
self.assertTrue(_ipv4_like(parameter.get('value'),
'192.168'))
elif parameter.get('name') == 'DHCPSERVER':
dhcp_server = subnet_v4.get('dhcp_server')
self.assertEqual(parameter.get('value'), dhcp_server)
elif parameter.get('name') == 'RASERVER':
ra_server = subnet_v6['gateway']['address'] + "/128"
self.assertEqual(parameter.get('value'), ra_server)
elif parameter.get('name') == 'PROJNET':
ipv4_cidr = subnet_v4['cidr']
net, mask = netutils.get_net_and_mask(ipv4_cidr)
self.assertEqual(parameter.get('value'), net)
elif parameter.get('name') == 'PROJMASK':
ipv4_cidr = subnet_v4['cidr']
net, mask = netutils.get_net_and_mask(ipv4_cidr)
self.assertEqual(parameter.get('value'), mask)
elif parameter.get('name') == 'PROJNET6':
ipv6_cidr = subnet_v6['cidr']
net, prefix = netutils.get_net_and_prefixlen(ipv6_cidr)
self.assertEqual(parameter.get('value'), net)
elif parameter.get('name') == 'PROJMASK6':
ipv6_cidr = subnet_v6['cidr']
net, prefix = netutils.get_net_and_prefixlen(ipv6_cidr)
self.assertEqual(parameter.get('value'), prefix)
else:
raise exception.InvalidParameterValue('unknown parameter '
'in filter')
@mock.patch.object(fakelibvirt.virConnect, "nwfilterLookupByName")
@mock.patch.object(fakelibvirt.virConnect, "nwfilterDefineXML")
def test_multinic_base_filter_selection(self,
mock_define,
mock_lookup):
fakefilter = NWFilterFakes()
mock_lookup.side_effect = fakefilter.nwfilterLookupByName
mock_define.side_effect = fakefilter.filterDefineXMLMock
instance_ref = self._create_instance()
self._create_security_group(instance_ref)
network_info = _fake_network_info(self, 2)
network_info[0]['network']['subnets'][0]['meta']['dhcp_server'] = \
'1.1.1.1'
self.fw.setup_basic_filtering(instance_ref, network_info)
def assert_filterref(instance, vif, expected=None):
expected = expected or []
nic_id = vif['address'].replace(':', '')
filter_name = self.fw._instance_filter_name(instance, nic_id)
f = fakefilter.nwfilterLookupByName(filter_name)
tree = etree.fromstring(f.xml)
frefs = [fr.get('filter') for fr in tree.findall('filterref')]
self.assertEqual(set(expected), set(frefs))
assert_filterref(instance_ref, network_info[0],
expected=['nova-base'])
assert_filterref(instance_ref, network_info[1],
expected=['nova-nodhcp'])
@mock.patch.object(firewall.LOG, 'debug')
def test_get_filter_uuid_unicode_exception_logging(self, debug):
with mock.patch.object(self.fw._conn, 'nwfilterLookupByName') as look:
look.side_effect = fakelibvirt.libvirtError(u"\U0001F4A9")
self.fw._get_filter_uuid('test')
self.assertEqual(2, debug.call_count)
self.assertEqual(u"Cannot find UUID for filter '%(name)s': '%(e)s'",
debug.call_args_list[0][0][0])
def test_define_filter_already_exists(self):
"""Tests that we ignore a libvirt error when the nw filter already
exists for a given name.
"""
error = fakelibvirt.libvirtError('already exists')
error.err = (fakelibvirt.VIR_ERR_OPERATION_FAILED, None,
"filter 'nova-no-nd-reflection' already exists with uuid "
"e740c5ec-c715-4f73-9874-630cc73d4ac2",)
with mock.patch.object(self.fw._conn, 'nwfilterDefineXML',
side_effect=error) as define:
self.fw._define_filter(mock.sentinel.xml)
define.assert_called_once_with(mock.sentinel.xml)
def test_define_filter_fails_wrong_message(self):
"""Tests that we reraise the libvirt error for an operational failure
if the error message is something unexpected.
"""
error = fakelibvirt.libvirtError('already exists')
error.err = (fakelibvirt.VIR_ERR_OPERATION_FAILED, None, 'oops',)
with mock.patch.object(self.fw._conn, 'nwfilterDefineXML',
side_effect=error) as define:
self.assertRaises(fakelibvirt.libvirtError,
self.fw._define_filter, mock.sentinel.xml)
define.assert_called_once_with(mock.sentinel.xml)
def test_define_filter_fails_wrong_code(self):
"""Tests that we reraise the libvirt error for an operational failure
if the error code is something unexpected.
"""
error = fakelibvirt.libvirtError('already exists')
error.err = (fakelibvirt.VIR_ERR_OPERATION_TIMEOUT, None, 'timeout',)
with mock.patch.object(self.fw._conn, 'nwfilterDefineXML',
side_effect=error) as define:
self.assertRaises(fakelibvirt.libvirtError,
self.fw._define_filter, mock.sentinel.xml)
define.assert_called_once_with(mock.sentinel.xml)

View File

@ -1,587 +0,0 @@
# 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
from nova import exception
from nova import objects
from nova import test
from nova.virt import firewall
_IPT_DRIVER_CLS = firewall.IptablesFirewallDriver
_FN_INSTANCE_RULES = 'instance_rules'
_FN_ADD_FILTERS = 'add_filters_for_instance'
_FN_DO_BASIC_RULES = '_do_basic_rules'
_FN_DO_DHCP_RULES = '_do_dhcp_rules'
class TestIptablesFirewallDriver(test.NoDBTestCase):
def setUp(self):
super(TestIptablesFirewallDriver, self).setUp()
self.driver = _IPT_DRIVER_CLS()
@mock.patch('nova.network.linux_net.iptables_manager')
def test_constructor(self, iptm_mock):
self.driver.__init__()
self.assertEqual({}, self.driver.instance_info)
self.assertFalse(self.driver.dhcp_create)
self.assertFalse(self.driver.dhcp_created)
self.assertEqual(iptm_mock, self.driver.iptables)
# NOTE(jaypipes): Here we are not testing the IptablesManager
# constructor. We are only testing the calls made against the
# IptablesManager singleton during initialization of the
# IptablesFirewallDriver.
expected = [
mock.call.add_chain('sg-fallback'),
mock.call.add_rule('sg-fallback', '-j DROP'),
]
iptm_mock.ipv4.__getitem__.return_value \
.assert_has_calls(expected)
iptm_mock.ipv6.__getitem__.return_value \
.assert_has_calls(expected)
def test_filter_defer_apply_on(self):
with mock.patch.object(self.driver.iptables,
'defer_apply_on') as dao_mock:
self.driver.filter_defer_apply_on()
dao_mock.assert_called_once_with()
def test_filter_defer_apply_off(self):
with mock.patch.object(self.driver.iptables,
'defer_apply_off') as dao_mock:
self.driver.filter_defer_apply_off()
dao_mock.assert_called_once_with()
@mock.patch.object(_IPT_DRIVER_CLS, 'remove_filters_for_instance')
def test_unfilter_instance_valid(self, rfii_mock):
with mock.patch.object(self.driver, 'instance_info') as ii_mock, \
mock.patch.object(self.driver, 'iptables') as ipt_mock:
fake_instance = objects.Instance(id=123)
ii_mock.pop.return_value = True
self.driver.unfilter_instance(fake_instance,
mock.sentinel.net_info)
ii_mock.pop.assert_called_once_with(fake_instance.id, None)
rfii_mock.assert_called_once_with(fake_instance)
ipt_mock.apply.assert_called_once_with()
@mock.patch.object(_IPT_DRIVER_CLS, 'remove_filters_for_instance')
def test_unfilter_instance_invalid(self, rfii_mock):
with mock.patch.object(self.driver, 'instance_info') as ii_mock, \
mock.patch.object(self.driver, 'iptables') as ipt_mock:
fake_instance = objects.Instance(id=123)
ii_mock.pop.return_value = False
self.driver.unfilter_instance(fake_instance,
mock.sentinel.net_info)
ii_mock.pop.assert_called_once_with(fake_instance.id, None)
self.assertFalse(rfii_mock.called)
self.assertFalse(ipt_mock.apply.called)
def setup_instance_filter(self, i_rules_mock):
# NOTE(chenli) The IptablesFirewallDriver init method calls the
# iptables manager, so we must reset here.
self.driver.iptables = mock.MagicMock()
i_mock = mock.MagicMock(spec=dict)
i_mock.id = 'fake_id'
i_rules_mock.return_value = (mock.sentinel.v4_rules,
mock.sentinel.v6_rules)
return i_mock
@mock.patch.object(_IPT_DRIVER_CLS, _FN_ADD_FILTERS)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_INSTANCE_RULES)
def test_prepare_instance_filter(self, i_rules_mock, add_filters_mock):
i_mock = self.setup_instance_filter(i_rules_mock)
self.driver.prepare_instance_filter(i_mock, mock.sentinel.net_info)
i_rules_mock.assert_called_once_with(i_mock, mock.sentinel.net_info)
add_filters_mock.assert_called_once_with(
i_mock, mock.sentinel.net_info,
mock.sentinel.v4_rules, mock.sentinel.v6_rules)
self.driver.iptables.apply.assert_called_once_with()
# When DHCP created flag is False, make sure we don't set any filters
gi_mock = self.driver.iptables.ipv4.__getitem__.return_value
self.assertFalse(gi_mock.called)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_ADD_FILTERS)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_INSTANCE_RULES)
def test_prepare_instance_filter_with_dhcp_create(self, i_rules_mock,
add_filters_mock):
i_mock = self.setup_instance_filter(i_rules_mock)
# add rules when DHCP create is set
self.driver.dhcp_create = True
self.driver.prepare_instance_filter(i_mock, mock.sentinel.net_info)
expected = [
mock.call.add_rule(
'INPUT',
'-s 0.0.0.0/32 -d 255.255.255.255/32 '
'-p udp -m udp --sport 68 --dport 67 -j ACCEPT'),
mock.call.add_rule(
'FORWARD',
'-s 0.0.0.0/32 -d 255.255.255.255/32 '
'-p udp -m udp --sport 68 --dport 67 -j ACCEPT')
]
self.driver.iptables.ipv4.__getitem__.return_value.assert_has_calls(
expected)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_ADD_FILTERS)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_INSTANCE_RULES)
def test_prepare_instance_filter_recreate(self, i_rules_mock,
add_filters_mock):
i_mock = self.setup_instance_filter(i_rules_mock)
# add rules when DHCP create is set and create the rule
self.driver.dhcp_create = True
self.driver.prepare_instance_filter(i_mock, mock.sentinel.net_info)
# Check we don't recreate the DHCP rules if we've already
# done so (there is a dhcp_created flag on the driver that is
# set when prepare_instance_filters() first creates them)
self.driver.iptables.ipv4.__getitem__.reset_mock()
self.driver.prepare_instance_filter(i_mock, mock.sentinel.net_info)
gi_mock = self.driver.iptables.ipv4.__getitem__.return_value
self.assertFalse(gi_mock.called)
def test_create_filter(self):
filter = self.driver._create_filter(['myip', 'otherip'], 'mychain')
self.assertEqual(filter, ['-d myip -j $mychain',
'-d otherip -j $mychain'])
def test_get_subnets(self):
subnet1 = {'version': '1', 'foo': 1}
subnet2 = {'version': '2', 'foo': 2}
subnet3 = {'version': '1', 'foo': 3}
network_info = [{'network': {'subnets': [subnet1, subnet2]}},
{'network': {'subnets': [subnet3]}}]
subnets = self.driver._get_subnets(network_info, '1')
self.assertEqual(subnets, [subnet1, subnet3])
def get_subnets_mock(self, network_info, version):
if version == 4:
return [{'ips': [{'address': '1.1.1.1'}, {'address': '2.2.2.2'}]}]
if version == 6:
return [{'ips': [{'address': '3.3.3.3'}]}]
def create_filter_mock(self, ips, chain_name):
if ips == ['1.1.1.1', '2.2.2.2']:
return 'rule1'
if ips == ['3.3.3.3']:
return 'rule2'
def test_filters_for_instance(self):
self.flags(use_ipv6=True)
chain_name = 'mychain'
network_info = {'foo': 'bar'}
self.driver._get_subnets = mock.Mock(side_effect=self.get_subnets_mock)
self.driver._create_filter = \
mock.Mock(side_effect=self.create_filter_mock)
ipv4_rules, ipv6_rules = \
self.driver._filters_for_instance(chain_name, network_info)
self.assertEqual(self.driver._get_subnets.mock_calls,
[mock.call(network_info, 4), mock.call(network_info, 6)])
self.assertEqual(self.driver._create_filter.mock_calls,
[mock.call(['1.1.1.1', '2.2.2.2'], chain_name),
mock.call(['3.3.3.3'], chain_name)])
self.assertEqual(ipv4_rules, 'rule1')
self.assertEqual(ipv6_rules, 'rule2')
def test_add_filters(self):
self.flags(use_ipv6=True)
self.driver.iptables.ipv4['filter'].add_rule = mock.Mock()
self.driver.iptables.ipv6['filter'].add_rule = mock.Mock()
chain_name = 'mychain'
ipv4_rules = ['rule1', 'rule2']
ipv6_rules = ['rule3', 'rule4']
self.driver._add_filters(chain_name, ipv4_rules, ipv6_rules)
self.assertEqual(self.driver.iptables.ipv4['filter'].add_rule.
mock_calls, [mock.call(chain_name, 'rule1'),
mock.call(chain_name, 'rule2')])
self.assertEqual(self.driver.iptables.ipv6['filter'].add_rule.
mock_calls, [mock.call(chain_name, 'rule3'),
mock.call(chain_name, 'rule4')])
@mock.patch.object(_IPT_DRIVER_CLS, '_instance_chain_name',
return_value=mock.sentinel.mychain)
@mock.patch.object(_IPT_DRIVER_CLS, '_filters_for_instance',
return_value=[mock.sentinel.ipv4_rules,
mock.sentinel.ipv6_rules])
@mock.patch.object(_IPT_DRIVER_CLS, '_add_filters')
def test_add_filters_for_instance(self, add_filters_mock,
ffi_mock, icn_mock):
self.flags(use_ipv6=True)
with mock.patch.object(self.driver.iptables.ipv6['filter'],
'add_chain') as ipv6_add_chain_mock, \
mock.patch.object(self.driver.iptables.ipv4['filter'],
'add_chain') as ipv4_add_chain_mock:
self.driver.add_filters_for_instance(
mock.sentinel.instance,
mock.sentinel.network_info,
mock.sentinel.inst_ipv4_rules,
mock.sentinel.inst_ipv6_rules)
ipv4_add_chain_mock.assert_called_with(mock.sentinel.mychain)
ipv6_add_chain_mock.assert_called_with(mock.sentinel.mychain)
icn_mock.assert_called_with(mock.sentinel.instance)
ffi_mock.assert_called_with(mock.sentinel.mychain,
mock.sentinel.network_info)
self.assertEqual([mock.call('local',
mock.sentinel.ipv4_rules,
mock.sentinel.ipv6_rules),
mock.call(mock.sentinel.mychain,
mock.sentinel.inst_ipv4_rules,
mock.sentinel.inst_ipv6_rules)],
add_filters_mock.mock_calls)
def test_remove_filters_for_instance(self):
self.flags(use_ipv6=True)
self.driver._instance_chain_name = \
mock.Mock(return_value='mychainname')
self.driver.iptables.ipv4['filter'].remove_chain = mock.Mock()
self.driver.iptables.ipv6['filter'].remove_chain = mock.Mock()
self.driver.remove_filters_for_instance('myinstance')
self.driver._instance_chain_name.assert_called_with('myinstance')
self.driver.iptables.ipv4['filter'].remove_chain.assert_called_with(
'mychainname')
self.driver.iptables.ipv6['filter'].remove_chain.assert_called_with(
'mychainname')
def test_instance_chain_name(self):
instance = mock.Mock()
instance.id = "myinstanceid"
instance_chain_name = self.driver._instance_chain_name(instance)
self.assertEqual(instance_chain_name, 'inst-myinstanceid')
def test_do_basic_rules(self):
ipv4_rules = ['rule1']
ipv6_rules = ['rule2']
self.driver._do_basic_rules(ipv4_rules, ipv6_rules,
mock.sentinel.net_info)
self.assertEqual(ipv4_rules,
['rule1', '-m state --state INVALID -j DROP',
'-m state --state ESTABLISHED,RELATED -j ACCEPT'])
self.assertEqual(ipv6_rules,
['rule2', '-m state --state INVALID -j DROP',
'-m state --state ESTABLISHED,RELATED -j ACCEPT'])
def test_do_dhcp_rules(self):
subnet1 = mock.Mock()
subnet1.get_meta = mock.Mock(return_value='mydhcp')
subnet2 = mock.Mock()
subnet2.get_meta = mock.Mock(return_value=None)
self.driver._get_subnets = mock.Mock(return_value=[subnet1, subnet2])
ipv4_rules = ['rule1']
self.driver._do_dhcp_rules(ipv4_rules, mock.sentinel.net_info)
self.assertEqual(ipv4_rules,
['rule1',
'-s mydhcp -p udp --sport 67 --dport 68 -j ACCEPT'])
def test_do_project_network_rules(self):
self.flags(use_ipv6=True)
subnet1 = {'cidr': 'mycidr1'}
subnet2 = {'cidr': 'mycidr2'}
ipv4_rules = ['rule1']
ipv6_rules = ['rule2']
self.driver._get_subnets = mock.Mock(return_value=[subnet1, subnet2])
self.driver._do_project_network_rules(ipv4_rules, ipv6_rules,
mock.sentinel.net_info)
self.assertEqual(ipv4_rules,
['rule1',
'-s mycidr1 -j ACCEPT', '-s mycidr2 -j ACCEPT'])
self.assertEqual(ipv6_rules,
['rule2',
'-s mycidr1 -j ACCEPT', '-s mycidr2 -j ACCEPT'])
def test_do_ra_rules(self):
subnet1 = {'gateway': {'address': 'myaddress1'}}
subnet2 = {'gateway': {'address': 'myaddress2'}}
self.driver._get_subnets = \
mock.Mock(return_value=[subnet1, subnet2])
ipv6_rules = ['rule1']
self.driver._do_ra_rules(ipv6_rules, mock.sentinel.net_info)
self.assertEqual(ipv6_rules, ['rule1',
'-s myaddress1/128 -p icmpv6 -j ACCEPT',
'-s myaddress2/128 -p icmpv6 -j ACCEPT'])
def test_build_icmp_rule(self):
rule = mock.Mock()
# invalid icmp type
rule.from_port = -1
icmp_rule = self.driver._build_icmp_rule(rule, 4)
self.assertEqual(icmp_rule, [])
# version 4 invalid icmp code
rule.from_port = 123
rule.to_port = -1
icmp_rule = self.driver._build_icmp_rule(rule, 4)
self.assertEqual(icmp_rule,
['-m', 'icmp', '--icmp-type', '123'])
# version 6 valid icmp code
rule.from_port = 123
rule.to_port = 456
icmp_rule = self.driver._build_icmp_rule(rule, 6)
self.assertEqual(icmp_rule,
['-m', 'icmp6', '--icmpv6-type', '123/456'])
def test_build_tcp_udp_rule(self):
rule = mock.Mock()
# equal from and to port
rule.from_port = 123
rule.to_port = 123
tu_rule = self.driver._build_tcp_udp_rule(rule, 42)
self.assertEqual(tu_rule, ['--dport', '123'])
# different from and to port
rule.to_port = 456
tu_rule = self.driver._build_tcp_udp_rule(rule, 42)
self.assertEqual(tu_rule, ['-m', 'multiport', '--dports', '123:456'])
def setup_instance_rules(self, ins_obj_cls_mock):
"""Create necessary mock varibles for instance_rules.
The i_mock and ni_mock represent instance_rules parameters
instance and network_info.
The i_obj_mock represent the return vaue for nova.objects.Instance.
"""
i_mock = mock.MagicMock(spec=dict)
ni_mock = mock.MagicMock(spec=dict)
i_obj_mock = mock.MagicMock()
ins_obj_cls_mock._from_db_object.return_value = i_obj_mock
driver = firewall.IptablesFirewallDriver()
return i_mock, ni_mock, i_obj_mock, driver
@mock.patch('nova.objects.SecurityGroupRuleList')
@mock.patch.object(_IPT_DRIVER_CLS, _FN_DO_DHCP_RULES)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_DO_BASIC_RULES)
@mock.patch('nova.objects.Instance')
@mock.patch('nova.context.get_admin_context',
return_value=mock.sentinel.ctx)
@mock.patch('nova.network.linux_net.iptables_manager')
def test_instance_rules_no_secgroups(self, _iptm_mock, ctx_mock,
ins_obj_cls_mock, _do_basic_mock, _do_dhcp_mock,
sec_grp_list_mock):
i_mock, ni_mock, i_obj_mock, driver = self.setup_instance_rules(
ins_obj_cls_mock)
# Simple unit test that verifies that the fallback jump
# is the only rule added to the returned list of rules if
# no secgroups are found (we ignore the basic and DHCP
# rule additions here)
sec_grp_list_mock.get_by_instance.return_value = []
v4_rules, v6_rules = driver.instance_rules(i_mock, ni_mock)
ins_obj_cls_mock._from_db_object.assert_called_once_with(
mock.sentinel.ctx, mock.ANY, i_mock, mock.ANY)
sec_grp_list_mock.get_by_instance.assert_called_once_with(
mock.sentinel.ctx, i_obj_mock)
expected = ['-j $sg-fallback']
self.assertEqual(expected, v4_rules)
self.assertEqual(expected, v6_rules)
@mock.patch('nova.objects.SecurityGroupRuleList')
@mock.patch('nova.objects.SecurityGroupList')
@mock.patch.object(_IPT_DRIVER_CLS, _FN_DO_DHCP_RULES)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_DO_BASIC_RULES)
@mock.patch('nova.objects.Instance')
@mock.patch('nova.context.get_admin_context',
return_value=mock.sentinel.ctx)
@mock.patch('nova.network.linux_net.iptables_manager')
def test_instance_rules_cidr(self, _iptm_mock, ctx_mock,
ins_obj_cls_mock, _do_basic_mock, _do_dhcp_mock,
sec_grp_list_mock, sec_grp_rule_list_mock):
i_mock, ni_mock, i_obj_mock, driver = self.setup_instance_rules(
ins_obj_cls_mock)
# Tests that sec group rules that contain a CIDR (i.e. the
# rule does not contain a grantee group of instances) populates
# the returned iptables rules with appropriate ingress and
# egress filters.
sec_grp_list_mock.get_by_instance.return_value = [
mock.sentinel.sec_grp
]
sec_grp_rule_list_mock.get_by_security_group.return_value = [
{
"cidr": "192.168.1.0/24",
"protocol": "tcp",
"to_port": "22",
"from_port": "22"
}
]
v4_rules, v6_rules = driver.instance_rules(i_mock, ni_mock)
expected = [
# '-j ACCEPT -p tcp --dport 22 -s 192.168.1.0/24',
'-j $sg-fallback'
]
self.assertEqual(expected, v4_rules)
expected = ['-j $sg-fallback']
self.assertEqual(expected, v6_rules)
def setup_grantee_group(
self, ins_obj_cls_mock, sec_grp_list_mock, sec_grp_rule_list_mock,
ins_list_mock):
i_mock, ni_mock, i_obj_mock, driver = self.setup_instance_rules(
ins_obj_cls_mock)
# Tests that sec group rules that DO NOT contain a CIDR (i.e. the
# rule contains a grantee group of instances) populates
# the returned iptables rules with appropriate ingress and
# egress filters after calling out to the network API for information
# about the instances in the grantee group.
sec_grp_list_mock.get_by_instance.return_value = [
mock.sentinel.sec_grp
]
sec_grp_rule_list_mock.get_by_security_group.return_value = [
{
"cidr": None,
"grantee_group": mock.sentinel.gg,
"protocol": "tcp",
"to_port": "22",
"from_port": "22"
}
]
i_obj_list_mock = mock.MagicMock()
i_obj_list_mock.info_cache.return_value = {
"deleted": False
}
ins_list_mock.get_by_security_group.return_value = [i_obj_list_mock]
return i_mock, i_obj_mock, ni_mock, driver
@mock.patch('nova.objects.Instance.get_network_info')
@mock.patch('nova.objects.InstanceList')
@mock.patch('nova.objects.SecurityGroupRuleList')
@mock.patch('nova.objects.SecurityGroupList')
@mock.patch.object(_IPT_DRIVER_CLS, _FN_DO_DHCP_RULES)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_DO_BASIC_RULES)
@mock.patch('nova.objects.Instance')
@mock.patch('nova.context.get_admin_context',
return_value=mock.sentinel.ctx)
@mock.patch('nova.network.linux_net.iptables_manager')
def test_instance_rules_grantee_group(self, _iptm_mock, ctx_mock,
ins_obj_cls_mock, _do_basic_mock, _do_dhcp_mock,
sec_grp_list_mock, sec_grp_rule_list_mock, ins_list_mock,
get_nw_info_mock):
i_mock, i_obj_mock, ni_mock, driver = self.setup_grantee_group(
ins_obj_cls_mock, sec_grp_list_mock, sec_grp_rule_list_mock,
ins_list_mock)
nw_info_mock = mock.MagicMock()
nw_info_mock.fixed_ips.return_value = [
{
"address": "10.0.1.4",
"version": 4
}
]
get_nw_info_mock.return_value = nw_info_mock
v4_rules, v6_rules = driver.instance_rules(i_mock, ni_mock)
expected = ['-j $sg-fallback']
self.assertEqual(expected, v4_rules)
self.assertEqual(expected, v6_rules)
@mock.patch('nova.objects.Instance.get_network_info')
@mock.patch('nova.objects.InstanceList')
@mock.patch('nova.objects.SecurityGroupRuleList')
@mock.patch('nova.objects.SecurityGroupList')
@mock.patch.object(_IPT_DRIVER_CLS, _FN_DO_DHCP_RULES)
@mock.patch.object(_IPT_DRIVER_CLS, _FN_DO_BASIC_RULES)
@mock.patch('nova.objects.Instance')
@mock.patch('nova.context.get_admin_context',
return_value=mock.sentinel.ctx)
@mock.patch('nova.network.linux_net.iptables_manager')
def test_instance_rules_grantee_group_instance_deleted(
self, _iptm_mock, ctx_mock, ins_obj_cls_mock, _do_basic_mock,
_do_dhcp_mock, sec_grp_list_mock, sec_grp_rule_list_mock,
ins_list_mock, get_nw_info_mock):
i_mock, i_obj_mock, ni_mock, driver = self.setup_grantee_group(
ins_obj_cls_mock, sec_grp_list_mock, sec_grp_rule_list_mock,
ins_list_mock)
# Emulate one of the instances in the grantee group being deleted
# in between when the spawn of this instance and when we set up
# network for that instance, and ensure that we do not crash and
# burn but just skip the deleted instance from the iptables filters
get_nw_info_mock.side_effect = exception.InstanceNotFound(
instance_id="_ignored")
v4_rules, v6_rules = driver.instance_rules(i_mock, ni_mock)
expected = ['-j $sg-fallback']
self.assertEqual(expected, v4_rules)
self.assertEqual(expected, v6_rules)
def test_refresh_security_group_rules(self):
self.driver.do_refresh_security_group_rules = mock.Mock()
self.driver.iptables.apply = mock.Mock()
self.driver.refresh_security_group_rules('mysecgroup')
self.driver.do_refresh_security_group_rules \
.assert_called_with('mysecgroup')
self.driver.iptables.apply.assert_called()
def test_refresh_instance_security_rules(self):
self.driver.do_refresh_instance_rules = mock.Mock()
self.driver.iptables.apply = mock.Mock()
self.driver.refresh_instance_security_rules('myinstance')
self.driver.do_refresh_instance_rules.assert_called_with('myinstance')
self.driver.iptables.apply.assert_called()
def test_do_refresh_security_group_rules(self):
self.driver.instance_info = \
{'1': ['myinstance1', 'netinfo1'],
'2': ['myinstance2', 'netinfo2']}
self.driver.instance_rules = \
mock.Mock(return_value=['myipv4rules', 'myipv6rules'])
self.driver._inner_do_refresh_rules = mock.Mock()
self.driver.do_refresh_security_group_rules('mysecgroup')
self.driver.instance_rules.assert_any_call('myinstance1', 'netinfo1')
self.driver.instance_rules.assert_any_call('myinstance2', 'netinfo2')
self.driver._inner_do_refresh_rules.assert_any_call(
'myinstance1', 'netinfo1',
'myipv4rules', 'myipv6rules')
self.driver._inner_do_refresh_rules.assert_any_call(
'myinstance2', 'netinfo2',
'myipv4rules', 'myipv6rules')
def test_do_refresh_instance_rules(self):
instance = mock.Mock()
instance.id = 'myid'
self.driver.instance_info = {instance.id: ['myinstance', 'mynetinfo']}
self.driver.instance_rules = \
mock.Mock(return_value=['myipv4rules', 'myipv6rules'])
self.driver._inner_do_refresh_rules = mock.Mock()
self.driver.do_refresh_instance_rules(instance)
self.driver.instance_rules.assert_called_with(instance, 'mynetinfo')
self.driver._inner_do_refresh_rules.assert_called_with(
instance, 'mynetinfo', 'myipv4rules', 'myipv6rules')

View File

@ -1,429 +0,0 @@
# Copyright 2011 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
# Copyright (c) 2011 Citrix Systems, Inc.
#
# 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 oslo_log import log as logging
from oslo_utils import importutils
import nova.conf
from nova import context
from nova.network import linux_net
from nova import objects
from nova import utils
from nova.virt import netutils
LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF
def load_driver(default, *args, **kwargs):
fw_class = importutils.import_class(CONF.firewall_driver or default)
return fw_class(*args, **kwargs)
class FirewallDriver(object):
"""Firewall Driver base class.
Defines methods that any driver providing security groups should implement.
"""
def prepare_instance_filter(self, instance, network_info):
"""Prepare filters for the instance.
At this point, the instance isn't running yet.
"""
raise NotImplementedError()
def filter_defer_apply_on(self):
"""Defer application of IPTables rules."""
pass
def filter_defer_apply_off(self):
"""Turn off deferral of IPTables rules and apply the rules now."""
pass
def unfilter_instance(self, instance, network_info):
"""Stop filtering instance."""
raise NotImplementedError()
def apply_instance_filter(self, instance, network_info):
"""Apply instance filter.
Once this method returns, the instance should be firewalled
appropriately. This method should as far as possible be a
no-op. It's vastly preferred to get everything set up in
prepare_instance_filter.
"""
raise NotImplementedError()
def refresh_security_group_rules(self, security_group_id):
"""Refresh security group rules from data store
Gets called when a rule has been added to or removed from
the security group.
"""
raise NotImplementedError()
def refresh_instance_security_rules(self, instance):
"""Refresh security group rules from data store
Gets called when an instance gets added to or removed from
the security group the instance is a member of or if the
group gains or loses a rule.
"""
raise NotImplementedError()
def setup_basic_filtering(self, instance, network_info):
"""Create rules to block spoofing and allow dhcp.
This gets called when spawning an instance, before
:py:meth:`prepare_instance_filter`.
"""
raise NotImplementedError()
def instance_filter_exists(self, instance, network_info):
"""Check nova-instance-instance-xxx exists."""
raise NotImplementedError()
class IptablesFirewallDriver(FirewallDriver):
"""Driver which enforces security groups through iptables rules."""
def __init__(self, **kwargs):
self.iptables = linux_net.iptables_manager
self.instance_info = {}
# Flags for DHCP request rule
self.dhcp_create = False
self.dhcp_created = False
self.iptables.ipv4['filter'].add_chain('sg-fallback')
self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP')
self.iptables.ipv6['filter'].add_chain('sg-fallback')
self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP')
def setup_basic_filtering(self, instance, network_info):
pass
def apply_instance_filter(self, instance, network_info):
"""No-op. Everything is done in prepare_instance_filter."""
pass
def filter_defer_apply_on(self):
self.iptables.defer_apply_on()
def filter_defer_apply_off(self):
self.iptables.defer_apply_off()
def unfilter_instance(self, instance, network_info):
if self.instance_info.pop(instance.id, None):
self.remove_filters_for_instance(instance)
self.iptables.apply()
else:
LOG.info('Attempted to unfilter instance which is not filtered',
instance=instance)
def prepare_instance_filter(self, instance, network_info):
self.instance_info[instance.id] = (instance, network_info)
ipv4_rules, ipv6_rules = self.instance_rules(instance, network_info)
self.add_filters_for_instance(instance, network_info, ipv4_rules,
ipv6_rules)
LOG.debug('Filters added to instance: %s', instance.id,
instance=instance)
# Ensure that DHCP request rule is updated if necessary
if (self.dhcp_create and not self.dhcp_created):
self.iptables.ipv4['filter'].add_rule(
'INPUT',
'-s 0.0.0.0/32 -d 255.255.255.255/32 '
'-p udp -m udp --sport 68 --dport 67 -j ACCEPT')
self.iptables.ipv4['filter'].add_rule(
'FORWARD',
'-s 0.0.0.0/32 -d 255.255.255.255/32 '
'-p udp -m udp --sport 68 --dport 67 -j ACCEPT')
self.dhcp_created = True
self.iptables.apply()
def _create_filter(self, ips, chain_name):
return ['-d %s -j $%s' % (ip, chain_name) for ip in ips]
def _get_subnets(self, network_info, version):
subnets = []
for vif in network_info:
if 'network' in vif and 'subnets' in vif['network']:
for subnet in vif['network']['subnets']:
if subnet['version'] == version:
subnets.append(subnet)
return subnets
def _filters_for_instance(self, chain_name, network_info):
"""Creates a rule corresponding to each ip that defines a
jump to the corresponding instance - chain for all the traffic
destined to that ip.
"""
v4_subnets = self._get_subnets(network_info, 4)
v6_subnets = self._get_subnets(network_info, 6)
ips_v4 = [ip['address'] for subnet in v4_subnets
for ip in subnet['ips']]
ipv4_rules = self._create_filter(ips_v4, chain_name)
ipv6_rules = ips_v6 = []
if CONF.use_ipv6:
if v6_subnets:
ips_v6 = [ip['address'] for subnet in v6_subnets
for ip in subnet['ips']]
ipv6_rules = self._create_filter(ips_v6, chain_name)
return ipv4_rules, ipv6_rules
def _add_filters(self, chain_name, ipv4_rules, ipv6_rules):
for rule in ipv4_rules:
self.iptables.ipv4['filter'].add_rule(chain_name, rule)
if CONF.use_ipv6:
for rule in ipv6_rules:
self.iptables.ipv6['filter'].add_rule(chain_name, rule)
def add_filters_for_instance(self, instance, network_info, inst_ipv4_rules,
inst_ipv6_rules):
chain_name = self._instance_chain_name(instance)
if CONF.use_ipv6:
self.iptables.ipv6['filter'].add_chain(chain_name)
self.iptables.ipv4['filter'].add_chain(chain_name)
ipv4_rules, ipv6_rules = self._filters_for_instance(chain_name,
network_info)
self._add_filters('local', ipv4_rules, ipv6_rules)
self._add_filters(chain_name, inst_ipv4_rules, inst_ipv6_rules)
def remove_filters_for_instance(self, instance):
chain_name = self._instance_chain_name(instance)
self.iptables.ipv4['filter'].remove_chain(chain_name)
if CONF.use_ipv6:
self.iptables.ipv6['filter'].remove_chain(chain_name)
def _instance_chain_name(self, instance):
return 'inst-%s' % (instance.id,)
def _do_basic_rules(self, ipv4_rules, ipv6_rules, network_info):
# Always drop invalid packets
ipv4_rules += ['-m state --state ' 'INVALID -j DROP']
ipv6_rules += ['-m state --state ' 'INVALID -j DROP']
# Allow established connections
ipv4_rules += ['-m state --state ESTABLISHED,RELATED -j ACCEPT']
ipv6_rules += ['-m state --state ESTABLISHED,RELATED -j ACCEPT']
def _do_dhcp_rules(self, ipv4_rules, network_info):
v4_subnets = self._get_subnets(network_info, 4)
dhcp_servers = [subnet.get_meta('dhcp_server')
for subnet in v4_subnets if subnet.get_meta('dhcp_server')]
for dhcp_server in dhcp_servers:
if dhcp_server:
ipv4_rules.append('-s %s -p udp --sport 67 --dport 68 '
'-j ACCEPT' % (dhcp_server,))
self.dhcp_create = True
def _do_project_network_rules(self, ipv4_rules, ipv6_rules, network_info):
v4_subnets = self._get_subnets(network_info, 4)
v6_subnets = self._get_subnets(network_info, 6)
cidrs = [subnet['cidr'] for subnet in v4_subnets]
for cidr in cidrs:
ipv4_rules.append('-s %s -j ACCEPT' % (cidr,))
if CONF.use_ipv6:
cidrv6s = [subnet['cidr'] for subnet in v6_subnets]
for cidrv6 in cidrv6s:
ipv6_rules.append('-s %s -j ACCEPT' % (cidrv6,))
def _do_ra_rules(self, ipv6_rules, network_info):
v6_subnets = self._get_subnets(network_info, 6)
gateways_v6 = [subnet['gateway']['address'] for subnet in v6_subnets]
for gateway_v6 in gateways_v6:
ipv6_rules.append(
'-s %s/128 -p icmpv6 -j ACCEPT' % (gateway_v6,))
def _build_icmp_rule(self, rule, version):
icmp_type = rule.from_port
icmp_code = rule.to_port
if icmp_type == -1:
icmp_type_arg = None
else:
icmp_type_arg = '%s' % icmp_type
if not icmp_code == -1:
icmp_type_arg += '/%s' % icmp_code
if icmp_type_arg:
if version == 4:
return ['-m', 'icmp', '--icmp-type', icmp_type_arg]
elif version == 6:
return ['-m', 'icmp6', '--icmpv6-type', icmp_type_arg]
# return empty list if icmp_type == -1
return []
def _build_tcp_udp_rule(self, rule, version):
if rule.from_port == rule.to_port:
return ['--dport', '%s' % (rule.from_port,)]
else:
return ['-m', 'multiport',
'--dports', '%s:%s' % (rule.from_port,
rule.to_port)]
def instance_rules(self, instance, network_info):
ctxt = context.get_admin_context()
if isinstance(instance, dict):
# NOTE(danms): allow old-world instance objects from
# unconverted callers; all we need is instance.uuid below
instance = objects.Instance._from_db_object(
ctxt, objects.Instance(), instance, [])
ipv4_rules = []
ipv6_rules = []
# Initialize with basic rules
self._do_basic_rules(ipv4_rules, ipv6_rules, network_info)
# Set up rules to allow traffic to/from DHCP server
self._do_dhcp_rules(ipv4_rules, network_info)
# Allow project network traffic
if CONF.allow_same_net_traffic:
self._do_project_network_rules(ipv4_rules, ipv6_rules,
network_info)
# We wrap these in CONF.use_ipv6 because they might cause
# a DB lookup. The other ones are just list operations, so
# they're not worth the clutter.
if CONF.use_ipv6:
# Allow RA responses
self._do_ra_rules(ipv6_rules, network_info)
# then, security group chains and rules
rules = objects.SecurityGroupRuleList.get_by_instance(ctxt, instance)
for rule in rules:
if not rule.cidr:
version = 4
else:
version = netutils.get_ip_version(rule.cidr)
if version == 4:
fw_rules = ipv4_rules
else:
fw_rules = ipv6_rules
protocol = rule.protocol
if protocol:
protocol = rule.protocol.lower()
if version == 6 and protocol == 'icmp':
protocol = 'icmpv6'
args = ['-j ACCEPT']
if protocol:
args += ['-p', protocol]
if protocol in ['udp', 'tcp']:
args += self._build_tcp_udp_rule(rule, version)
elif protocol == 'icmp':
args += self._build_icmp_rule(rule, version)
if rule.cidr:
args += ['-s', str(rule.cidr)]
fw_rules += [' '.join(args)]
else:
if rule.grantee_group:
insts = objects.InstanceList.get_by_security_group(
ctxt, rule.grantee_group)
for inst in insts:
if inst.info_cache.deleted:
LOG.debug('ignoring deleted cache')
continue
nw_info = inst.get_network_info()
ips = [ip['address'] for ip in nw_info.fixed_ips()
if ip['version'] == version]
LOG.debug('ips: %r', ips, instance=inst)
for ip in ips:
subrule = args + ['-s %s' % ip]
fw_rules += [' '.join(subrule)]
ipv4_rules += ['-j $sg-fallback']
ipv6_rules += ['-j $sg-fallback']
LOG.debug('Security Group Rules %s translated to ipv4: %r, ipv6: %r',
list(rules), ipv4_rules, ipv6_rules,
instance=instance)
return ipv4_rules, ipv6_rules
def instance_filter_exists(self, instance, network_info):
pass
def refresh_security_group_rules(self, security_group):
self.do_refresh_security_group_rules(security_group)
self.iptables.apply()
def refresh_instance_security_rules(self, instance):
self.do_refresh_instance_rules(instance)
self.iptables.apply()
@utils.synchronized('iptables', external=True)
def _inner_do_refresh_rules(self, instance, network_info, ipv4_rules,
ipv6_rules):
chain_name = self._instance_chain_name(instance)
if not self.iptables.ipv4['filter'].has_chain(chain_name):
LOG.info('instance chain %s disappeared during refresh, skipping',
chain_name, instance=instance)
return
self.remove_filters_for_instance(instance)
self.add_filters_for_instance(instance, network_info, ipv4_rules,
ipv6_rules)
def do_refresh_security_group_rules(self, security_group):
id_list = self.instance_info.keys()
for instance_id in id_list:
try:
instance, network_info = self.instance_info[instance_id]
except KeyError:
# NOTE(danms): instance cache must have been modified,
# ignore this deleted instance and move on
continue
ipv4_rules, ipv6_rules = self.instance_rules(instance,
network_info)
self._inner_do_refresh_rules(instance, network_info, ipv4_rules,
ipv6_rules)
def do_refresh_instance_rules(self, instance):
_instance, network_info = self.instance_info[instance.id]
ipv4_rules, ipv6_rules = self.instance_rules(instance, network_info)
self._inner_do_refresh_rules(instance, network_info, ipv4_rules,
ipv6_rules)
class NoopFirewallDriver(object):
"""Firewall driver which just provides No-op methods."""
def __init__(self, *args, **kwargs):
pass
def _noop(self, *args, **kwargs):
pass
def __getattr__(self, key):
return self._noop
def instance_filter_exists(self, instance, network_info):
return True

View File

@ -1,353 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
# Copyright (c) 2010 Citrix Systems, Inc.
#
# 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 eventlet import greenthread
from lxml import etree
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import importutils
from oslo_utils import uuidutils
import nova.conf
import nova.virt.firewall as base_firewall
from nova.virt import netutils
LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF
libvirt = None
class NWFilterFirewall(base_firewall.FirewallDriver):
"""This class implements a network filtering mechanism by using
libvirt's nwfilter.
all instances get a filter ("nova-base") applied. This filter
provides some basic security such as protection against MAC
spoofing, IP spoofing, and ARP spoofing.
"""
def __init__(self, host, **kwargs):
"""Create an NWFilter firewall driver
:param host: nova.virt.libvirt.host.Host instance
:param kwargs: currently unused
"""
global libvirt
if libvirt is None:
try:
libvirt = importutils.import_module('libvirt')
except ImportError:
LOG.warning("Libvirt module could not be loaded. "
"NWFilterFirewall will not work correctly.")
self._host = host
self.static_filters_configured = False
def apply_instance_filter(self, instance, network_info):
"""No-op. Everything is done in prepare_instance_filter."""
pass
def _get_connection(self):
return self._host.get_connection()
_conn = property(_get_connection)
def nova_no_nd_reflection_filter(self):
"""This filter protects false positives on IPv6 Duplicate Address
Detection(DAD).
"""
uuid = self._get_filter_uuid('nova-no-nd-reflection')
return '''<filter name='nova-no-nd-reflection' chain='ipv6'>
<!-- no nd reflection -->
<!-- drop if destination mac is v6 mcast mac addr and
we sent it. -->
<uuid>%s</uuid>
<rule action='drop' direction='in'>
<mac dstmacaddr='33:33:00:00:00:00'
dstmacmask='ff:ff:00:00:00:00' srcmacaddr='$MAC'/>
</rule>
</filter>''' % uuid
def nova_dhcp_filter(self):
"""The standard allow-dhcp-server filter is an <ip> one, so it uses
ebtables to allow traffic through. Without a corresponding rule in
iptables, it'll get blocked anyway.
"""
uuid = self._get_filter_uuid('nova-allow-dhcp-server')
return '''<filter name='nova-allow-dhcp-server' chain='ipv4'>
<uuid>%s</uuid>
<rule action='accept' direction='out'
priority='100'>
<udp srcipaddr='0.0.0.0'
dstipaddr='255.255.255.255'
srcportstart='68'
dstportstart='67'/>
</rule>
<rule action='accept' direction='in'
priority='100'>
<udp srcipaddr='$DHCPSERVER'
srcportstart='67'
dstportstart='68'/>
</rule>
</filter>''' % uuid
def setup_basic_filtering(self, instance, network_info):
"""Set up basic filtering (MAC, IP, and ARP spoofing protection)."""
LOG.info('Called setup_basic_filtering in nwfilter',
instance=instance)
LOG.info('Ensuring static filters', instance=instance)
self._ensure_static_filters()
nodhcp_base_filter = self.get_base_filter_list(instance, False)
dhcp_base_filter = self.get_base_filter_list(instance, True)
for vif in network_info:
_base_filter = nodhcp_base_filter
for subnet in vif['network']['subnets']:
if subnet.get_meta('dhcp_server'):
_base_filter = dhcp_base_filter
break
self._define_filter(self._get_instance_filter_xml(instance,
_base_filter,
vif))
def _get_instance_filter_parameters(self, vif):
parameters = []
def format_parameter(parameter, value):
return ("<parameter name='%s' value='%s'/>" % (parameter, value))
network = vif['network']
if not vif['network'] or not vif['network']['subnets']:
return parameters
v4_subnets = [s for s in network['subnets'] if s['version'] == 4]
v6_subnets = [s for s in network['subnets'] if s['version'] == 6]
for subnet in v4_subnets:
for ip in subnet['ips']:
parameters.append(format_parameter('IP', ip['address']))
dhcp_server = subnet.get_meta('dhcp_server')
if dhcp_server:
parameters.append(format_parameter('DHCPSERVER', dhcp_server))
ipv4_cidr = subnet['cidr']
net, mask = netutils.get_net_and_mask(ipv4_cidr)
parameters.append(format_parameter('PROJNET', net))
parameters.append(format_parameter('PROJMASK', mask))
for subnet in v6_subnets:
gateway = subnet.get('gateway')
if gateway:
ra_server = gateway['address'] + "/128"
parameters.append(format_parameter('RASERVER', ra_server))
ipv6_cidr = subnet['cidr']
net, prefix = netutils.get_net_and_prefixlen(ipv6_cidr)
parameters.append(format_parameter('PROJNET6', net))
parameters.append(format_parameter('PROJMASK6', prefix))
return parameters
def _get_instance_filter_xml(self, instance, filters, vif):
nic_id = vif['address'].replace(':', '')
instance_filter_name = self._instance_filter_name(instance, nic_id)
parameters = self._get_instance_filter_parameters(vif)
uuid = self._get_filter_uuid(instance_filter_name)
xml = '''<filter name='%s' chain='root'>''' % instance_filter_name
xml += '<uuid>%s</uuid>' % uuid
for f in filters:
xml += '''<filterref filter='%s'>''' % f
xml += ''.join(parameters)
xml += '</filterref>'
xml += '</filter>'
return xml
def get_base_filter_list(self, instance, allow_dhcp):
"""Obtain a list of base filters to apply to an instance.
The return value should be a list of strings, each
specifying a filter name. Subclasses can override this
function to add additional filters as needed. Additional
filters added to the list must also be correctly defined
within the subclass.
"""
if allow_dhcp:
base_filter = 'nova-base'
else:
base_filter = 'nova-nodhcp'
return [base_filter]
def _ensure_static_filters(self):
"""Static filters are filters that have no need to be IP aware.
There is no configuration or tuneability of these filters, so they
can be set up once and forgotten about.
"""
if self.static_filters_configured:
return
filter_set = ['no-mac-spoofing',
'no-ip-spoofing',
'no-arp-spoofing']
self._define_filter(self.nova_no_nd_reflection_filter())
filter_set.append('nova-no-nd-reflection')
self._define_filter(self._filter_container('nova-nodhcp', filter_set))
filter_set.append('allow-dhcp-server')
self._define_filter(self._filter_container('nova-base', filter_set))
self._define_filter(self.nova_dhcp_filter())
self.static_filters_configured = True
def _filter_container(self, name, filters):
uuid = self._get_filter_uuid(name)
xml = '''<filter name='%s' chain='root'>
<uuid>%s</uuid>
%s
</filter>''' % (name, uuid,
''.join(["<filterref filter='%s'/>" % (f,) for f in filters]))
return xml
def _get_filter_uuid(self, name):
try:
flt = self._conn.nwfilterLookupByName(name)
xml = flt.XMLDesc(0)
doc = etree.fromstring(xml)
u = doc.find("./uuid").text
except Exception as e:
LOG.debug(u"Cannot find UUID for filter '%(name)s': '%(e)s'",
{'name': name, 'e': e})
u = uuidutils.generate_uuid(dashed=False)
LOG.debug("UUID for filter '%s' is '%s'", name, u)
return u
def _define_filter(self, xml):
if callable(xml):
xml = xml()
try:
self._conn.nwfilterDefineXML(xml)
except libvirt.libvirtError as ex:
with excutils.save_and_reraise_exception() as ctxt:
errcode = ex.get_error_code()
if errcode == libvirt.VIR_ERR_OPERATION_FAILED:
# Since libvirt 1.2.7 this operation can fail if the filter
# with the same name already exists for the given uuid.
# Unfortunately there is not a specific error code for this
# so we have to parse the error message to see if that was
# the failure.
errmsg = ex.get_error_message()
if 'already exists with uuid' in errmsg:
ctxt.reraise = False
def unfilter_instance(self, instance, network_info):
"""Clear out the nwfilter rules."""
for vif in network_info:
nic_id = vif['address'].replace(':', '')
instance_filter_name = self._instance_filter_name(instance, nic_id)
# nwfilters may be defined in a separate thread in the case
# of libvirt non-blocking mode, so we wait for completion
max_retry = CONF.live_migration_retry_count
for cnt in range(max_retry):
try:
_nw = self._conn.nwfilterLookupByName(instance_filter_name)
_nw.undefine()
break
except libvirt.libvirtError as e:
if cnt == max_retry - 1:
raise
errcode = e.get_error_code()
if errcode == libvirt.VIR_ERR_OPERATION_INVALID:
# This happens when the instance filter is still in use
# (ie. when the instance has not terminated properly)
LOG.info('Failed to undefine network filter '
'%(name)s. Try %(cnt)d of %(max_retry)d.',
{'name': instance_filter_name,
'cnt': cnt + 1,
'max_retry': max_retry},
instance=instance)
greenthread.sleep(1)
else:
LOG.debug('The nwfilter(%s) is not found.',
instance_filter_name, instance=instance)
break
@staticmethod
def _instance_filter_name(instance, nic_id=None):
if not nic_id:
return 'nova-instance-%s' % (instance.name)
return 'nova-instance-%s-%s' % (instance.name, nic_id)
def instance_filter_exists(self, instance, network_info):
"""Check nova-instance-instance-xxx exists."""
for vif in network_info:
nic_id = vif['address'].replace(':', '')
instance_filter_name = self._instance_filter_name(instance, nic_id)
try:
self._conn.nwfilterLookupByName(instance_filter_name)
except libvirt.libvirtError:
name = instance.name
LOG.debug('The nwfilter(%(instance_filter_name)s) for '
'%(name)s is not found.',
{'instance_filter_name': instance_filter_name,
'name': name},
instance=instance)
return False
return True
class IptablesFirewallDriver(base_firewall.IptablesFirewallDriver):
def __init__(self, execute=None, **kwargs):
"""Create an IP tables firewall driver instance
:param execute: unused, pass None
:param kwargs: extra arguments
The @kwargs parameter must contain a key 'host' that
maps to an instance of the nova.virt.libvirt.host.Host
class.
"""
super(IptablesFirewallDriver, self).__init__(**kwargs)
self.nwfilter = NWFilterFirewall(kwargs['host'])
def setup_basic_filtering(self, instance, network_info):
"""Set up basic NWFilter."""
self.nwfilter.setup_basic_filtering(instance, network_info)
def apply_instance_filter(self, instance, network_info):
"""No-op. Everything is done in prepare_instance_filter."""
pass
def unfilter_instance(self, instance, network_info):
# NOTE(salvatore-orlando):
# Overriding base class method for applying nwfilter operation
if self.instance_info.pop(instance.id, None):
self.remove_filters_for_instance(instance)
self.iptables.apply()
self.nwfilter.unfilter_instance(instance, network_info)
else:
LOG.info('Attempted to unfilter instance which is not filtered',
instance=instance)
def instance_filter_exists(self, instance, network_info):
"""Check nova-instance-instance-xxx exists."""
return self.nwfilter.instance_filter_exists(instance, network_info)

View File

@ -1,60 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
# Copyright (c) 2010 Citrix Systems, Inc.
#
# 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 oslo_serialization import jsonutils
from nova.virt import firewall
class Dom0IptablesFirewallDriver(firewall.IptablesFirewallDriver):
"""Dom0IptablesFirewallDriver class
This class provides an implementation for nova.virt.Firewall
using iptables. This class is meant to be used with the xenapi
backend and uses xenapi plugin to enforce iptables rules in dom0.
"""
def _plugin_execute(self, *cmd, **kwargs):
# Prepare arguments for plugin call
args = {}
process_input = kwargs.get('process_input', None)
if process_input is not None and isinstance(process_input, bytes):
kwargs['process_input'] = process_input.decode('utf-8')
args.update(map(lambda x: (x, str(kwargs[x])), kwargs))
args['cmd_args'] = jsonutils.dumps(cmd)
ret = self._session.call_plugin('xenhost.py', 'iptables_config', args)
json_ret = jsonutils.loads(ret)
return (json_ret['out'], json_ret['err'])
def __init__(self, xenapi_session=None, **kwargs):
from nova.network import linux_net
super(Dom0IptablesFirewallDriver, self).__init__(**kwargs)
self._session = xenapi_session
# Create IpTablesManager with executor through plugin
self.iptables = linux_net.IptablesManager(
redirect_privsep_calls_to=self._plugin_execute)
self.iptables.ipv4['filter'].add_chain('sg-fallback')
self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP')
self.iptables.ipv6['filter'].add_chain('sg-fallback')
self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP')
def _build_tcp_udp_rule(self, rule, version):
if rule.from_port == rule.to_port:
return ['--dport', '%s' % (rule.from_port,)]
else:
# No multiport needed for XS!
return ['--dport', '%s:%s' % (rule.from_port,
rule.to_port)]

View File

@ -59,4 +59,6 @@ upgrade:
network driver which has now been removed. The config options have
therefore been removed also.
* ``vmware.vlan_interface``
* ``[DEFAULT] firewall_driver``
* ``[DEFAULT] allow_same_net_traffic``
* ``[vmware] vlan_interface``