Skip to content

Commit

Permalink
Fix STS Credentials Provider to use regional endpoint if possible (#237)
Browse files Browse the repository at this point in the history
  • Loading branch information
waahm7 committed May 9, 2024
1 parent 140652a commit ef9cfa1
Show file tree
Hide file tree
Showing 9 changed files with 519 additions and 129 deletions.
32 changes: 28 additions & 4 deletions include/aws/auth/credentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,14 +360,14 @@ struct aws_credentials_provider_x509_options {
---------------------------------------------------------------------------------
| Parameter | Environment Variable Name | Config File Property Name |
----------------------------------------------------------------------------------
| region | AWS_DEFAULT_REGION | region |
| region | AWS_REGION/AWS_DEFAULT_REGION| region |
| role_arn | AWS_ROLE_ARN | role_arn |
| role_session_name | AWS_ROLE_SESSION_NAME | role_session_name |
| token_file_path | AWS_WEB_IDENTITY_TOKEN_FILE | web_identity_token_file |
|--------------------------------------------------------------------------------|
* The order of resolution is the following
* 1. Parameters
* 2. Environment Variables
* 2. Environment Variables (in case of region, the AWS_REGION is preferred over the AWS_DEFAULT_REGION)
* 3. Config File
*/
struct aws_credentials_provider_sts_web_identity_options {
Expand Down Expand Up @@ -465,7 +465,12 @@ struct aws_credentials_provider_sso_options {
};

/**
* Configuration options for the STS credentials provider
* Configuration options for the STS credentials provider.
* STS Credentials Provider will try to automatically resolve the region and use a regional STS endpoint if successful.
* The region resolution order is the following:
* 1. AWS_REGION environment variable
* 2. AWS_DEFAULT_REGION environment variable
* 3. The region property in the config file.
*/
struct aws_credentials_provider_sts_options {
/*
Expand Down Expand Up @@ -504,6 +509,26 @@ struct aws_credentials_provider_sts_options {
*/
const struct aws_http_proxy_options *http_proxy_options;

/**
* (Optional)
* Uses a cached config file profile collection (~/.aws/config). You can also pass a merged profile collection,
* which contains both a config file and a credentials file.
* If provided, config_file_name_override is ignored.
*/
struct aws_profile_collection *profile_collection_cached;

/*
* (Optional)
* Override of what profile to use; if not set, 'default' will be used.
*/
struct aws_byte_cursor profile_name_override;

/*
* (Optional)
* Override path to the profile config file (~/.aws/config by default).
*/
struct aws_byte_cursor config_file_name_override;

struct aws_credentials_provider_shutdown_options shutdown_options;

/* For mocking, leave NULL otherwise */
Expand Down Expand Up @@ -573,7 +598,6 @@ struct aws_credentials_provider_chain_default_options {
* Use a cached merged profile collection. A merge collection has both config file
* (~/.aws/config) and credentials file based profile collection (~/.aws/credentials) using
* `aws_profile_collection_new_from_merge`.
* If this option is provided, `config_file_name_override` and `credentials_file_name_override` will be ignored.
*/
struct aws_profile_collection *profile_collection_cached;

Expand Down
20 changes: 20 additions & 0 deletions include/aws/auth/private/credentials_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct aws_http_connection_manager;
struct aws_http_make_request_options;
struct aws_http_stream;
struct aws_json_value;
struct aws_profile;

/*
* Internal struct tracking an asynchronous credentials query.
Expand Down Expand Up @@ -164,6 +165,17 @@ struct aws_credentials *aws_parse_credentials_from_json_document(
AWS_AUTH_API
enum aws_retry_error_type aws_credentials_provider_compute_retry_error_type(int response_code, int error_code);

/*
* Constructs an endpoint in the format of service_name.region.amazonaws.com
* If the region is cn-north-1 or cn-northwest-1, .cn is appended to support China-specific regional endpoints.
*/
AWS_AUTH_API
int aws_credentials_provider_construct_regional_endpoint(
struct aws_allocator *allocator,
struct aws_string **out_endpoint,
const struct aws_string *region,
const struct aws_string *service_name);

/*
* Loads an aws config profile collection
*/
Expand All @@ -172,6 +184,14 @@ struct aws_profile_collection *aws_load_profile_collection_from_config_file(
struct aws_allocator *allocator,
struct aws_byte_cursor config_file_name_override);

/*
* Resolve region from environment in the following order:
* 1. AWS_REGION
* 2. AWS_DEFAULT_REGION
*/
AWS_AUTH_API
struct aws_string *aws_credentials_provider_resolve_region_from_env(struct aws_allocator *allocator);

AWS_EXTERN_C_END

#endif /* AWS_AUTH_CREDENTIALS_PRIVATE_H */
3 changes: 3 additions & 0 deletions source/credentials_provider_profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ static struct aws_credentials_provider *s_create_sts_based_provider(
.tls_ctx = tls_ctx,
.role_arn = aws_byte_cursor_from_string(aws_profile_property_get_value(role_arn_property)),
.session_name = aws_byte_cursor_from_c_str(session_name_array),
.profile_name_override = options->profile_name_override,
.config_file_name_override = options->config_file_name_override,
.profile_collection_cached = options->profile_collection_cached,
.duration_seconds = 0,
.function_table = options->function_table,
};
Expand Down
47 changes: 7 additions & 40 deletions source/credentials_provider_sso.c
Original file line number Diff line number Diff line change
Expand Up @@ -534,41 +534,7 @@ static struct aws_credentials_provider_vtable s_aws_credentials_provider_sso_vta
.get_credentials = s_credentials_provider_sso_get_credentials,
.destroy = s_credentials_provider_sso_destroy,
};

static int s_construct_sso_portal_endpoint(
struct aws_allocator *allocator,
struct aws_byte_buf *out_endpoint,
const struct aws_string *region) {
AWS_PRECONDITION(allocator);
AWS_PRECONDITION(out_endpoint);

if (!region) {
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}
aws_byte_buf_clean_up(out_endpoint);
struct aws_byte_cursor sso_prefix = aws_byte_cursor_from_c_str("portal.sso.");
struct aws_byte_cursor region_cursor = aws_byte_cursor_from_string(region);
struct aws_byte_cursor amazonaws_cursor = aws_byte_cursor_from_c_str(".amazonaws.com");
struct aws_byte_cursor cn_cursor = aws_byte_cursor_from_c_str(".cn");

if (aws_byte_buf_init_copy_from_cursor(out_endpoint, allocator, sso_prefix) ||
aws_byte_buf_append_dynamic(out_endpoint, &region_cursor) ||
aws_byte_buf_append_dynamic(out_endpoint, &amazonaws_cursor)) {
goto on_error;
}

if (aws_string_eq_c_str_ignore_case(region, "cn-north-1") ||
aws_string_eq_c_str_ignore_case(region, "cn-northwest-1")) {
if (aws_byte_buf_append_dynamic(out_endpoint, &cn_cursor)) {
goto on_error;
}
}
return AWS_OP_SUCCESS;

on_error:
aws_byte_buf_clean_up(out_endpoint);
return AWS_OP_ERR;
}
AWS_STATIC_STRING_FROM_LITERAL(s_sso_service_name, "portal.sso");

AWS_STATIC_STRING_FROM_LITERAL(s_sso_account_id, "sso_account_id");
AWS_STATIC_STRING_FROM_LITERAL(s_sso_region, "sso_region");
Expand All @@ -577,7 +543,7 @@ AWS_STATIC_STRING_FROM_LITERAL(s_sso_session, "sso_session");

struct sso_parameters {
struct aws_allocator *allocator;
struct aws_byte_buf endpoint;
struct aws_string *endpoint;
struct aws_string *sso_account_id;
struct aws_string *sso_role_name;
struct aws_credentials_provider *token_provider;
Expand All @@ -587,7 +553,7 @@ static void s_parameters_destroy(struct sso_parameters *parameters) {
if (!parameters) {
return;
}
aws_byte_buf_clean_up(&parameters->endpoint);
aws_string_destroy(parameters->endpoint);
aws_string_destroy(parameters->sso_account_id);
aws_string_destroy(parameters->sso_role_name);
aws_credentials_provider_release(parameters->token_provider);
Expand Down Expand Up @@ -716,7 +682,8 @@ static struct sso_parameters *s_parameters_new(
parameters->sso_account_id = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_account_id));
parameters->sso_role_name = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_role_name));
/* determine endpoint */
if (s_construct_sso_portal_endpoint(allocator, &parameters->endpoint, aws_profile_property_get_value(sso_region))) {
if (aws_credentials_provider_construct_regional_endpoint(
allocator, &parameters->endpoint, aws_profile_property_get_value(sso_region), s_sso_service_name)) {
AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to construct sso endpoint");
goto on_finish;
}
Expand Down Expand Up @@ -776,7 +743,7 @@ struct aws_credentials_provider *aws_credentials_provider_new_sso(
}

aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx);
struct aws_byte_cursor host = aws_byte_cursor_from_buf(&parameters->endpoint);
struct aws_byte_cursor host = aws_byte_cursor_from_string(parameters->endpoint);
if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) {
AWS_LOGF_ERROR(
AWS_LS_AUTH_CREDENTIALS_PROVIDER,
Expand Down Expand Up @@ -816,7 +783,7 @@ struct aws_credentials_provider *aws_credentials_provider_new_sso(
}

impl->token_provider = aws_credentials_provider_acquire(parameters->token_provider);
impl->endpoint = aws_string_new_from_buf(allocator, &parameters->endpoint);
impl->endpoint = aws_string_new_from_string(allocator, parameters->endpoint);
impl->sso_account_id = aws_string_new_from_string(allocator, parameters->sso_account_id);
impl->sso_role_name = aws_string_new_from_string(allocator, parameters->sso_role_name);

Expand Down

0 comments on commit ef9cfa1

Please sign in to comment.