summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@gmail.com>2012-11-30 17:03:25 -0800
committerVishvananda Ishaya <vishvananda@gmail.com>2012-12-10 18:04:32 -0800
commita2101c4e7017715af0a29675b89e14ee2884bd89 (patch)
treea1e71cf5ba4af4eb175835ec98bf22be775f4324 /nova/api
parent255692feea3eee12bfc763f75fc8f3dabdbe9ba5 (diff)
downloadnova-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.py34
-rw-r--r--nova/api/metadata/handler.py3
-rw-r--r--nova/api/metadata/password.py44
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()