Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ECS Credentials Provider to add support for AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE #233

Merged
merged 18 commits into from
Apr 18, 2024
1 change: 1 addition & 0 deletions include/aws/auth/auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ enum aws_auth_errors {
AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE,
AWS_AUTH_IMDS_CLIENT_SOURCE_FAILURE,
AWS_AUTH_PROFILE_STS_CREDENTIALS_PROVIDER_CYCLE_FAILURE,
AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_TOKEN_FILE_PATH,
DmitriyMusatkin marked this conversation as resolved.
Show resolved Hide resolved

AWS_AUTH_ERROR_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_AUTH_PACKAGE_ID)
};
Expand Down
16 changes: 13 additions & 3 deletions include/aws/auth/credentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,20 @@ struct aws_credentials_provider_imds_options {
* or via a full uri specified by environment variables:
* AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
* AWS_CONTAINER_CREDENTIALS_FULL_URI
* AWS_CONTAINER_AUTHORIZATION_TOKEN
*
* If both relative uri and absolute uri are set, relative uri
* has higher priority. Token is used in auth header but only for
* absolute uri.
* has higher priority.
*
* Currently, the ECS creds provider doesn't read those environment variables and requires host & path_and_query
* TODO: Support AWS_CONTAINER_CREDENTIALS_RELATIVE_URI and AWS_CONTAINER_CREDENTIALS_FULL_URI
* parameters.
*
* For the Authorization token, there are three ways (in order of priority).
* 1. auth_token parameter
* 2. AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (env var which contains absolute path to the token file. The file will be
* re-read for each call to get credentials.)
* 3. AWS_CONTAINER_AUTHORIZATION_TOKEN (env var which contains static auth token)
*
* While above information is used in request only, endpoint info
* is needed when creating ecs provider to initiate the connection
* manager, more specifically, host and http scheme (tls or not)
Expand Down
5 changes: 4 additions & 1 deletion source/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ static struct aws_error_info s_errors[] = {
"Failed to source the IMDS resource"),
AWS_DEFINE_ERROR_INFO_AUTH(
AWS_AUTH_PROFILE_STS_CREDENTIALS_PROVIDER_CYCLE_FAILURE,
"Failed to resolve credentials because the profile contains a cycle in the assumeRole chain.")
"Failed to resolve credentials because the profile contains a cycle in the assumeRole chain."),
AWS_DEFINE_ERROR_INFO_AUTH(
AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_TOKEN_FILE_PATH,
"Failed to read the ECS token file specified in the AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE environment variable."),
};
/* clang-format on */

Expand Down
15 changes: 1 addition & 14 deletions source/credentials_provider_default_chain.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_relative_uri, "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI");
AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_full_uri, "AWS_CONTAINER_CREDENTIALS_FULL_URI");
AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_token, "AWS_CONTAINER_AUTHORIZATION_TOKEN");
AWS_STATIC_STRING_FROM_LITERAL(s_ecs_host, "169.254.170.2");
AWS_STATIC_STRING_FROM_LITERAL(s_ec2_creds_env_disable, "AWS_EC2_METADATA_DISABLED");

