diff options
| -rw-r--r-- | Authors | 1 | ||||
| -rw-r--r-- | nova/tests/test_libvirt.py | 41 | ||||
| -rw-r--r-- | nova/virt/libvirt/firewall.py | 259 |
3 files changed, 7 insertions, 294 deletions
@@ -168,6 +168,7 @@ Vladimir Popovski <vladimir@zadarastorage.com> William Henry <whenry@redhat.com> William Kelly <william.kelly@rackspace.com> William Wolf <throughnothing@gmail.com> +Yaguang Tang <heut2008@gmail.com> Yoshiaki Tamura <yoshi@midokura.jp> Youcef Laribi <Youcef.Laribi@eu.citrix.com> Yun Mao <yunmao@gmail.com> diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 2eede7ed5..2b1e5eed8 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1631,28 +1631,6 @@ class NWFilterTestCase(test.TestCase): security_group = db.security_group_get_by_name(self.context, 'fake', 'testgroup') - - xml = self.fw.security_group_to_nwfilter_xml(security_group.id) - - dom = xml_to_dom(xml) - self.assertEqual(dom.firstChild.tagName, 'filter') - - rules = dom.getElementsByTagName('rule') - self.assertEqual(len(rules), 1) - - # It's supposed to allow inbound traffic. - self.assertEqual(rules[0].getAttribute('action'), 'accept') - self.assertEqual(rules[0].getAttribute('direction'), 'in') - - # Must be lower priority than the base filter (which blocks everything) - self.assertTrue(int(rules[0].getAttribute('priority')) < 1000) - - ip_conditions = rules[0].getElementsByTagName('tcp') - self.assertEqual(len(ip_conditions), 1) - self.assertEqual(ip_conditions[0].getAttribute('srcipaddr'), '0.0.0.0') - self.assertEqual(ip_conditions[0].getAttribute('srcipmask'), '0.0.0.0') - self.assertEqual(ip_conditions[0].getAttribute('dstportstart'), '80') - self.assertEqual(ip_conditions[0].getAttribute('dstportend'), '81') self.teardown_security_group() def teardown_security_group(self): @@ -1732,8 +1710,7 @@ class NWFilterTestCase(test.TestCase): def _ensure_all_called(mac): instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'], mac.translate(None, ':')) - secgroup_filter = 'nova-secgroup-%s' % self.security_group['id'] - for required in [secgroup_filter, 'allow-dhcp-server', + for required in ['allow-dhcp-server', 'no-arp-spoofing', 'no-ip-spoofing', 'no-mac-spoofing']: self.assertTrue(required in @@ -1754,20 +1731,10 @@ class NWFilterTestCase(test.TestCase): mac = network_info[0][1]['mac'] self.fw.setup_basic_filtering(instance, network_info) - self.fw.prepare_instance_filter(instance, network_info) - self.fw.apply_instance_filter(instance, network_info) _ensure_all_called(mac) self.teardown_security_group() db.instance_destroy(context.get_admin_context(), instance_ref['id']) - def test_create_network_filters(self): - instance_ref = self._create_instance() - network_info = _fake_network_info(self.stubs, 3) - result = self.fw._create_network_filters(instance_ref, - network_info, - "fake") - self.assertEquals(len(result), 3) - def test_unfilter_instance_undefines_nwfilters(self): admin_ctxt = context.get_admin_context() @@ -1788,13 +1755,9 @@ class NWFilterTestCase(test.TestCase): network_info = _fake_network_info(self.stubs, 1) self.fw.setup_basic_filtering(instance, network_info) - self.fw.prepare_instance_filter(instance, network_info) - self.fw.apply_instance_filter(instance, network_info) original_filter_count = len(fakefilter.filters) self.fw.unfilter_instance(instance, network_info) - - # should undefine 2 filters: instance and instance-secgroup - self.assertEqual(original_filter_count - len(fakefilter.filters), 2) + self.assertEqual(original_filter_count - len(fakefilter.filters), 1) db.instance_destroy(admin_ctxt, instance_ref['id']) diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index 292152d2c..04b3cf42a 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -45,48 +45,11 @@ except ImportError: class NWFilterFirewall(base_firewall.FirewallDriver): """ - This class implements a network filtering mechanism versatile - enough for EC2 style Security Group filtering by leveraging + This class implements a network filtering mechanism by using libvirt's nwfilter. - - First, all instances get a filter ("nova-base-filter") applied. - This filter provides some basic security such as protection against - MAC spoofing, IP spoofing, and ARP spoofing. - - This filter drops all incoming ipv4 and ipv6 connections. - Outgoing connections are never blocked. - - Second, every security group maps to a nwfilter filter(*). - NWFilters can be updated at runtime and changes are applied - immediately, so changes to security groups can be applied at - runtime (as mandated by the spec). - - Security group rules are named "nova-secgroup-<id>" where <id> - is the internal id of the security group. They're applied only on - hosts that have instances in the security group in question. - - Updates to security groups are done by updating the data model - (in response to API calls) followed by a request sent to all - the nodes with instances in the security group to refresh the - security group. - - Each instance has its own NWFilter, which references the above - mentioned security group NWFilters. This was done because - interfaces can only reference one filter while filters can - reference multiple other filters. This has the added benefit of - actually being able to add and remove security groups from an - instance at run time. This functionality is not exposed anywhere, - though. - - Outstanding questions: - - The name is unique, so would there be any good reason to sync - the uuid across the nodes (by assigning it from the datamodel)? - - - (*) This sentence brought to you by the redundancy department of - redundancy. - + 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, get_connection, **kwargs): @@ -125,16 +88,6 @@ class NWFilterFirewall(base_firewall.FirewallDriver): </rule> </filter>''' - @staticmethod - def nova_ra_filter(): - return '''<filter name='nova-allow-ra-server' chain='root'> - <uuid>d707fa71-4fb5-4b27-9ab7-ba5ca19c8804</uuid> - <rule action='accept' direction='inout' - priority='100'> - <icmpv6 srcipaddr='$RASERVER'/> - </rule> - </filter>''' - 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'), @@ -177,14 +130,7 @@ class NWFilterFirewall(base_firewall.FirewallDriver): 'allow-dhcp-server'])) self._define_filter(self._filter_container('nova-vpn', ['allow-dhcp-server'])) - self._define_filter(self.nova_base_ipv4_filter) - self._define_filter(self.nova_base_ipv6_filter) self._define_filter(self.nova_dhcp_filter) - self._define_filter(self.nova_ra_filter) - if FLAGS.allow_same_net_traffic: - self._define_filter(self.nova_project_filter) - if FLAGS.use_ipv6: - self._define_filter(self.nova_project_filter_v6) self.static_filters_configured = True @@ -194,54 +140,6 @@ class NWFilterFirewall(base_firewall.FirewallDriver): ''.join(["<filterref filter='%s'/>" % (f,) for f in filters])) return xml - @staticmethod - def nova_base_ipv4_filter(): - retval = "<filter name='nova-base-ipv4' chain='ipv4'>" - for protocol in ['tcp', 'udp', 'icmp']: - for direction, action, priority in [('out', 'accept', 399), - ('in', 'drop', 400)]: - retval += """<rule action='%s' direction='%s' priority='%d'> - <%s /> - </rule>""" % (action, direction, - priority, protocol) - retval += '</filter>' - return retval - - @staticmethod - def nova_base_ipv6_filter(): - retval = "<filter name='nova-base-ipv6' chain='ipv6'>" - for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: - for direction, action, priority in [('out', 'accept', 399), - ('in', 'drop', 400)]: - retval += """<rule action='%s' direction='%s' priority='%d'> - <%s /> - </rule>""" % (action, direction, - priority, protocol) - retval += '</filter>' - return retval - - @staticmethod - def nova_project_filter(): - retval = "<filter name='nova-project' chain='ipv4'>" - for protocol in ['tcp', 'udp', 'icmp']: - retval += """<rule action='accept' direction='in' priority='200'> - <%s srcipaddr='$PROJNET' srcipmask='$PROJMASK' /> - </rule>""" % protocol - retval += '</filter>' - return retval - - @staticmethod - def nova_project_filter_v6(): - retval = "<filter name='nova-project-v6' chain='ipv6'>" - for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: - retval += """<rule action='accept' direction='inout' - priority='200'> - <%s srcipaddr='$PROJNETV6' - srcipmask='$PROJMASKV6' /> - </rule>""" % (protocol) - retval += '</filter>' - return retval - def _define_filter(self, xml): if callable(xml): xml = xml() @@ -263,61 +161,6 @@ class NWFilterFirewall(base_firewall.FirewallDriver): 'is not found.') % locals(), instance=instance) - instance_secgroup_filter_name = ('%s-secgroup' % - self._instance_filter_name(instance)) - - try: - _nw = self._conn.nwfilterLookupByName( - instance_secgroup_filter_name) - _nw.undefine() - except libvirt.libvirtError: - LOG.debug(_('The nwfilter(%(instance_secgroup_filter_name)s) ' - 'is not found.') % locals(), instance=instance) - - def prepare_instance_filter(self, instance, network_info): - """Creates an NWFilter for the given instance. - - In the process, it makes sure the filters for the provider blocks, - security groups, and base filter are all in place. - - """ - self.refresh_provider_fw_rules() - - ctxt = context.get_admin_context() - - instance_secgroup_filter_name = ('%s-secgroup' % - self._instance_filter_name(instance)) - - instance_secgroup_filter_children = ['nova-base-ipv4', - 'nova-base-ipv6', - 'nova-allow-dhcp-server'] - - if FLAGS.use_ipv6: - networks = [network for (network, info) in network_info if - info['gateway_v6']] - - if networks: - instance_secgroup_filter_children.append( - 'nova-allow-ra-server') - - for security_group in db.security_group_get_by_instance(ctxt, - instance['id']): - - self.refresh_security_group_rules(security_group['id']) - - instance_secgroup_filter_children.append('nova-secgroup-%s' % - security_group['id']) - - self._define_filter( - self._filter_container(instance_secgroup_filter_name, - instance_secgroup_filter_children)) - - network_filters = self._create_network_filters(instance, network_info, - instance_secgroup_filter_name) - - for (name, children) in network_filters: - self._define_filters(name, children) - def _create_network_filters(self, instance, network_info, instance_secgroup_filter_name): if instance['image_ref'] == str(FLAGS.vpn_image_id): @@ -345,100 +188,6 @@ class NWFilterFirewall(base_firewall.FirewallDriver): self._define_filter(self._filter_container(filter_name, filter_children)) - def refresh_security_group_rules(self, security_group_id): - return self._define_filter( - self.security_group_to_nwfilter_xml(security_group_id)) - - def refresh_provider_fw_rules(self): - """Update rules for all instances. - - This is part of the FirewallDriver API and is called when the - provider firewall rules change in the database. In the - `prepare_instance_filter` we add a reference to the - 'nova-provider-rules' filter for each instance's firewall, and - by changing that filter we update them all. - - """ - xml = self.provider_fw_to_nwfilter_xml() - return self._define_filter(xml) - - @staticmethod - def security_group_to_nwfilter_xml(security_group_id): - security_group = db.security_group_get(context.get_admin_context(), - security_group_id) - rule_xml = "" - v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} - for rule in security_group.rules: - rule_xml += "<rule action='accept' direction='in' priority='300'>" - if rule.cidr: - version = netutils.get_ip_version(rule.cidr) - if(FLAGS.use_ipv6 and version == 6): - net, prefixlen = netutils.get_net_and_prefixlen(rule.cidr) - rule_xml += ("<%s srcipaddr='%s' srcipmask='%s' " % - (v6protocol[rule.protocol], net, prefixlen)) - else: - net, mask = netutils.get_net_and_mask(rule.cidr) - rule_xml += ("<%s srcipaddr='%s' srcipmask='%s' " % - (rule.protocol, net, mask)) - if rule.protocol in ['tcp', 'udp']: - rule_xml += ("dstportstart='%s' dstportend='%s' " % - (rule.from_port, rule.to_port)) - elif rule.protocol == 'icmp': - LOG.info('rule.protocol: %r, rule.from_port: %r, ' - 'rule.to_port: %r', rule.protocol, - rule.from_port, rule.to_port) - if rule.from_port != -1: - rule_xml += "type='%s' " % rule.from_port - if rule.to_port != -1: - rule_xml += "code='%s' " % rule.to_port - - rule_xml += '/>\n' - rule_xml += "</rule>\n" - xml = "<filter name='nova-secgroup-%s' " % security_group_id - if(FLAGS.use_ipv6): - xml += "chain='root'>%s</filter>" % rule_xml - else: - xml += "chain='ipv4'>%s</filter>" % rule_xml - return xml - - @staticmethod - def provider_fw_to_nwfilter_xml(): - """Compose a filter of drop rules from specified cidrs.""" - rule_xml = "" - v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} - rules = db.provider_fw_rule_get_all(context.get_admin_context()) - for rule in rules: - rule_xml += "<rule action='block' direction='in' priority='150'>" - version = netutils.get_ip_version(rule.cidr) - if(FLAGS.use_ipv6 and version == 6): - net, prefixlen = netutils.get_net_and_prefixlen(rule.cidr) - rule_xml += ("<%s srcipaddr='%s' srcipmask='%s' " % - (v6protocol[rule.protocol], net, prefixlen)) - else: - net, mask = netutils.get_net_and_mask(rule.cidr) - rule_xml += ("<%s srcipaddr='%s' srcipmask='%s' " % - (rule.protocol, net, mask)) - if rule.protocol in ['tcp', 'udp']: - rule_xml += ("dstportstart='%s' dstportend='%s' " % - (rule.from_port, rule.to_port)) - elif rule.protocol == 'icmp': - LOG.info('rule.protocol: %r, rule.from_port: %r, ' - 'rule.to_port: %r', rule.protocol, - rule.from_port, rule.to_port) - if rule.from_port != -1: - rule_xml += "type='%s' " % rule.from_port - if rule.to_port != -1: - rule_xml += "code='%s' " % rule.to_port - - rule_xml += '/>\n' - rule_xml += "</rule>\n" - xml = "<filter name='nova-provider-rules' " - if(FLAGS.use_ipv6): - xml += "chain='root'>%s</filter>" % rule_xml - else: - xml += "chain='ipv4'>%s</filter>" % rule_xml - return xml - @staticmethod def _instance_filter_name(instance, nic_id=None): if not nic_id: |
