diff options
| author | Vishvananda Ishaya <vishvananda@gmail.com> | 2012-11-30 17:03:25 -0800 |
|---|---|---|
| committer | Vishvananda Ishaya <vishvananda@gmail.com> | 2012-12-10 18:04:32 -0800 |
| commit | a2101c4e7017715af0a29675b89e14ee2884bd89 (patch) | |
| tree | a1e71cf5ba4af4eb175835ec98bf22be775f4324 /nova/api | |
| parent | 255692feea3eee12bfc763f75fc8f3dabdbe9ba5 (diff) | |
| download | nova-a2101c4e7017715af0a29675b89e14ee2884bd89.tar.gz nova-a2101c4e7017715af0a29675b89e14ee2884bd89.tar.xz nova-a2101c4e7017715af0a29675b89e14ee2884bd89.zip | |
Allows an instance to post encrypted password
Exposes a new url in openstack metadata with two methods:
GET 169.254.169.254/openstack/latest/password # get password
POST 169.254.169.254/openstack/latest/password # post password
The password can only be set once and will be stored in an
instance_system_metadata value with the key 'password'
Part of blueprint get-password
Change-Id: I4bbee8326a09fe38d6393e9e70f009daae0c6ece
Diffstat (limited to 'nova/api')
| -rw-r--r-- | nova/api/metadata/base.py | 34 | ||||
| -rw-r--r-- | nova/api/metadata/handler.py | 3 | ||||
| -rw-r--r-- | nova/api/metadata/password.py | 44 |
3 files changed, 77 insertions, 4 deletions
diff --git a/nova/api/metadata/base.py b/nova/api/metadata/base.py index b271662b8..1c316bf9c 100644 --- a/nova/api/metadata/base.py +++ b/nova/api/metadata/base.py @@ -24,11 +24,13 @@ import os import posixpath from nova.api.ec2 import ec2utils +from nova.api.metadata import password from nova import block_device from nova import context from nova import db from nova import network from nova.openstack.common import cfg +from nova.openstack.common import timeutils from nova.virt import netutils @@ -57,11 +59,17 @@ VERSIONS = [ '2009-04-04', ] -OPENSTACK_VERSIONS = ["2012-08-10"] +FOLSOM = '2012-08-10' +GRIZZLY = '2013-04-04' +OPENSTACK_VERSIONS = [ + FOLSOM, + GRIZZLY, +] CONTENT_DIR = "content" MD_JSON_NAME = "meta_data.json" UD_NAME = "user_data" +PASS_NAME = "password" class InvalidMetadataVersion(Exception): @@ -128,6 +136,13 @@ class InstanceMetadata(): for item in instance.get('metadata', []): self.launch_metadata[item['key']] = item['value'] + self.password = '' + # get password if set + for item in instance.get('system_metadata', []): + if item['key'] == 'password': + self.password = item['value'] or '' + break + self.uuid = instance.get('uuid') self.content = {} @@ -257,6 +272,8 @@ class InstanceMetadata(): ret = [MD_JSON_NAME] if self.userdata_raw is not None: ret.append(UD_NAME) + if self._check_os_version(GRIZZLY, version): + ret.append(PASS_NAME) return ret if path == UD_NAME: @@ -264,6 +281,9 @@ class InstanceMetadata(): raise KeyError(path) return self.userdata_raw + if path == PASS_NAME and self._check_os_version(GRIZZLY, version): + return password.handle_password + if path != MD_JSON_NAME: raise KeyError(path) @@ -303,8 +323,11 @@ class InstanceMetadata(): return data[path] - def _check_version(self, required, requested): - return VERSIONS.index(requested) >= VERSIONS.index(required) + def _check_version(self, required, requested, versions=VERSIONS): + return versions.index(requested) >= versions.index(required) + + def _check_os_version(self, required, requested): + return self._check_version(required, requested, OPENSTACK_VERSIONS) def _get_hostname(self): return "%s%s%s" % (self.instance['hostname'], @@ -332,7 +355,10 @@ class InstanceMetadata(): # specifically handle the top level request if len(path_tokens) == 1: if path_tokens[0] == "openstack": - versions = OPENSTACK_VERSIONS + ["latest"] + # NOTE(vish): don't show versions that are in the future + today = timeutils.utcnow().strftime("%Y-%m-%d") + versions = [v for v in OPENSTACK_VERSIONS if v <= today] + versions += ["latest"] else: versions = VERSIONS + ["latest"] return versions diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py index 06fdce30e..b164c5fea 100644 --- a/nova/api/metadata/handler.py +++ b/nova/api/metadata/handler.py @@ -120,6 +120,9 @@ class MetadataRequestHandler(wsgi.Application): except base.InvalidMetadataPath: raise webob.exc.HTTPNotFound() + if callable(data): + return data(req, meta_data) + return base.ec2_md_print(data) def _handle_remote_ip_request(self, req): diff --git a/nova/api/metadata/password.py b/nova/api/metadata/password.py new file mode 100644 index 000000000..3cda67eee --- /dev/null +++ b/nova/api/metadata/password.py @@ -0,0 +1,44 @@ +# Copyright 2012 Nebula, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from webob import exc + +from nova import context +from nova import db + + +MAX_SIZE = 256 + + +def handle_password(req, meta_data): + ctxt = context.get_admin_context() + password = meta_data.password + if req.method == 'GET': + return meta_data.password + elif req.method == 'POST': + # NOTE(vish): The conflict will only happen once the metadata cache + # updates, but it isn't a huge issue if it can be set for + # a short window. + if meta_data.password: + raise exc.HTTPConflict() + if (req.content_length > MAX_SIZE or len(req.body) > MAX_SIZE): + msg = _("Request is too large.") + raise exc.HTTPBadRequest(explanation=msg) + db.instance_system_metadata_update(ctxt, + meta_data.uuid, + {'password': req.body}, + False) + else: + raise exc.HTTPBadRequest() |
