/******************************************************************************* * Copyright Amazon.com, Inc. or its affiliates. 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. A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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. * ***************************************************************************** * __ _ _ ___ * ( )( \/\/ )/ __) * /__\ \ / \__ \ * (_)(_) \/\/ (___/ * * AWS SDK for .NET */ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Threading; using Amazon.Runtime; using ThirdParty.Json.LitJson; using System.Globalization; using Amazon.Runtime.Internal.Util; using Amazon.Util.Internal; using Amazon.Util; namespace Amazon.EC2.Util { /// /// EC2 Instance Metadata. /// If this class is used on a non-EC2 instance, the properties in this class /// will return null. /// /// /// /// Amazon EC2 instances can access instance-specific metadata, as well as data supplied when launching the instances, using a specific URI. /// /// /// You can use this data to build more generic AMIs that can be modified by configuration files supplied at launch time. /// For example, if you run web servers for various small businesses, they can all use the same AMI and retrieve their content from the /// Amazon S3 bucket you specify at launch. To add a new customer at any time, simply create a bucket for the customer, add their content, /// and launch your AMI. /// /// /// More information about EC2 Metadata /// /// [Obsolete("This class is deprecated and will be removed in a future release." + " Please update your code to use the Amazon.Util.EC2InstanceMetadata class, located in the AWSSDK.Core assembly.")] public static class EC2Metadata { private static string EC2_METADATA_SVC = "http://169.254.169.254", EC2_METADATA_ROOT = EC2_METADATA_SVC + "/latest/meta-data", EC2_USERDATA_ROOT = EC2_METADATA_SVC + "/latest/user-data/", EC2_APITOKEN_URL = EC2_METADATA_SVC + "latest/api/token"; private static int DEFAULT_RETRIES = 3, MIN_PAUSE_MS = 250, MAX_RETRIES = 3; private static Dictionary _cache = new Dictionary(); private static readonly string _userAgent = InternalSDKUtils.BuildUserAgentString(string.Empty); /// /// The AMI ID used to launch the instance. /// public static string AmiId { get { return FetchData("/ami-id"); } } /// /// The index of this instance in the reservation. /// public static string AmiLaunchIndex { get { return FetchData("/ami-launch-index"); } } /// /// The manifest path of the AMI with which the instance was launched. /// public static string AmiManifestPath { get { return FetchData("/ami-manifest-path"); } } /// /// The AMI IDs of any instances that were rebundled to create this AMI. /// Will only exist if the AMI manifest file contained an ancestor-amis key. /// public static IEnumerable AncestorAmiIds { get { return GetItems("/ancestor-ami-ids"); } } /// /// The private hostname of the instance. /// In cases where multiple network interfaces are present, /// this refers to the eth0 device (the device for which the device number is 0). /// public static string Hostname { get { return FetchData("/hostname"); } } /// /// Notifies the instance that it should reboot in preparation for bundling. /// Valid values: none | shutdown | bundle-pending. /// public static string InstanceAction { get { return FetchData("/instance-action"); } } /// /// The ID of this instance. /// public static string InstanceId { get { return FetchData("/instance-id"); } } /// /// The type of instance. /// public static string InstanceType { get { return FetchData("/instance-type"); } } /// /// The ID of the kernel launched with this instance, if applicable. /// public static string KernelId { get { return GetData("kernel-id"); } } /// /// The local hostname of the instance. In cases where multiple network interfaces are present, /// this refers to the eth0 device (the device for which device-number is 0). /// public static string LocalHostname { get { return FetchData("/local-hostname"); } } /// /// The instance's MAC address. In cases where multiple network interfaces are present, /// this refers to the eth0 device (the device for which device-number is 0). /// public static string MacAddress { get { return FetchData("/mac"); } } /// /// The private IP address of the instance. In cases where multiple network interfaces are present, /// this refers to the eth0 device (the device for which device-number is 0). /// public static string PrivateIpAddress { get { return FetchData("/local-ipv4"); } } /// /// The Availability Zone in which the instance launched. /// public static string AvailabilityZone { get { return FetchData("/placement/availability-zone"); } } /// /// Product codes associated with the instance, if any. /// public static IEnumerable ProductCodes { get { return GetItems("/product-codes"); } } /// /// Public key. Only available if supplied at instance launch time. /// public static string PublicKey { get { return FetchData("/public-keys/0/openssh-key"); } } /// /// The ID of the RAM disk specified at launch time, if applicable. /// public static string RamdiskId { get { return FetchData("/ramdisk-id"); } } /// /// ID of the reservation. /// public static string ReservationId { get { return FetchData("/reservation-id"); } } /// /// The names of the security groups applied to the instance. /// public static IEnumerable SecurityGroups { get { return GetItems("/security-groups"); } } /// /// Returns information about the last time the instance profile was updated, /// including the instance's LastUpdated date, InstanceProfileArn, and InstanceProfileId. /// public static IAMInfo IAMInstanceProfileInfo { get { var json = GetData("/iam/info"); if (null == json) return null; IAMInfo info; try { info = JsonMapper.ToObject(json); } catch { info = new IAMInfo { Code = "Failed", Message = "Could not parse response from metadata service." }; } return info; } } /// /// Returns the temporary security credentials (AccessKeyId, SecretAccessKey, SessionToken, and Expiration) /// associated with the IAM roles on the instance. /// public static IDictionary IAMSecurityCredentials { get { var list = GetItems("/iam/security-credentials"); if (list == null) return null; var creds = new Dictionary(); foreach (var item in list) { var json = GetData("/iam/security-credentials/" + item); try { var cred = JsonMapper.ToObject(json); creds[item] = cred; } catch { creds[item] = new IAMSecurityCredential { Code = "Failed", Message = "Could not parse response from metadata service." }; } } return creds; } } /// /// The virtual devices associated with the ami, root, ebs, and swap. /// public static IDictionary BlockDeviceMapping { get { var keys = GetItems("/block-device-mapping"); if (keys == null) return null; var mapping = new Dictionary(); foreach (var key in keys) { mapping[key] = GetData("/block-device-mapping/" + key); } return mapping; } } /// /// The network interfaces on the instance. /// public static IEnumerable NetworkInterfaces { get { var macs = GetItems("/network/interfaces/macs/"); if (macs == null) return null; var interfaces = new List(); foreach (var mac in macs) { interfaces.Add(new NetworkInterface(mac.Trim('/'))); } return interfaces; } } /// /// The metadata sent to the instance. /// public static string UserData { get { return GetData(EC2_USERDATA_ROOT); } } /// /// Return the list of items in the metadata at path. /// /// Path at which to query the metadata /// List of items returned by the metadata service public static IEnumerable GetItems(string path) { return GetItems(path, DEFAULT_RETRIES, false); } /// /// Return the metadata at the path /// /// Path at which to query the metadata /// Data returned by the metadata service public static string GetData(string path) { return GetData(path, DEFAULT_RETRIES); } /// /// Return the metadata at the path /// /// Path at which to query the metadata /// Number of attempts to make /// Data returned by the metadata service public static string GetData(string path, int tries) { var items = GetItems(path, tries, true); if (items != null && items.Count > 0) return items[0]; return null; } /// /// Return the list of items in the metadata at path. /// /// Path at which to query the metadata /// Number of attempts to make /// List of items returned by the metadata service public static IEnumerable GetItems(string path, int tries) { return GetItems(path, tries, false); } private static string FetchData(string path) { return FetchData(path, false); } private static string FetchData(string path, bool force) { try { if (force || !_cache.ContainsKey(path)) _cache[path] = GetData(path); return _cache[path]; } catch { return null; } } private static List GetItems(string path, int tries, bool slurp) { return GetItems(path, tries, slurp, null); } private static List GetItems(string path, int tries, bool slurp, string token) { var items = new List(); //For all meta-data queries we need to fetch an api token to use. In the event a //token cannot be obtained we will fallback to not using a token. Dictionary headers = null; if (token == null) { token = Amazon.Util.EC2InstanceMetadata.FetchApiToken(); } if (!string.IsNullOrEmpty(token)) { headers = new Dictionary(); headers.Add(HeaderKeys.XAwsEc2MetadataToken, token); } try { if (!Amazon.Util.EC2InstanceMetadata.IsIMDSEnabled) { throw new IMDSDisabledException(); } HttpWebRequest request; if (path.StartsWith("http", StringComparison.Ordinal)) request = WebRequest.Create(path) as HttpWebRequest; else request = WebRequest.Create(EC2_METADATA_ROOT + path) as HttpWebRequest; request.Timeout = (int)TimeSpan.FromSeconds(5).TotalMilliseconds; request.UserAgent = _userAgent; if(headers != null) { foreach(var header in headers) { request.Headers.Add(header.Key, header.Value); } } using (var response = request.GetResponse()) { using (var stream = new StreamReader(response.GetResponseStream())) { if (slurp) items.Add(stream.ReadToEnd()); else { string line; do { line = stream.ReadLine(); if (line != null) items.Add(line.Trim()); } while (line != null); } } } } catch (WebException wex) { var response = wex.Response as HttpWebResponse; if (response != null) { if (response.StatusCode == HttpStatusCode.NotFound) { return null; } else if (response.StatusCode == HttpStatusCode.Unauthorized) { EC2InstanceMetadata.ClearTokenFlag(); Logger.GetLogger(typeof(Amazon.EC2.Util.EC2Metadata)).Error(wex, "EC2 Metadata service returned unauthorized for token based secure data flow."); throw; } } if (tries <= 1) { Logger.GetLogger(typeof(Amazon.EC2.Util.EC2Metadata)).Error(wex, "Unable to contact EC2 Metadata service."); return null; } PauseExponentially(tries); return GetItems(path, tries - 1, slurp, token); } catch (IMDSDisabledException) { // Keep this behavior identical to when HttpStatusCode.NotFound is returned. return null; } return items; } private static void PauseExponentially(int tries) { tries = Math.Min(tries, MAX_RETRIES); var pause = (int)(Math.Pow(2, DEFAULT_RETRIES - tries) * MIN_PAUSE_MS); Thread.Sleep(pause < MIN_PAUSE_MS ? MIN_PAUSE_MS : pause); } #if !NETSTANDARD [Serializable] #endif private class IMDSDisabledException : InvalidOperationException { }; } /// /// Returns information about the last time the instance profile was updated, /// including the instance's LastUpdated date, InstanceProfileArn, and InstanceProfileId. /// [Obsolete("This class is deprecated and will be removed in a future release." + " Please update your code to use the Amazon.Util.IAMInstanceProfileMetadata class, located in the AWSSDK.Core assembly.")] public class IAMInfo { /// /// The status of the instance profile /// public string Code { get; set; } /// /// Further information about the status of the instance profile /// public string Message { get; set; } /// /// The date and time the instance profile was updated /// public DateTime LastUpdated { get; set; } /// /// The Amazon Resource Name (ARN) of the instance profile /// public string InstanceProfileArn { get; set; } /// /// The Id of the instance profile /// public string InstanceProfileId { get; set; } } /// /// The temporary security credentials (AccessKeyId, SecretAccessKey, SessionToken, and Expiration) associated with the IAM role. /// [Obsolete("This class is deprecated and will be removed in a future release." + " Please update your code to use the Amazon.Util.IAMSecurityCredentialMetadata class, located in the AWSSDK.Core assembly.")] public class IAMSecurityCredential { /// /// The status of the security credential /// public string Code { get; set; } /// /// Further information about the status of the instance profile /// public string Message { get; set; } /// /// The date and time the security credential was last updated /// public DateTime LastUpdated { get; set; } /// /// The type of the security credential /// public string Type { get; set; } /// /// The uniqe id of the security credential /// public string AccessKeyId { get; set; } /// /// The secret key used to sign requests /// public string SecretAccessKey { get; set; } /// /// The security token /// public string Token { get; set; } /// /// The date and time when these credentials expire /// public DateTime Expiration { get; set; } } /// /// All of the metadata associated with a network interface on the instance. /// [Obsolete("This class is deprecated and will be removed in a future release." + " Please update your code to use the Amazon.Util.NetworkInterfaceMetadata class, located in the AWSSDK.Core assembly.")] public class NetworkInterface { private string _path; private string _mac; private IEnumerable _availableKeys; private Dictionary _data = new Dictionary(); private NetworkInterface() { } /// /// Construct an instance of NetworkInterface /// /// public NetworkInterface(string macAddress) { _mac = macAddress; _path = string.Format(CultureInfo.InvariantCulture, "/network/interfaces/macs/{0}/", _mac); } /// /// The interface's Media Access Control (mac) address. /// public string MacAddress { get { return _mac; } } /// /// The ID of the owner of the network interface. /// /// /// In multiple-interface environments, an interface can be attached by a third party, such as Elastic Load Balancing. /// Traffic on an interface is always billed to the interface owner. /// public string OwnerId { get { return GetData("owner-id"); } } /// /// The interface's profile /// public string Profile { get { return GetData("profile"); } } /// /// The interface's local hostname. /// public string LocalHostname { get { return GetData("local-hostname"); } } /// /// The private IP addresses associated with the interface. /// public IEnumerable LocalIPv4s { get { return GetItems("local-ipv4s"); } } /// /// The interface's public hostname. /// public string PublicHostname { get { return GetData("public-hostname"); } } /// /// The elastic IP addresses associated with the interface. /// /// /// There may be multiple IP addresses on an instance. /// public IEnumerable PublicIPv4s { get { return GetItems("public-ipv4s"); } } /// /// Security groups to which the network interface belongs. /// public IEnumerable SecurityGroups { get { return GetItems("security-groups"); } } /// /// IDs of the security groups to which the network interface belongs. Returned only for Amazon EC2 instances launched into a VPC. /// public IEnumerable SecurityGroupIds { get { return GetItems("security-group-ids"); } } /// /// The ID of the Amazon EC2-VPC subnet in which the interface resides. /// /// /// Returned only for Amazon EC2 instances launched into a VPC. /// public string SubnetId { get { return GetData("subnet-id"); } } /// /// The CIDR block of the Amazon EC2-VPC subnet in which the interface resides. /// /// /// Returned only for Amazon EC2 instances launched into a VPC. /// public string SubnetIPv4CidrBlock { get { return GetData("subnet-ipv4-cidr-block"); } } /// /// The CIDR block of the Amazon EC2-VPC subnet in which the interface resides. /// /// /// Returned only for Amazon EC2 instances launched into a VPC. /// public string VpcId { get { return GetData("vpc-id"); } } /// /// Get the private IPv4 address(es) that are associated with the public-ip address and assigned to that interface. /// /// The public IP address /// Private IPv4 address(es) associated with the public IP address public IEnumerable GetIpV4Association(string publicIp) { return EC2Metadata.GetItems(string.Format(CultureInfo.InvariantCulture, "{0}ipv4-associations/{1}", _path, publicIp)); } private string GetData(string key) { if (_data.ContainsKey(key)) return _data[key]; // Since the keys are variable, cache a list of which ones are available // to prevent unnecessary trips to the service. if (null == _availableKeys) _availableKeys = EC2Metadata.GetItems(_path); if (_availableKeys.Contains(key)) { _data[key] = EC2Metadata.GetData(_path + key); return _data[key]; } else return null; } private IEnumerable GetItems(string key) { if (null == _availableKeys) _availableKeys = EC2Metadata.GetItems(_path); if (_availableKeys.Contains(key)) { return EC2Metadata.GetItems(_path + key); } else return new List(); } } }