/*******************************************************************************
* 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();
}
}
}