summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorSoren Hansen <soren.hansen@rackspace.com>2010-09-10 14:56:36 +0200
committerSoren Hansen <soren.hansen@rackspace.com>2010-09-10 14:56:36 +0200
commitc3dd0aa79d982d8f34172e6023d4b632ea23f2b9 (patch)
tree490a63f31baec93a2a705f017077a1372e4e94aa /nova
parentecbbfa343edf0ca0e82b35dc655fa23701bbdf22 (diff)
First pass of nwfilter based security group implementation. It is not where it is supposed to be and it does not actually do anything yet.
Diffstat (limited to 'nova')
-rw-r--r--nova/auth/manager.py2
-rw-r--r--nova/db/sqlalchemy/api.py1
-rw-r--r--nova/endpoint/cloud.py1
-rw-r--r--nova/tests/virt_unittest.py50
-rw-r--r--nova/virt/libvirt_conn.py63
5 files changed, 112 insertions, 5 deletions
diff --git a/nova/auth/manager.py b/nova/auth/manager.py
index 6aa5721c8..281e2d8f0 100644
--- a/nova/auth/manager.py
+++ b/nova/auth/manager.py
@@ -649,7 +649,7 @@ class AuthManager(object):
def delete_user(self, user):
"""Deletes a user"""
with self.driver() as drv:
- for security_group in db.security_group_get_by_user(context = {}, user_id=user.id):
+ for security_group in db.security_group_get_by_user(context = {}, user_id=User.safe_id(user)):
db.security_group_destroy({}, security_group.id)
drv.delete_user(User.safe_id(user))
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 4027e901c..622e76cd7 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -598,6 +598,7 @@ def security_group_create(_context, values):
def security_group_get_by_id(_context, security_group_id):
with managed_session() as session:
return session.query(models.SecurityGroup) \
+ .options(eagerload('rules')) \
.get(security_group_id)
diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py
index e6eca9850..5e5ed6c5e 100644
--- a/nova/endpoint/cloud.py
+++ b/nova/endpoint/cloud.py
@@ -299,7 +299,6 @@ class CloudController(object):
def authorize_security_group_ingress(self, context, group_name,
to_port=None, from_port=None,
ip_protocol=None, cidr_ip=None,
- user_id=None,
source_security_group_name=None,
source_security_group_owner_id=None):
security_group = db.security_group_get_by_user_and_name(context,
diff --git a/nova/tests/virt_unittest.py b/nova/tests/virt_unittest.py
index 2aab16809..b8dcec12b 100644
--- a/nova/tests/virt_unittest.py
+++ b/nova/tests/virt_unittest.py
@@ -14,23 +14,30 @@
# License for the specific language governing permissions and limitations
# under the License.
+from xml.dom.minidom import parseString
+
from nova import flags
from nova import test
+from nova.endpoint import cloud
from nova.virt import libvirt_conn
FLAGS = flags.FLAGS
class LibvirtConnTestCase(test.TrialTestCase):
- def test_get_uri_and_template(self):
+ def bitrot_test_get_uri_and_template(self):
class MockDataModel(object):
+ def __getitem__(self, name):
+ return self.datamodel[name]
+
def __init__(self):
self.datamodel = { 'name' : 'i-cafebabe',
'memory_kb' : '1024000',
'basepath' : '/some/path',
'bridge_name' : 'br100',
'mac_address' : '02:12:34:46:56:67',
- 'vcpus' : 2 }
+ 'vcpus' : 2,
+ 'project_id' : None }
type_uri_map = { 'qemu' : ('qemu:///system',
[lambda s: '<domain type=\'qemu\'>' in s,
@@ -53,7 +60,7 @@ class LibvirtConnTestCase(test.TrialTestCase):
self.assertEquals(uri, expected_uri)
for i, check in enumerate(checks):
- xml = conn.toXml(MockDataModel())
+ xml = conn.to_xml(MockDataModel())
self.assertTrue(check(xml), '%s failed check %d' % (xml, i))
# Deliberately not just assigning this string to FLAGS.libvirt_uri and
@@ -67,3 +74,40 @@ class LibvirtConnTestCase(test.TrialTestCase):
uri, template = conn.get_uri_and_template()
self.assertEquals(uri, testuri)
+
+class NWFilterTestCase(test.TrialTestCase):
+ def test_stuff(self):
+ cloud_controller = cloud.CloudController()
+ class FakeContext(object):
+ pass
+
+ context = FakeContext()
+ context.user = FakeContext()
+ context.user.id = 'fake'
+ context.user.is_superuser = lambda:True
+ cloud_controller.create_security_group(context, 'testgroup', 'test group description')
+ cloud_controller.authorize_security_group_ingress(context, 'testgroup', from_port='80',
+ to_port='81', ip_protocol='tcp',
+ cidr_ip='0.0.0.0/0')
+
+ fw = libvirt_conn.NWFilterFirewall()
+ xml = fw.security_group_to_nwfilter_xml(1)
+
+ dom = parseString(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'), 'allow')
+ 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('ip')
+ self.assertEqual(len(ip_conditions), 1)
+ self.assertEqual(ip_conditions[0].getAttribute('protocol'), 'tcp')
+ self.assertEqual(ip_conditions[0].getAttribute('dstportstart'), '80')
+ self.assertEqual(ip_conditions[0].getAttribute('dstportend'), '81')
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index e26030158..7bf2a68b1 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -426,3 +426,66 @@ class LibvirtConnection(object):
"""
domain = self._conn.lookupByName(instance_name)
return domain.interfaceStats(interface)
+
+
+class NWFilterFirewall(object):
+ """
+ This class implements a network filtering mechanism versatile
+ enough for EC2 style Security Group filtering by leveraging
+ libvirt's nwfilter.
+
+ First, all instances get a filter ("nova-base-filter") applied.
+ 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.
+
+ 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.
+ """
+
+ def __init__(self):
+ pass
+
+ def nova_base_filter(self):
+ return '''<filter name='nova-base-filter' chain='root'>
+ <uuid>26717364-50cf-42d1-8185-29bf893ab110</uuid>
+ <rule action='drop' direction='in' priority='1000'>
+ <ipv6 />
+ </rule>
+ <rule action='drop' direction='in' priority='1000'>
+ <ip />
+ </rule>
+</filter>'''
+
+ def security_group_to_nwfilter_xml(self, security_group_id):
+ security_group = db.security_group_get_by_id({}, security_group_id)
+ rule_xml = ""
+ for rule in security_group.rules:
+ rule_xml += "<rule action='allow' direction='in' priority='900'>"
+ if rule.cidr:
+ rule_xml += ("<ip srcipaddr='%s' protocol='%s' " +
+ "dstportstart='%s' dstportend='%s' />") % \
+ (rule.cidr, rule.protocol,
+ rule.from_port, rule.to_port)
+ rule_xml += "</rule>"
+ xml = '''<filter name='nova-secgroup-%d' chain='root'>%s</filter>''' % (security_group_id, rule_xml,)
+ return xml