summaryrefslogtreecommitdiff
path: root/cloudinit/ec2_utils.py
diff options
context:
space:
mode:
authorRyan Harper <ryan.harper@canonical.com>2019-11-22 21:05:44 -0600
committerChad Smith <chad.smith@canonical.com>2019-11-22 20:05:44 -0700
commit4bc399e0cd0b7e9177f948aecd49f6b8323ff30b (patch)
treeceb03a0fcad1a205907554cb87707e12bbf4049d /cloudinit/ec2_utils.py
parent310f8605a5fe62dacf8edc63a809a061085bb907 (diff)
downloadvyos-cloud-init-4bc399e0cd0b7e9177f948aecd49f6b8323ff30b.tar.gz
vyos-cloud-init-4bc399e0cd0b7e9177f948aecd49f6b8323ff30b.zip
ec2: Add support for AWS IMDS v2 (session-oriented) (#55)
* ec2: Add support for AWS IMDS v2 (session-oriented) AWS now supports a new version of fetching Instance Metadata[1]. Update cloud-init's ec2 utility functions and update ec2 derived datasources accordingly. For DataSourceEc2 (versus ec2-look-alikes) cloud-init will issue the PUT request to obtain an API token for the maximum lifetime and then all subsequent interactions with the IMDS will include the token in the header. If the API token endpoint is unreachable on Ec2 platform, log a warning and fallback to using IMDS v1 and which does not use session tokens when communicating with the Instance metadata service. We handle read errors, typically seen if the IMDS is beyond one etwork hop (IMDSv2 responses have a ttl=1), by setting the api token to a disabled value and then using IMDSv1 paths. To support token-based headers, ec2_utils functions were updated to support custom headers_cb and exception_cb callback functions so Ec2 could store, or refresh API tokens in the event of token becoming stale. [1] https://docs.aws.amazon.com/AWSEC2/latest/ \ UserGuide/ec2-instance-metadata.html \ #instance-metadata-v2-how-it-works
Diffstat (limited to 'cloudinit/ec2_utils.py')
-rw-r--r--cloudinit/ec2_utils.py37
1 files changed, 24 insertions, 13 deletions
diff --git a/cloudinit/ec2_utils.py b/cloudinit/ec2_utils.py
index 3b7b17f1..57708c14 100644
--- a/cloudinit/ec2_utils.py
+++ b/cloudinit/ec2_utils.py
@@ -134,25 +134,28 @@ class MetadataMaterializer(object):
return joined
-def _skip_retry_on_codes(status_codes, _request_args, cause):
+def skip_retry_on_codes(status_codes, _request_args, cause):
"""Returns False if cause.code is in status_codes."""
return cause.code not in status_codes
def get_instance_userdata(api_version='latest',
metadata_address='http://169.254.169.254',
- ssl_details=None, timeout=5, retries=5):
+ ssl_details=None, timeout=5, retries=5,
+ headers_cb=None, exception_cb=None):
ud_url = url_helper.combine_url(metadata_address, api_version)
ud_url = url_helper.combine_url(ud_url, 'user-data')
user_data = ''
try:
- # It is ok for userdata to not exist (thats why we are stopping if
- # NOT_FOUND occurs) and just in that case returning an empty string.
- exception_cb = functools.partial(_skip_retry_on_codes,
- SKIP_USERDATA_CODES)
+ if not exception_cb:
+ # It is ok for userdata to not exist (thats why we are stopping if
+ # NOT_FOUND occurs) and just in that case returning an empty
+ # string.
+ exception_cb = functools.partial(skip_retry_on_codes,
+ SKIP_USERDATA_CODES)
response = url_helper.read_file_or_url(
ud_url, ssl_details=ssl_details, timeout=timeout,
- retries=retries, exception_cb=exception_cb)
+ retries=retries, exception_cb=exception_cb, headers_cb=headers_cb)
user_data = response.contents
except url_helper.UrlError as e:
if e.code not in SKIP_USERDATA_CODES:
@@ -165,11 +168,13 @@ def get_instance_userdata(api_version='latest',
def _get_instance_metadata(tree, api_version='latest',
metadata_address='http://169.254.169.254',
ssl_details=None, timeout=5, retries=5,
- leaf_decoder=None):
+ leaf_decoder=None, headers_cb=None,
+ exception_cb=None):
md_url = url_helper.combine_url(metadata_address, api_version, tree)
caller = functools.partial(
url_helper.read_file_or_url, ssl_details=ssl_details,
- timeout=timeout, retries=retries)
+ timeout=timeout, retries=retries, headers_cb=headers_cb,
+ exception_cb=exception_cb)
def mcaller(url):
return caller(url).contents
@@ -191,22 +196,28 @@ def _get_instance_metadata(tree, api_version='latest',
def get_instance_metadata(api_version='latest',
metadata_address='http://169.254.169.254',
ssl_details=None, timeout=5, retries=5,
- leaf_decoder=None):
+ leaf_decoder=None, headers_cb=None,
+ exception_cb=None):
# Note, 'meta-data' explicitly has trailing /.
# this is required for CloudStack (LP: #1356855)
return _get_instance_metadata(tree='meta-data/', api_version=api_version,
metadata_address=metadata_address,
ssl_details=ssl_details, timeout=timeout,
- retries=retries, leaf_decoder=leaf_decoder)
+ retries=retries, leaf_decoder=leaf_decoder,
+ headers_cb=headers_cb,
+ exception_cb=exception_cb)
def get_instance_identity(api_version='latest',
metadata_address='http://169.254.169.254',
ssl_details=None, timeout=5, retries=5,
- leaf_decoder=None):
+ leaf_decoder=None, headers_cb=None,
+ exception_cb=None):
return _get_instance_metadata(tree='dynamic/instance-identity',
api_version=api_version,
metadata_address=metadata_address,
ssl_details=ssl_details, timeout=timeout,
- retries=retries, leaf_decoder=leaf_decoder)
+ retries=retries, leaf_decoder=leaf_decoder,
+ headers_cb=headers_cb,
+ exception_cb=exception_cb)
# vi: ts=4 expandtab