Expand All @@ -41,29 +40,20 @@ static struct aws_credentials_provider *s_aws_credentials_provider_new_ecs_or_im
struct aws_client_bootstrap *bootstrap,
struct aws_tls_ctx *tls_ctx) {

struct aws_byte_cursor auth_token_cursor;
AWS_ZERO_STRUCT(auth_token_cursor);

struct aws_credentials_provider *ecs_or_imds_provider = NULL;
struct aws_string *ecs_relative_uri = NULL;
struct aws_string *ecs_full_uri = NULL;
struct aws_string *ec2_imds_disable = NULL;
struct aws_string *ecs_token = NULL;

if (aws_get_environment_value(allocator, s_ecs_creds_env_relative_uri, &ecs_relative_uri) != AWS_OP_SUCCESS ||
aws_get_environment_value(allocator, s_ecs_creds_env_full_uri, &ecs_full_uri) != AWS_OP_SUCCESS ||
aws_get_environment_value(allocator, s_ec2_creds_env_disable, &ec2_imds_disable) != AWS_OP_SUCCESS ||
aws_get_environment_value(allocator, s_ecs_creds_env_token, &ecs_token) != AWS_OP_SUCCESS) {
aws_get_environment_value(allocator, s_ec2_creds_env_disable, &ec2_imds_disable) != AWS_OP_SUCCESS) {
AWS_LOGF_ERROR(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
"Failed reading environment variables during default credentials provider chain initialization.");
goto clean_up;
}

if (ecs_token && ecs_token->len) {
auth_token_cursor = aws_byte_cursor_from_string(ecs_token);
}

/*
* ToDo: the uri choice logic should be done in the ecs provider init logic. As it stands, it's a nightmare
* to try and use the ecs provider anywhere outside the default chain.
Expand All @@ -79,7 +69,6 @@ static struct aws_credentials_provider *s_aws_credentials_provider_new_ecs_or_im
.host = aws_byte_cursor_from_string(s_ecs_host),
.path_and_query = aws_byte_cursor_from_string(ecs_relative_uri),
.tls_ctx = NULL,
.auth_token = auth_token_cursor,
};
ecs_or_imds_provider = aws_credentials_provider_new_ecs(allocator, &ecs_options);

Expand Down Expand Up @@ -111,7 +100,6 @@ static struct aws_credentials_provider *s_aws_credentials_provider_new_ecs_or_im
.host = uri.host_name,
.path_and_query = path_and_query,
.tls_ctx = aws_byte_cursor_eq_c_str_ignore_case(&(uri.scheme), "HTTPS") ? tls_ctx : NULL,
.auth_token = auth_token_cursor,
.port = uri.port,
};

Expand All @@ -138,7 +126,6 @@ static struct aws_credentials_provider *s_aws_credentials_provider_new_ecs_or_im
aws_string_destroy(ecs_relative_uri);
aws_string_destroy(ecs_full_uri);
aws_string_destroy(ec2_imds_disable);
aws_string_destroy(ecs_token);

return ecs_or_imds_provider;
}
Expand Down
54 changes: 46 additions & 8 deletions source/credentials_provider_ecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
* SPDX-License-Identifier: Apache-2.0.
*/

#include "aws/common/byte_buf.h"
#include <aws/auth/credentials.h>

#include <aws/auth/private/credentials_utils.h>
#include <aws/common/clock.h>
#include <aws/common/date_time.h>
#include <aws/common/environment.h>
#include <aws/common/string.h>
#include <aws/http/connection.h>
#include <aws/http/connection_manager.h>
Expand All @@ -28,13 +30,17 @@
#define ECS_RESPONSE_SIZE_LIMIT 10000
#define ECS_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS 2

AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_token_file, "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE");
AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_token, "AWS_CONTAINER_AUTHORIZATION_TOKEN");

static void s_on_connection_manager_shutdown(void *user_data);

struct aws_credentials_provider_ecs_impl {
struct aws_http_connection_manager *connection_manager;
const struct aws_auth_http_system_vtable *function_table;
struct aws_string *host;
struct aws_string *path_and_query;
struct aws_string *auth_token_file_path;
struct aws_string *auth_token;
};

Expand All @@ -47,6 +53,7 @@ struct aws_credentials_provider_ecs_user_data {
struct aws_credentials_provider *ecs_provider;
aws_on_get_credentials_callback_fn *original_callback;
void *original_user_data;
struct aws_byte_buf auth_token;

/* mutable */
struct aws_http_connection *connection;
Expand All @@ -68,6 +75,7 @@ static void s_aws_credentials_provider_ecs_user_data_destroy(struct aws_credenti
impl->connection_manager, user_data->connection);
}

aws_byte_buf_clean_up(&user_data->auth_token);
aws_byte_buf_clean_up(&user_data->current_result);

