Skip to content

Commit

Permalink
Refactor ECSCredentialsProvider Part 1 (#236)
Browse files Browse the repository at this point in the history
  • Loading branch information
waahm7 committed May 6, 2024
1 parent 277c8fb commit 140652a
Show file tree
Hide file tree
Showing 10 changed files with 794 additions and 323 deletions.
80 changes: 57 additions & 23 deletions include/aws/auth/credentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,31 +226,41 @@ struct aws_credentials_provider_imds_options {
};

/*
* Configuration options for the provider that sources credentials from ECS container metadata
*
* ECS creds provider can be used to access creds via either
* relative uri to a fixed endpoint http://169.254.170.2,
* or via a full uri specified by environment variables:
* Configuration options for the provider that sources credentials from ECS container metadata.
* The ECS creds provider can be used to access creds via either a relative URI to a fixed endpoint
* (http://169.254.170.2) or via a full URI specified by environment variables (in order of priority):
* AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
* AWS_CONTAINER_CREDENTIALS_FULL_URI
*
* If both relative uri and absolute uri are set, relative 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)
* from endpoint are needed.
* For the Authorization token, there are two ways (in order of priority):
* 1. AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (an env var which contains the absolute path to the token file. The file
* will be re-read for each call to get credentials.)
* 2. AWS_CONTAINER_AUTHORIZATION_TOKEN (an env var that contains a static auth token)
*/
struct aws_credentials_provider_ecs_environment_options {
struct aws_credentials_provider_shutdown_options shutdown_options;

/*
* (Required)
* Connection bootstrap to use for any network connections made while sourcing credentials
*/
struct aws_client_bootstrap *bootstrap;

/*
* (Required)
* Client TLS context to use when making a query. This will only be used if the AWS_CONTAINER_CREDENTIALS_FULL_URI
* is set and starts with https
*/
struct aws_tls_ctx *tls_ctx;

/* For mocking the http layer in tests, leave NULL otherwise */
struct aws_auth_http_system_vtable *function_table;
};

/*
* Configuration options for the provider that sources credentials from ECS container metadata.
* This options struct doesn't read anything from the environment and requires everything to be explicitly passed in. If
* you need to read properties from the environment, use the `aws_credentials_provider_ecs_environment_options`.
*/
struct aws_credentials_provider_ecs_options {
struct aws_credentials_provider_shutdown_options shutdown_options;
Expand All @@ -271,7 +281,15 @@ struct aws_credentials_provider_ecs_options {
struct aws_byte_cursor path_and_query;

/*
* Authorization token to include in the credentials query
* Authorization token file path to include in the credentials query. The file will be re-read for each call to
* get_credentials.
* This has higher priority than `auth_token`.
*/
struct aws_byte_cursor auth_token_file_path;

/*
* Authorization token to include in the credentials query.
* No effect if `auth_token_file_path` is set.
*/
struct aws_byte_cursor auth_token;

Expand Down Expand Up @@ -996,8 +1014,24 @@ struct aws_credentials_provider *aws_credentials_provider_new_imds(
struct aws_allocator *allocator,
const struct aws_credentials_provider_imds_options *options);

/**
* Creates a provider that sources credentials from the ecs role credentials service and reads the required params from
* environment variables
*
* @param allocator memory allocator to use for all memory allocation
* @param options provider-specific configuration options
*
* @return the newly-constructed credentials provider, or NULL if an error occurred.
*/
AWS_AUTH_API
struct aws_credentials_provider *aws_credentials_provider_new_ecs_from_environment(
struct aws_allocator *allocator,
const struct aws_credentials_provider_ecs_environment_options *options);

/**
* Creates a provider that sources credentials from the ecs role credentials service
* This function doesn't read anything from the environment and requires everything to be explicitly passed in.
* If you need to read properties from the environment, use the `aws_credentials_provider_new_ecs_from_environment`.
*
* @param allocator memory allocator to use for all memory allocation
* @param options provider-specific configuration options
Expand Down
2 changes: 1 addition & 1 deletion source/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static struct aws_error_info s_errors[] = {
"Attempt to sign an http request with an invalid signing configuration"),
AWS_DEFINE_ERROR_INFO_AUTH(
AWS_AUTH_CREDENTIALS_PROVIDER_INVALID_ENVIRONMENT,
"Valid credentials could not be sourced from process environment"),
"Required environment variables could not be sourced from process environment"),
AWS_DEFINE_ERROR_INFO_AUTH(
AWS_AUTH_CREDENTIALS_PROVIDER_INVALID_DELEGATE,
"Valid credentials could not be sourced from the provided vtable"),
Expand Down
2 changes: 1 addition & 1 deletion source/credentials_provider_cognito.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include <inttypes.h>

#define COGNITO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS 5
#define COGNITO_MAX_RETRIES 8
#define COGNITO_MAX_RETRIES 3
#define HTTP_REQUEST_BODY_INITIAL_SIZE 1024
#define HTTP_RESPONSE_BODY_INITIAL_SIZE 4096

Expand Down
101 changes: 22 additions & 79 deletions source/credentials_provider_default_chain.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@
# pragma warning(disable : 4221)
#endif /* _MSC_VER */

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_host, "169.254.170.2");
AWS_STATIC_STRING_FROM_LITERAL(s_ec2_creds_env_disable, "AWS_EC2_METADATA_DISABLED");

/**
Expand All @@ -40,94 +37,40 @@ 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_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;

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_LOGF_ERROR(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
"Failed reading environment variables during default credentials provider chain initialization.");
goto clean_up;
}

/*
* 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.
*/
if (ecs_relative_uri && ecs_relative_uri->len) {
AWS_LOGF_INFO(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
"default chain: ECS credentials provider with relative URI %s will be used to retrieve credentials",
aws_string_c_str(ecs_relative_uri));
struct aws_credentials_provider_ecs_options ecs_options = {
.shutdown_options = *shutdown_options,
.bootstrap = bootstrap,
.host = aws_byte_cursor_from_string(s_ecs_host),
.path_and_query = aws_byte_cursor_from_string(ecs_relative_uri),
.tls_ctx = NULL,
};
ecs_or_imds_provider = aws_credentials_provider_new_ecs(allocator, &ecs_options);

} else if (ecs_full_uri && ecs_full_uri->len) {
struct aws_uri uri;
struct aws_byte_cursor uri_cstr = aws_byte_cursor_from_string(ecs_full_uri);
if (AWS_OP_ERR == aws_uri_init_parse(&uri, allocator, &uri_cstr)) {
AWS_LOGF_ERROR(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
"default chain: failed to parse URI %s during default credentials provider chain initialization: %s",
aws_string_c_str(ecs_full_uri),
aws_error_str(aws_last_error()));
goto clean_up;
}

/* Try to create the ECS provider. This will fail if its environment variables aren't set */
struct aws_credentials_provider_ecs_environment_options ecs_options = {
.shutdown_options = *shutdown_options,
.bootstrap = bootstrap,
.tls_ctx = tls_ctx,
};
struct aws_credentials_provider *ecs_provider =
aws_credentials_provider_new_ecs_from_environment(allocator, &ecs_options);
if (ecs_provider != NULL) {
AWS_LOGF_INFO(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
"default chain: ECS credentials provider with full URI %s will be used to retrieve credentials",
aws_string_c_str(ecs_full_uri));

struct aws_byte_cursor path_and_query = uri.path_and_query;
if (path_and_query.len == 0) {
path_and_query = aws_byte_cursor_from_c_str("/");
}

struct aws_credentials_provider_ecs_options ecs_options = {
.shutdown_options = *shutdown_options,
.bootstrap = bootstrap,
.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,
.port = uri.port,
};
"default chain: ECS credentials provider will be used to retrieve credentials");
return ecs_provider;
}

ecs_or_imds_provider = aws_credentials_provider_new_ecs(allocator, &ecs_options);
aws_uri_clean_up(&uri);
} else if (ec2_imds_disable == NULL || aws_string_eq_c_str_ignore_case(ec2_imds_disable, "false")) {
/* Can we do IMDS? */
struct aws_string *ec2_imds_disable = NULL;
aws_get_environment_value(allocator, s_ec2_creds_env_disable, &ec2_imds_disable);
bool imds_enabled = ec2_imds_disable == NULL || aws_string_eq_c_str_ignore_case(ec2_imds_disable, "false");
aws_string_destroy(ec2_imds_disable);
if (imds_enabled) {
AWS_LOGF_INFO(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
"default chain: IMDS credentials provider will be used to retrieve credentials");
struct aws_credentials_provider_imds_options imds_options = {
.shutdown_options = *shutdown_options,
.bootstrap = bootstrap,
};
ecs_or_imds_provider = aws_credentials_provider_new_imds(allocator, &imds_options);
return aws_credentials_provider_new_imds(allocator, &imds_options);
}

clean_up:
if (ecs_or_imds_provider == NULL) {
AWS_LOGF_INFO(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
"default chain: neither ECS nor IMDS will be used to retrieve credentials");
}

aws_string_destroy(ecs_relative_uri);
aws_string_destroy(ecs_full_uri);
aws_string_destroy(ec2_imds_disable);

return ecs_or_imds_provider;
AWS_LOGF_INFO(
AWS_LS_AUTH_CREDENTIALS_PROVIDER, "default chain: neither ECS nor IMDS will be used to retrieve credentials");
return NULL;
}

struct default_chain_callback_data {
Expand Down
Loading

0 comments on commit 140652a

Please sign in to comment.