summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Thode <prometheanfire@gentoo.org>2013-12-13 20:47:39 +0000
committerMatthew Thode <prometheanfire@gentoo.org>2013-12-13 20:47:39 +0000
commitc683a5ef4c8fe015034669d569fb1b6fbd51a90d (patch)
tree6194b6ac13c9cb58dca8f26c0058edf288beb77e /sys-cluster/neutron/files
parentRequire automake-1.12. Bug #493996 thanks to piruthiviraj natarajan <piruthiv... (diff)
downloadgentoo-2-c683a5ef4c8fe015034669d569fb1b6fbd51a90d.tar.gz
gentoo-2-c683a5ef4c8fe015034669d569fb1b6fbd51a90d.tar.bz2
gentoo-2-c683a5ef4c8fe015034669d569fb1b6fbd51a90d.zip
neutron fix for CVE-2013-6419
(Portage version: 2.2.7/cvs/Linux x86_64, signed Manifest commit with key 0x2471eb3e40ac5ac3)
Diffstat (limited to 'sys-cluster/neutron/files')
-rw-r--r--sys-cluster/neutron/files/CVE-2013-6419_2013.1.4.patch218
-rw-r--r--sys-cluster/neutron/files/CVE-2013-6419_2013.2.patch295
2 files changed, 513 insertions, 0 deletions
diff --git a/sys-cluster/neutron/files/CVE-2013-6419_2013.1.4.patch b/sys-cluster/neutron/files/CVE-2013-6419_2013.1.4.patch
new file mode 100644
index 000000000000..abb8e5f83794
--- /dev/null
+++ b/sys-cluster/neutron/files/CVE-2013-6419_2013.1.4.patch
@@ -0,0 +1,218 @@
+commit 933a88e49428f0fbdeb78695279b0a4ce3715b12
+Author: Aaron Rosen <arosen@nicira.com>
+Date: Mon Oct 7 15:34:38 2013 -0700
+
+ Add X-Tenant-ID to metadata request
+
+ Previously, one could update a port's device_id to be that of another tenant's
+ instance_id and then be able to retrieve that instance's metadata. In order
+ to prevent this X-Tenant-ID is now passed in the metadata request to nova and
+ nova then checks that X-Tenant-ID also matches the tenant_id for the instance
+ against it's database to ensure it's not being spoofed.
+
+ DocImpact - When upgrading OpenStack nova and neturon, neutron should be
+ updated first (and neutron-metadata-agent restarted before nova is
+ upgraded) in order to minimize downtime. This is because there is
+ also a patch to nova which has checks X-Tenant-ID against it's
+ database therefore neutron-metadata-agent needs to pass that
+ before nova is upgraded for metadata to work.
+
+ Fixes bug: 1235450
+
+ Conflicts:
+
+ quantum/agent/metadata/agent.py
+
+diff --git a/quantum/agent/metadata/agent.py b/quantum/agent/metadata/agent.py
+index 7bdfae8..e1abe93 100644
+--- a/quantum/agent/metadata/agent.py
++++ b/quantum/agent/metadata/agent.py
+@@ -83,9 +83,9 @@ class MetadataProxyHandler(object):
+ try:
+ LOG.debug(_("Request: %s"), req)
+
+- instance_id = self._get_instance_id(req)
++ instance_id, tenant_id = self._get_instance_and_tenant_id(req)
+ if instance_id:
+- return self._proxy_request(instance_id, req)
++ return self._proxy_request(instance_id, tenant_id, req)
+ else:
+ return webob.exc.HTTPNotFound()
+
+@@ -95,7 +95,7 @@ class MetadataProxyHandler(object):
+ 'Please try your request again.')
+ return webob.exc.HTTPInternalServerError(explanation=unicode(msg))
+
+- def _get_instance_id(self, req):
++ def _get_instance_and_tenant_id(self, req):
+ qclient = self._get_quantum_client()
+
+ remote_address = req.headers.get('X-Forwarded-For')
+@@ -116,12 +116,14 @@ class MetadataProxyHandler(object):
+ fixed_ips=['ip_address=%s' % remote_address])['ports']
+
+ if len(ports) == 1:
+- return ports[0]['device_id']
++ return ports[0]['device_id'], ports[0]['tenant_id']
++ return None, None
+
+- def _proxy_request(self, instance_id, req):
++ def _proxy_request(self, instance_id, tenant_id, req):
+ headers = {
+ 'X-Forwarded-For': req.headers.get('X-Forwarded-For'),
+ 'X-Instance-ID': instance_id,
++ 'X-Tenant-ID': tenant_id,
+ 'X-Instance-ID-Signature': self._sign_instance_id(instance_id)
+ }
+
+diff --git a/quantum/tests/unit/test_metadata_agent.py b/quantum/tests/unit/test_metadata_agent.py
+index c81a237..0e74bcb 100644
+--- a/quantum/tests/unit/test_metadata_agent.py
++++ b/quantum/tests/unit/test_metadata_agent.py
+@@ -54,8 +54,9 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+
+ def test_call(self):
+ req = mock.Mock()
+- with mock.patch.object(self.handler, '_get_instance_id') as get_id:
+- get_id.return_value = 'id'
++ with mock.patch.object(self.handler,
++ '_get_instance_and_tenant_id') as get_ids:
++ get_ids.return_value = ('instance_id', 'tenant_id')
+ with mock.patch.object(self.handler, '_proxy_request') as proxy:
+ proxy.return_value = 'value'
+
+@@ -64,21 +65,23 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+
+ def test_call_no_instance_match(self):
+ req = mock.Mock()
+- with mock.patch.object(self.handler, '_get_instance_id') as get_id:
+- get_id.return_value = None
++ with mock.patch.object(self.handler,
++ '_get_instance_and_tenant_id') as get_ids:
++ get_ids.return_value = None, None
+ retval = self.handler(req)
+ self.assertIsInstance(retval, webob.exc.HTTPNotFound)
+
+ def test_call_internal_server_error(self):
+ req = mock.Mock()
+- with mock.patch.object(self.handler, '_get_instance_id') as get_id:
+- get_id.side_effect = Exception
++ with mock.patch.object(self.handler,
++ '_get_instance_and_tenant_id') as get_ids:
++ get_ids.side_effect = Exception
+ retval = self.handler(req)
+ self.assertIsInstance(retval, webob.exc.HTTPInternalServerError)
+ self.assertEqual(len(self.log.mock_calls), 2)
+
+- def _get_instance_id_helper(self, headers, list_ports_retval,
+- networks=None, router_id=None):
++ def _get_instance_and_tenant_id_helper(self, headers, list_ports_retval,
++ networks=None, router_id=None):
+ headers['X-Forwarded-For'] = '192.168.1.1'
+ req = mock.Mock(headers=headers)
+
+@@ -86,8 +89,7 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+ return {'ports': list_ports_retval.pop(0)}
+
+ self.qclient.return_value.list_ports.side_effect = mock_list_ports
+- retval = self.handler._get_instance_id(req)
+-
++ instance_id, tenant_id = self.handler._get_instance_and_tenant_id(req)
+ expected = [
+ mock.call(
+ username=FakeConf.admin_user,
+@@ -114,7 +116,7 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+
+ self.qclient.assert_has_calls(expected)
+
+- return retval
++ return (instance_id, tenant_id)
+
+ def test_get_instance_id_router_id(self):
+ router_id = 'the_id'
+@@ -125,13 +127,14 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+ networks = ['net1', 'net2']
+ ports = [
+ [{'network_id': 'net1'}, {'network_id': 'net2'}],
+- [{'device_id': 'device_id'}]
++ [{'device_id': 'device_id', 'tenant_id': 'tenant_id'}]
+ ]
+
+ self.assertEqual(
+- self._get_instance_id_helper(headers, ports, networks=networks,
+- router_id=router_id),
+- 'device_id'
++ self._get_instance_and_tenant_id_helper(headers, ports,
++ networks=networks,
++ router_id=router_id),
++ ('device_id', 'tenant_id')
+ )
+
+ def test_get_instance_id_router_id_no_match(self):
+@@ -145,10 +148,11 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+ [{'network_id': 'net1'}, {'network_id': 'net2'}],
+ []
+ ]
+-
+- self.assertIsNone(
+- self._get_instance_id_helper(headers, ports, networks=networks,
+- router_id=router_id),
++ self.assertEqual(
++ self._get_instance_and_tenant_id_helper(headers, ports,
++ networks=networks,
++ router_id=router_id),
++ (None, None)
+ )
+
+ def test_get_instance_id_network_id(self):
+@@ -158,12 +162,14 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+ }
+
+ ports = [
+- [{'device_id': 'device_id'}]
++ [{'device_id': 'device_id',
++ 'tenant_id': 'tenant_id'}]
+ ]
+
+ self.assertEqual(
+- self._get_instance_id_helper(headers, ports, networks=['the_id']),
+- 'device_id'
++ self._get_instance_and_tenant_id_helper(headers, ports,
++ networks=['the_id']),
++ ('device_id', 'tenant_id')
+ )
+
+ def test_get_instance_id_network_id_no_match(self):
+@@ -174,8 +180,10 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+
+ ports = [[]]
+
+- self.assertIsNone(
+- self._get_instance_id_helper(headers, ports, networks=['the_id'])
++ self.assertEqual(
++ self._get_instance_and_tenant_id_helper(headers, ports,
++ networks=['the_id']),
++ (None, None)
+ )
+
+ def _proxy_request_test_helper(self, response_code=200, method='GET'):
+@@ -190,7 +198,8 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+ with mock.patch('httplib2.Http') as mock_http:
+ mock_http.return_value.request.return_value = (resp, 'content')
+
+- retval = self.handler._proxy_request('the_id', req)
++ retval = self.handler._proxy_request('the_id', 'tenant_id',
++ req)
+ mock_http.assert_has_calls([
+ mock.call().request(
+ 'http://9.9.9.9:8775/the_path',
+@@ -198,7 +207,8 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+ headers={
+ 'X-Forwarded-For': '8.8.8.8',
+ 'X-Instance-ID-Signature': 'signed',
+- 'X-Instance-ID': 'the_id'
++ 'X-Instance-ID': 'the_id',
++ 'X-Tenant-ID': 'tenant_id'
+ },
+ body=body
+ )]
diff --git a/sys-cluster/neutron/files/CVE-2013-6419_2013.2.patch b/sys-cluster/neutron/files/CVE-2013-6419_2013.2.patch
new file mode 100644
index 000000000000..6530915b470b
--- /dev/null
+++ b/sys-cluster/neutron/files/CVE-2013-6419_2013.2.patch
@@ -0,0 +1,295 @@
+commit 78f47f96437deefa0388f2dd63651fea0165eaf1
+Author: Aaron Rosen <arosen@nicira.com>
+Date: Mon Oct 7 15:34:38 2013 -0700
+
+ Add X-Tenant-ID to metadata request
+
+ Previously, one could update a port's device_id to be that of another tenant's
+ instance_id and then be able to retrieve that instance's metadata. In order
+ to prevent this X-Tenant-ID is now passed in the metadata request to nova and
+ nova then checks that X-Tenant-ID also matches the tenant_id for the instance
+ against it's database to ensure it's not being spoofed.
+
+ DocImpact - When upgrading OpenStack nova and neturon, neutron should be
+ updated first (and neutron-metadata-agent restarted before nova is
+ upgraded) in order to minimize downtime. This is because there is
+ also a patch to nova which has checks X-Tenant-ID against it's
+ database therefore neutron-metadata-agent needs to pass that
+ before nova is upgraded for metadata to work.
+
+ Fixes bug: 1235450
+
+diff --git a/neutron/agent/metadata/agent.py b/neutron/agent/metadata/agent.py
+index dcb0e00..e0042f4 100644
+--- a/neutron/agent/metadata/agent.py
++++ b/neutron/agent/metadata/agent.py
+@@ -84,61 +84,62 @@ class MetadataProxyHandler(object):
+ endpoint_url=self.auth_info.get('endpoint_url'),
+ endpoint_type=self.conf.endpoint_type
+ )
+ return qclient
+
+ @webob.dec.wsgify(RequestClass=webob.Request)
+ def __call__(self, req):
+ try:
+ LOG.debug(_("Request: %s"), req)
+
+- instance_id = self._get_instance_id(req)
++ instance_id, tenant_id = self._get_instance_and_tenant_id(req)
+ if instance_id:
+- return self._proxy_request(instance_id, req)
++ return self._proxy_request(instance_id, tenant_id, req)
+ else:
+ return webob.exc.HTTPNotFound()
+
+ except Exception:
+ LOG.exception(_("Unexpected error."))
+ msg = _('An unknown error has occurred. '
+ 'Please try your request again.')
+ return webob.exc.HTTPInternalServerError(explanation=unicode(msg))
+
+- def _get_instance_id(self, req):
++ def _get_instance_and_tenant_id(self, req):
+ qclient = self._get_neutron_client()
+
+ remote_address = req.headers.get('X-Forwarded-For')
+ network_id = req.headers.get('X-Neutron-Network-ID')
+ router_id = req.headers.get('X-Neutron-Router-ID')
+
+ if network_id:
+ networks = [network_id]
+ else:
+ internal_ports = qclient.list_ports(
+ device_id=router_id,
+ device_owner=DEVICE_OWNER_ROUTER_INTF)['ports']
+
+ networks = [p['network_id'] for p in internal_ports]
+
+ ports = qclient.list_ports(
+ network_id=networks,
+ fixed_ips=['ip_address=%s' % remote_address])['ports']
+
+ self.auth_info = qclient.get_auth_info()
+-
+ if len(ports) == 1:
+- return ports[0]['device_id']
++ return ports[0]['device_id'], ports[0]['tenant_id']
++ return None, None
+
+- def _proxy_request(self, instance_id, req):
++ def _proxy_request(self, instance_id, tenant_id, req):
+ headers = {
+ 'X-Forwarded-For': req.headers.get('X-Forwarded-For'),
+ 'X-Instance-ID': instance_id,
++ 'X-Tenant-ID': tenant_id,
+ 'X-Instance-ID-Signature': self._sign_instance_id(instance_id)
+ }
+
+ url = urlparse.urlunsplit((
+ 'http',
+ '%s:%s' % (self.conf.nova_metadata_ip,
+ self.conf.nova_metadata_port),
+ req.path_info,
+ req.query_string,
+ ''))
+diff --git a/neutron/tests/unit/test_metadata_agent.py b/neutron/tests/unit/test_metadata_agent.py
+index 36b6f84..aa1cc84 100644
+--- a/neutron/tests/unit/test_metadata_agent.py
++++ b/neutron/tests/unit/test_metadata_agent.py
+@@ -48,54 +48,56 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+ self.addCleanup(self.qclient_p.stop)
+
+ self.log_p = mock.patch.object(agent, 'LOG')
+ self.log = self.log_p.start()
+ self.addCleanup(self.log_p.stop)
+
+ self.handler = agent.MetadataProxyHandler(FakeConf)
+
+ def test_call(self):
+ req = mock.Mock()
+- with mock.patch.object(self.handler, '_get_instance_id') as get_id:
+- get_id.return_value = 'id'
++ with mock.patch.object(self.handler,
++ '_get_instance_and_tenant_id') as get_ids:
++ get_ids.return_value = ('instance_id', 'tenant_id')
+ with mock.patch.object(self.handler, '_proxy_request') as proxy:
+ proxy.return_value = 'value'
+
+ retval = self.handler(req)
+ self.assertEqual(retval, 'value')
+
+ def test_call_no_instance_match(self):
+ req = mock.Mock()
+- with mock.patch.object(self.handler, '_get_instance_id') as get_id:
+- get_id.return_value = None
++ with mock.patch.object(self.handler,
++ '_get_instance_and_tenant_id') as get_ids:
++ get_ids.return_value = None, None
+ retval = self.handler(req)
+ self.assertIsInstance(retval, webob.exc.HTTPNotFound)
+
+ def test_call_internal_server_error(self):
+ req = mock.Mock()
+- with mock.patch.object(self.handler, '_get_instance_id') as get_id:
+- get_id.side_effect = Exception
++ with mock.patch.object(self.handler,
++ '_get_instance_and_tenant_id') as get_ids:
++ get_ids.side_effect = Exception
+ retval = self.handler(req)
+ self.assertIsInstance(retval, webob.exc.HTTPInternalServerError)
+ self.assertEqual(len(self.log.mock_calls), 2)
+
+- def _get_instance_id_helper(self, headers, list_ports_retval,
+- networks=None, router_id=None):
++ def _get_instance_and_tenant_id_helper(self, headers, list_ports_retval,
++ networks=None, router_id=None):
+ headers['X-Forwarded-For'] = '192.168.1.1'
+ req = mock.Mock(headers=headers)
+
+ def mock_list_ports(*args, **kwargs):
+ return {'ports': list_ports_retval.pop(0)}
+
+ self.qclient.return_value.list_ports.side_effect = mock_list_ports
+- retval = self.handler._get_instance_id(req)
+-
++ instance_id, tenant_id = self.handler._get_instance_and_tenant_id(req)
+ expected = [
+ mock.call(
+ username=FakeConf.admin_user,
+ tenant_name=FakeConf.admin_tenant_name,
+ region_name=FakeConf.auth_region,
+ auth_url=FakeConf.auth_url,
+ password=FakeConf.admin_password,
+ auth_strategy=FakeConf.auth_strategy,
+ auth_token=None,
+ endpoint_url=None,
+@@ -111,105 +113,113 @@ class TestMetadataProxyHandler(base.BaseTestCase):
+ )
+
+ expected.append(
+ mock.call().list_ports(
+ network_id=networks or [],
+ fixed_ips=['ip_address=192.168.1.1'])
+ )
+
+ self.qclient.assert_has_calls(expected)
+
+- return retval
++ return (instance_id, tenant_id)
+
+ def test_get_instance_id_router_id(self):
+ router_id = 'the_id'
+ headers = {
+ 'X-Neutron-Router-ID': router_id
+ }
+
+ networks = ['net1', 'net2']
+ ports = [
+ [{'network_id': 'net1'}, {'network_id': 'net2'}],
+- [{'device_id': 'device_id'}]
++ [{'device_id': 'device_id', 'tenant_id': 'tenant_id'}]
+ ]
+
+ self.assertEqual(
+- self._get_instance_id_helper(headers, ports, networks=networks,
+- router_id=router_id),
+- 'device_id'
++ self._get_instance_and_tenant_id_helper(headers, ports,
++ networks=networks,
++ router_id=router_id),
++ ('device_id', 'tenant_id')
+ )
+
+ def test_get_instance_id_router_id_no_match(self):
+ router_id = 'the_id'
+ headers = {
+ 'X-Neutron-Router-ID': router_id
+ }
+
+ networks = ['net1', 'net2']
+ ports = [
+ [{'network_id': 'net1'}, {'network_id': 'net2'}],
+ []
+ ]
+-
+- self.assertIsNone(
+- self._get_instance_id_helper(headers, ports, networks=networks,
+- router_id=router_id),
++ self.assertEqual(
++ self._get_instance_and_tenant_id_helper(headers, ports,
++ networks=networks,
++ router_id=router_id),
++ (None, None)
+ )
+
+ def test_get_instance_id_network_id(self):
+ network_id = 'the_id'
+ headers = {
+ 'X-Neutron-Network-ID': network_id
+ }
+
+ ports = [
+- [{'device_id': 'device_id'}]
++ [{'device_id': 'device_id',
++ 'tenant_id': 'tenant_id'}]
+ ]
+
+ self.assertEqual(
+- self._get_instance_id_helper(headers, ports, networks=['the_id']),
+- 'device_id'
++ self._get_instance_and_tenant_id_helper(headers, ports,
++ networks=['the_id']),
++ ('device_id', 'tenant_id')
+ )
+
+ def test_get_instance_id_network_id_no_match(self):
+ network_id = 'the_id'
+ headers = {
+ 'X-Neutron-Network-ID': network_id
+ }
+
+ ports = [[]]
+
+- self.assertIsNone(
+- self._get_instance_id_helper(headers, ports, networks=['the_id'])
++ self.assertEqual(
++ self._get_instance_and_tenant_id_helper(headers, ports,
++ networks=['the_id']),
++ (None, None)
+ )
+
+ def _proxy_request_test_helper(self, response_code=200, method='GET'):
+ hdrs = {'X-Forwarded-For': '8.8.8.8'}
+ body = 'body'
+
+ req = mock.Mock(path_info='/the_path', query_string='', headers=hdrs,
+ method=method, body=body)
+ resp = mock.Mock(status=response_code)
+ with mock.patch.object(self.handler, '_sign_instance_id') as sign:
+ sign.return_value = 'signed'
+ with mock.patch('httplib2.Http') as mock_http:
+ mock_http.return_value.request.return_value = (resp, 'content')
+
+- retval = self.handler._proxy_request('the_id', req)
++ retval = self.handler._proxy_request('the_id', 'tenant_id',
++ req)
+ mock_http.assert_has_calls([
+ mock.call().request(
+ 'http://9.9.9.9:8775/the_path',
+ method=method,
+ headers={
+ 'X-Forwarded-For': '8.8.8.8',
+ 'X-Instance-ID-Signature': 'signed',
+- 'X-Instance-ID': 'the_id'
++ 'X-Instance-ID': 'the_id',
++ 'X-Tenant-ID': 'tenant_id'
+ },
+ body=body
+ )]
+ )
+
+ return retval
+
+ def test_proxy_request_post(self):
+ self.assertEqual('content',
+ self._proxy_request_test_helper(method='POST'))