From 918d9998e1855879e35b039430b71277e3200814 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Mon, 16 Jan 2012 15:35:08 +0000 Subject: [PATCH] Add ipv4 and ipv6 validation Fixes bug 891264 Change-Id: Ie5975a6ee8129392b308d405ab5cb9303bdd0a89 --- nova/api/openstack/compute/servers.py | 35 +++- .../api/openstack/compute/test_servers.py | 164 ++++++++++++++++++ 2 files changed, 197 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index e90e30b7dfe..941629244f5 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -16,6 +16,7 @@ import base64 import os +import socket from xml.dom import minidom from webob import exc @@ -568,6 +569,20 @@ def _validate_user_data(self, user_data): expl = _('Userdata content cannot be decoded') raise exc.HTTPBadRequest(explanation=expl) + def _validate_access_ipv4(self, address): + try: + socket.inet_aton(address) + except socket.error: + expl = _('accessIPv4 is not proper IPv4 format') + raise exc.HTTPBadRequest(explanation=expl) + + def _validate_access_ipv6(self, address): + try: + socket.inet_pton(socket.AF_INET6, address) + except socket.error: + expl = _('accessIPv4 is not proper IPv4 format') + raise exc.HTTPBadRequest(explanation=expl) + @wsgi.serializers(xml=ServerTemplate) @exception.novaclient_converter @scheduler_api.redirect_handler @@ -634,6 +649,14 @@ def create(self, req, body): requested_networks = self._get_requested_networks( requested_networks) + (access_ip_v4, ) = server_dict.get('accessIPv4'), + if access_ip_v4 is not None: + self._validate_access_ipv4(access_ip_v4) + + (access_ip_v6, ) = server_dict.get('accessIPv6'), + if access_ip_v6 is not None: + self._validate_access_ipv6(access_ip_v6) + try: flavor_id = self._flavor_id_from_req_data(body) except ValueError as error: @@ -687,8 +710,8 @@ def create(self, req, body): display_description=name, key_name=key_name, metadata=server_dict.get('metadata', {}), - access_ip_v4=server_dict.get('accessIPv4'), - access_ip_v6=server_dict.get('accessIPv6'), + access_ip_v4=access_ip_v4, + access_ip_v6=access_ip_v6, injected_files=injected_files, admin_password=password, zone_blob=zone_blob, @@ -767,10 +790,12 @@ def update(self, req, id, body): if 'accessIPv4' in body['server']: access_ipv4 = body['server']['accessIPv4'] + self._validate_access_ipv4(access_ipv4) update_dict['access_ip_v4'] = access_ipv4.strip() if 'accessIPv6' in body['server']: access_ipv6 = body['server']['accessIPv6'] + self._validate_access_ipv6(access_ipv6) update_dict['access_ip_v6'] = access_ipv6.strip() if 'auto_disk_config' in body['server']: @@ -1008,6 +1033,12 @@ def _action_rebuild(self, req, id, body): 'metadata': 'metadata', } + if 'accessIPv4' in body: + self._validate_access_ipv4(body['accessIPv4']) + + if 'accessIPv6' in body: + self._validate_access_ipv6(body['accessIPv6']) + kwargs = {} for request_attribute, instance_attribute in attr_map.items(): diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index 77c89d47db6..bf572af5c40 100644 --- a/nova/tests/api/openstack/compute/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -1085,6 +1085,17 @@ def test_update_server_access_ipv4(self): self.assertEqual(res_dict['server']['id'], FAKE_UUID) self.assertEqual(res_dict['server']['accessIPv4'], '0.0.0.0') + def test_update_server_access_ipv4_bad_format(self): + self.stubs.Set(nova.db, 'instance_get', + return_server_with_attributes(access_ipv4='0.0.0.0')) + req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req.method = 'PUT' + req.content_type = 'application/json' + body = {'server': {'accessIPv4': 'bad_format'}} + req.body = json.dumps(body) + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, FAKE_UUID, body) + def test_update_server_access_ipv6(self): self.stubs.Set(nova.db, 'instance_get', return_server_with_attributes(access_ipv6='beef::0123')) @@ -1098,6 +1109,17 @@ def test_update_server_access_ipv6(self): self.assertEqual(res_dict['server']['id'], FAKE_UUID) self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') + def test_update_server_access_ipv6_bad_format(self): + self.stubs.Set(nova.db, 'instance_get', + return_server_with_attributes(access_ipv6='beef::0123')) + req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) + req.method = 'PUT' + req.content_type = 'application/json' + body = {'server': {'accessIPv6': 'bad_format'}} + req.body = json.dumps(body) + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, FAKE_UUID, body) + def test_update_server_adminPass_ignored(self): inst_dict = dict(name='server_test', adminPass='bacon') body = dict(server=inst_dict) @@ -1122,6 +1144,80 @@ def server_update(context, id, params): self.assertEqual(res_dict['server']['id'], FAKE_UUID) self.assertEqual(res_dict['server']['name'], 'server_test') + def test_rebuild_instance_with_access_ipv4_bad_format(self): + + def fake_get_instance(*args, **kwargs): + return fakes.stub_instance(1, vm_state=vm_states.ACTIVE) + + self.stubs.Set(nova.db, 'instance_get', fake_get_instance) + # proper local hrefs must start with 'http://localhost/v2/' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v2/fake/images/%s' % image_uuid + access_ipv4 = 'bad_format' + access_ipv6 = 'fead::1234' + body = { + 'rebuild': { + 'name': 'new_name', + 'imageRef': image_href, + 'accessIPv4': access_ipv4, + 'accessIPv6': access_ipv6, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "MQ==", + }, + ], + }, + } + + req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller._action_rebuild, req, 1, body) + + def test_rebuild_instance_with_access_ipv6_bad_format(self): + + def fake_get_instance(*args, **kwargs): + return fakes.stub_instance(1, vm_state=vm_states.ACTIVE) + + self.stubs.Set(nova.db, 'instance_get', fake_get_instance) + # proper local hrefs must start with 'http://localhost/v2/' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v2/fake/images/%s' % image_uuid + access_ipv4 = '1.2.3.4' + access_ipv6 = 'bad_format' + body = { + 'rebuild': { + 'name': 'new_name', + 'imageRef': image_href, + 'accessIPv4': access_ipv4, + 'accessIPv6': access_ipv6, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "MQ==", + }, + ], + }, + } + + req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller._action_rebuild, req, 1, body) + def test_get_all_server_details(self): expected_flavor = { "id": "1", @@ -1571,6 +1667,74 @@ def test_create_instance_with_access_ip(self): self.assertEqual(FLAGS.password_length, len(server['adminPass'])) self.assertEqual(FAKE_UUID, server['id']) + def test_create_instance_bad_format_access_ip_v4(self): + # proper local hrefs must start with 'http://localhost/v2/' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v2/fake/images/%s' % image_uuid + flavor_ref = 'http://localhost/fake/flavors/3' + access_ipv4 = 'bad_format' + access_ipv6 = 'fead::1234' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'accessIPv4': access_ipv4, + 'accessIPv6': access_ipv6, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "MQ==", + }, + ], + }, + } + + req = fakes.HTTPRequest.blank('/v2/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, body) + + def test_create_instance_bad_format_access_ip_v6(self): + # proper local hrefs must start with 'http://localhost/v2/' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v2/fake/images/%s' % image_uuid + flavor_ref = 'http://localhost/fake/flavors/3' + access_ipv4 = '1.2.3.4' + access_ipv6 = 'bad_format' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'accessIPv4': access_ipv4, + 'accessIPv6': access_ipv6, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "MQ==", + }, + ], + }, + } + + req = fakes.HTTPRequest.blank('/v2/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, body) + def test_create_instance(self): # proper local hrefs must start with 'http://localhost/v2/' image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'