|
1 Upstream patch fixed in Grizzly 2013.1.5, Havana 2013.2.1, Icehouse |
|
2 |
|
3 commit 07006be9165d1008ca0382b6f0ad25b13a676a55 |
|
4 Author: Aaron Rosen <[email protected]> |
|
5 Date: Mon Oct 7 13:33:31 2013 -0700 |
|
6 |
|
7 Prevent spoofing instance_id from neutron to nova |
|
8 |
|
9 Previously, one could update a port's device_id in neutron to be |
|
10 that of another tenant's instance_id and then be able to retrieve |
|
11 that instance's metadata. This patch prevents this from occurring by |
|
12 checking that X-Tenant-ID received from the metadata request matches |
|
13 the tenant_id in the nova database. |
|
14 |
|
15 DocImpact - This patch is dependent on another patch in neutron |
|
16 which adds X-Tenant-ID to the request. Therefore to |
|
17 minimize downtime one should upgrade Neutron first (then |
|
18 restart neutron-metadata-agent) and lastly update nova. |
|
19 |
|
20 Change-Id: I93bf662797c3986324ca2099b403833c2e990fb4 |
|
21 Closes-Bug: #1235450 |
|
22 |
|
23 diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py |
|
24 index bbaeba5..2b7f659 100644 |
|
25 --- a/nova/api/metadata/handler.py |
|
26 +++ b/nova/api/metadata/handler.py |
|
27 @@ -144,6 +144,7 @@ class MetadataRequestHandler(wsgi.Application): |
|
28 |
|
29 def _handle_instance_id_request(self, req): |
|
30 instance_id = req.headers.get('X-Instance-ID') |
|
31 + tenant_id = req.headers.get('X-Tenant-ID') |
|
32 signature = req.headers.get('X-Instance-ID-Signature') |
|
33 remote_address = req.headers.get('X-Forwarded-For') |
|
34 |
|
35 @@ -151,8 +152,12 @@ class MetadataRequestHandler(wsgi.Application): |
|
36 |
|
37 if instance_id is None: |
|
38 msg = _('X-Instance-ID header is missing from request.') |
|
39 + elif tenant_id is None: |
|
40 + msg = _('X-Tenant-ID header is missing from request.') |
|
41 elif not isinstance(instance_id, basestring): |
|
42 msg = _('Multiple X-Instance-ID headers found within request.') |
|
43 + elif not isinstance(tenant_id, basestring): |
|
44 + msg = _('Multiple X-Tenant-ID headers found within request.') |
|
45 else: |
|
46 msg = None |
|
47 |
|
48 @@ -188,4 +193,12 @@ class MetadataRequestHandler(wsgi.Application): |
|
49 LOG.error(_('Failed to get metadata for instance id: %s'), |
|
50 instance_id) |
|
51 |
|
52 + if meta_data.instance['project_id'] != tenant_id: |
|
53 + LOG.warning(_("Tenant_id %(tenant_id)s does not match tenant_id " |
|
54 + "of instance %(instance_id)s."), |
|
55 + {'tenant_id': tenant_id, |
|
56 + 'instance_id': instance_id}) |
|
57 + # causes a 404 to be raised |
|
58 + meta_data = None |
|
59 + |
|
60 return meta_data |
|
61 diff --git a/nova/tests/test_metadata.py b/nova/tests/test_metadata.py |
|
62 index 01f274f..51b6f72 100644 |
|
63 --- a/nova/tests/test_metadata.py |
|
64 +++ b/nova/tests/test_metadata.py |
|
65 @@ -510,6 +510,7 @@ class MetadataHandlerTestCase(test.TestCase): |
|
66 relpath="/2009-04-04/user-data", |
|
67 address="192.192.192.2", |
|
68 headers={'X-Instance-ID': 'a-b-c-d', |
|
69 + 'X-Tenant-ID': 'test', |
|
70 'X-Instance-ID-Signature': signed}) |
|
71 self.assertEqual(response.status_int, 200) |
|
72 |
|
73 @@ -522,6 +523,7 @@ class MetadataHandlerTestCase(test.TestCase): |
|
74 fake_get_metadata_by_instance_id=fake_get_metadata, |
|
75 headers={'X-Forwarded-For': '192.192.192.2', |
|
76 'X-Instance-ID': 'a-b-c-d', |
|
77 + 'X-Tenant-ID': 'test', |
|
78 'X-Instance-ID-Signature': signed}) |
|
79 |
|
80 self.assertEqual(response.status_int, 200) |
|
81 @@ -536,10 +538,36 @@ class MetadataHandlerTestCase(test.TestCase): |
|
82 fake_get_metadata_by_instance_id=fake_get_metadata, |
|
83 headers={'X-Forwarded-For': '192.192.192.2', |
|
84 'X-Instance-ID': 'a-b-c-d', |
|
85 + 'X-Tenant-ID': 'test', |
|
86 'X-Instance-ID-Signature': ''}) |
|
87 |
|
88 self.assertEqual(response.status_int, 403) |
|
89 |
|
90 + # missing X-Tenant-ID from request |
|
91 + response = fake_request( |
|
92 + self.stubs, self.mdinst, |
|
93 + relpath="/2009-04-04/user-data", |
|
94 + address="192.192.192.2", |
|
95 + fake_get_metadata_by_instance_id=fake_get_metadata, |
|
96 + headers={'X-Forwarded-For': '192.192.192.2', |
|
97 + 'X-Instance-ID': 'a-b-c-d', |
|
98 + 'X-Instance-ID-Signature': signed}) |
|
99 + |
|
100 + self.assertEqual(response.status_int, 400) |
|
101 + |
|
102 + # mismatched X-Tenant-ID |
|
103 + response = fake_request( |
|
104 + self.stubs, self.mdinst, |
|
105 + relpath="/2009-04-04/user-data", |
|
106 + address="192.192.192.2", |
|
107 + fake_get_metadata_by_instance_id=fake_get_metadata, |
|
108 + headers={'X-Forwarded-For': '192.192.192.2', |
|
109 + 'X-Instance-ID': 'a-b-c-d', |
|
110 + 'X-Tenant-ID': 'FAKE', |
|
111 + 'X-Instance-ID-Signature': signed}) |
|
112 + |
|
113 + self.assertEqual(response.status_int, 404) |
|
114 + |
|
115 # without X-Forwarded-For |
|
116 response = fake_request( |
|
117 self.stubs, self.mdinst, |
|
118 @@ -547,6 +575,7 @@ class MetadataHandlerTestCase(test.TestCase): |
|
119 address="192.192.192.2", |
|
120 fake_get_metadata_by_instance_id=fake_get_metadata, |
|
121 headers={'X-Instance-ID': 'a-b-c-d', |
|
122 + 'X-Tenant-ID': 'test', |
|
123 'X-Instance-ID-Signature': signed}) |
|
124 |
|
125 self.assertEqual(response.status_int, 500) |
|
126 @@ -564,6 +593,7 @@ class MetadataHandlerTestCase(test.TestCase): |
|
127 fake_get_metadata_by_instance_id=fake_get_metadata, |
|
128 headers={'X-Forwarded-For': '192.192.192.2', |
|
129 'X-Instance-ID': 'z-z-z-z', |
|
130 + 'X-Tenant-ID': 'test', |
|
131 'X-Instance-ID-Signature': signed}) |
|
132 self.assertEqual(response.status_int, 500) |
|
133 |