if (user_data->request) {
Expand All @@ -84,9 +92,6 @@ static struct aws_credentials_provider_ecs_user_data *s_aws_credentials_provider

struct aws_credentials_provider_ecs_user_data *wrapped_user_data =
aws_mem_calloc(ecs_provider->allocator, 1, sizeof(struct aws_credentials_provider_ecs_user_data));
if (wrapped_user_data == NULL) {
goto on_error;
}

wrapped_user_data->allocator = ecs_provider->allocator;
wrapped_user_data->ecs_provider = ecs_provider;
Expand All @@ -98,12 +103,33 @@ static struct aws_credentials_provider_ecs_user_data *s_aws_credentials_provider
goto on_error;
}

return wrapped_user_data;
struct aws_credentials_provider_ecs_impl *impl = ecs_provider->impl;
if (impl->auth_token_file_path) {
DmitriyMusatkin marked this conversation as resolved.
Show resolved Hide resolved
if (aws_byte_buf_init_from_file(
&wrapped_user_data->auth_token,
ecs_provider->allocator,
aws_string_c_str(impl->auth_token_file_path))) {
AWS_LOGF_ERROR(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
"(id=%p) ECS credentials provider failed to read token from the path: %s with error: %d",
(void *)ecs_provider,
aws_string_c_str(impl->auth_token_file_path),
aws_last_error());
aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_TOKEN_FILE_PATH);
DmitriyMusatkin marked this conversation as resolved.
Show resolved Hide resolved
goto on_error;
}
} else if (impl->auth_token) {
if (aws_byte_buf_init_copy_from_cursor(
&wrapped_user_data->auth_token,
ecs_provider->allocator,
aws_byte_cursor_from_string(impl->auth_token))) {
goto on_error;
}
}

return wrapped_user_data;
on_error:

s_aws_credentials_provider_ecs_user_data_destroy(wrapped_user_data);

return NULL;
}

Expand Down Expand Up @@ -318,10 +344,10 @@ static int s_make_ecs_http_query(
goto on_error;
}

if (impl->auth_token != NULL) {
if (ecs_user_data->auth_token.len) {
struct aws_http_header auth_header = {
.name = aws_byte_cursor_from_string(s_ecs_authorization_header),
.value = aws_byte_cursor_from_string(impl->auth_token),
.value = aws_byte_cursor_from_buf(&ecs_user_data->auth_token),
};
if (aws_http_message_add_header(request, auth_header)) {
goto on_error;
Expand Down Expand Up @@ -462,6 +488,7 @@ static void s_credentials_provider_ecs_destroy(struct aws_credentials_provider *

aws_string_destroy(impl->path_and_query);
aws_string_destroy(impl->auth_token);
aws_string_destroy(impl->auth_token_file_path);
aws_string_destroy(impl->host);

/* aws_http_connection_manager_release will eventually leads to call of s_on_connection_manager_shutdown,
Expand Down Expand Up @@ -567,7 +594,18 @@ struct aws_credentials_provider *aws_credentials_provider_new_ecs(
if (impl->auth_token == NULL) {
goto on_error;
}
} else {
/* read the environment variables */
struct aws_string *ecs_env_token_file_path = NULL;
struct aws_string *ecs_env_token = NULL;
if (aws_get_environment_value(allocator, s_ecs_creds_env_token_file, &ecs_env_token_file_path) ||
aws_get_environment_value(allocator, s_ecs_creds_env_token, &ecs_env_token)) {
goto on_error;
}
impl->auth_token_file_path = ecs_env_token_file_path;
impl->auth_token = ecs_env_token;
}

impl->path_and_query = aws_string_new_from_cursor(allocator, &options->path_and_query);
if (impl->path_and_query == NULL) {
goto on_error;
Expand Down
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ add_test_case(credentials_provider_ecs_connect_failure)
add_test_case(credentials_provider_ecs_request_failure)
add_test_case(credentials_provider_ecs_bad_document_failure)
add_test_case(credentials_provider_ecs_basic_success)
add_test_case(credentials_provider_ecs_basic_success_token_file)
add_test_case(credentials_provider_ecs_basic_success_token_env)
add_test_case(credentials_provider_ecs_basic_success_token_env_with_parameter_token)
add_test_case(credentials_provider_ecs_no_auth_token_success)
add_test_case(credentials_provider_ecs_success_multi_part_doc)
add_test_case(credentials_provider_ecs_real_new_destroy)
Expand Down
Loading
Loading