Skip to main content

rusoto_credential/
instance_metadata.rs

1//! The Credentials Provider for an AWS Resource's IAM Role.
2
3use async_trait::async_trait;
4use hyper::Uri;
5use std::time::Duration;
6
7use crate::request::HttpClient;
8use crate::{
9    parse_credentials_from_aws_service, AwsCredentials, CredentialsError, ProvideAwsCredentials,
10};
11
12const AWS_CREDENTIALS_PROVIDER_IP: &str = "169.254.169.254";
13const AWS_CREDENTIALS_PROVIDER_PATH: &str = "latest/meta-data/iam/security-credentials";
14
15/// Provides AWS credentials from a resource's IAM role.
16///
17/// The provider has a default timeout of 30 seconds. While it should work well for most setups,
18/// you can change the timeout using the `set_timeout` method.
19///
20/// # Examples
21///
22/// ```rust
23/// use std::time::Duration;
24///
25/// use rusoto_credential::InstanceMetadataProvider;
26///
27/// let mut provider = InstanceMetadataProvider::new();
28/// // you can overwrite the default timeout like this:
29/// provider.set_timeout(Duration::from_secs(60));
30/// ```
31///
32/// The source location can be changed from the default of 169.254.169.254:
33///
34/// ```rust
35/// use std::time::Duration;
36///
37/// use rusoto_credential::InstanceMetadataProvider;
38///
39/// let mut provider = InstanceMetadataProvider::new();
40/// // you can overwrite the default endpoint like this:
41/// provider.set_ip_addr_with_port("127.0.0.1", "8080");
42/// ```
43#[derive(Clone, Debug)]
44pub struct InstanceMetadataProvider {
45    client: HttpClient,
46    timeout: Duration,
47    metadata_ip_addr: String,
48}
49
50impl InstanceMetadataProvider {
51    /// Create a new provider with the given handle.
52    pub fn new() -> Self {
53        InstanceMetadataProvider {
54            client: HttpClient::new(),
55            timeout: Duration::from_secs(30),
56            metadata_ip_addr: AWS_CREDENTIALS_PROVIDER_IP.to_string(),
57        }
58    }
59
60    /// Set the timeout on the provider to the specified duration.
61    pub fn set_timeout(&mut self, timeout: Duration) {
62        self.timeout = timeout;
63    }
64
65    /// Allow overriding host and port of instance metadata service.
66    pub fn set_ip_addr_with_port(&mut self, ip: &str, port: &str) {
67        self.metadata_ip_addr = format!("{}:{}", ip, port);
68    }
69}
70
71impl Default for InstanceMetadataProvider {
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77#[async_trait]
78impl ProvideAwsCredentials for InstanceMetadataProvider {
79    async fn credentials(&self) -> Result<AwsCredentials, CredentialsError> {
80        let role_name = get_role_name(&self.client, self.timeout, &self.metadata_ip_addr)
81            .await
82            .map_err(|err| CredentialsError {
83                message: format!("Could not get credentials from iam: {}", err.to_string()),
84            })?;
85
86        let cred_str = get_credentials_from_role(
87            &self.client,
88            self.timeout,
89            &role_name,
90            &self.metadata_ip_addr,
91        )
92        .await
93        .map_err(|err| CredentialsError {
94            message: format!("Could not get credentials from iam: {}", err.to_string()),
95        })?;
96
97        parse_credentials_from_aws_service(&cred_str)
98    }
99}
100
101/// Gets the role name to get credentials for using the IAM Metadata Service (169.254.169.254).
102async fn get_role_name(
103    client: &HttpClient,
104    timeout: Duration,
105    ip_addr: &str,
106) -> Result<String, CredentialsError> {
107    let role_name_address = format!("http://{}/{}/", ip_addr, AWS_CREDENTIALS_PROVIDER_PATH);
108    let uri = match role_name_address.parse::<Uri>() {
109        Ok(u) => u,
110        Err(e) => return Err(CredentialsError::new(e)),
111    };
112
113    Ok(client.get(uri, timeout).await?)
114}
115
116/// Gets the credentials for an EC2 Instances IAM Role.
117async fn get_credentials_from_role(
118    client: &HttpClient,
119    timeout: Duration,
120    role_name: &str,
121    ip_addr: &str,
122) -> Result<String, CredentialsError> {
123    let credentials_provider_url = format!(
124        "http://{}/{}/{}",
125        ip_addr, AWS_CREDENTIALS_PROVIDER_PATH, role_name
126    );
127
128    let uri = match credentials_provider_url.parse::<Uri>() {
129        Ok(u) => u,
130        Err(e) => return Err(CredentialsError::new(e)),
131    };
132
133    Ok(client.get(uri, timeout).await?)
134}