From 9ae6ec40868047feae4baf98693abd7ee1b3638a Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 8 May 2024 11:38:40 -0700 Subject: [PATCH] Add support for AWS_REGION as well --- include/aws/auth/credentials.h | 12 ++- include/aws/auth/private/credentials_utils.h | 8 ++ source/credentials_provider_sts.c | 14 ++- .../credentials_provider_sts_web_identity.c | 9 +- source/credentials_utils.c | 16 ++++ tests/CMakeLists.txt | 1 + tests/credentials_provider_sts_tests.c | 91 ++++++++++++++++++- 7 files changed, 137 insertions(+), 14 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index c7c4e296..71c69821 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -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, AWS_REGION is preferred over AWS_DEFAULT_REGION) * 3. Config File */ struct aws_credentials_provider_sts_web_identity_options { @@ -465,8 +465,14 @@ 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 { /* * Connection bootstrap to use for any network connections made while sourcing credentials diff --git a/include/aws/auth/private/credentials_utils.h b/include/aws/auth/private/credentials_utils.h index 32fd2096..d2d17d41 100644 --- a/include/aws/auth/private/credentials_utils.h +++ b/include/aws/auth/private/credentials_utils.h @@ -184,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 */ diff --git a/source/credentials_provider_sts.c b/source/credentials_provider_sts.c index cedfeb65..0aa56bdf 100644 --- a/source/credentials_provider_sts.c +++ b/source/credentials_provider_sts.c @@ -671,27 +671,25 @@ static struct aws_credentials_provider_vtable s_aws_credentials_provider_sts_vta }; AWS_STATIC_STRING_FROM_LITERAL(s_region_config, "region"); -AWS_STATIC_STRING_FROM_LITERAL(s_region_env, "AWS_DEFAULT_REGION"); /* * Try to resolve the region in the following order + * 1. Check `AWS_REGION` environment variable * 1. Check `AWS_DEFAULT_REGION` environment variable * 2. check `region` config file property. */ static struct aws_string *s_resolve_region( struct aws_allocator *allocator, const struct aws_credentials_provider_sts_options *options) { - struct aws_profile_collection *profile_collection = NULL; - struct aws_string *region = NULL; - struct aws_string *profile_name = NULL; - - /* check environment variable */ - aws_get_environment_value(allocator, s_region_env, ®ion); - + /* check environment variable first */ + struct aws_string *region = aws_credentials_provider_resolve_region_from_env(allocator); if (region != NULL && region->len > 0) { return region; } + struct aws_profile_collection *profile_collection = NULL; + struct aws_string *profile_name = NULL; + /* check the config file */ if (options->profile_collection_cached) { profile_collection = aws_profile_collection_acquire(options->profile_collection_cached); diff --git a/source/credentials_provider_sts_web_identity.c b/source/credentials_provider_sts_web_identity.c index 91e0acca..78314bf2 100644 --- a/source/credentials_provider_sts_web_identity.c +++ b/source/credentials_provider_sts_web_identity.c @@ -808,7 +808,6 @@ static void s_on_connection_manager_shutdown(void *user_data) { } AWS_STATIC_STRING_FROM_LITERAL(s_region_config, "region"); -AWS_STATIC_STRING_FROM_LITERAL(s_region_env, "AWS_DEFAULT_REGION"); AWS_STATIC_STRING_FROM_LITERAL(s_role_arn_config, "role_arn"); AWS_STATIC_STRING_FROM_LITERAL(s_role_arn_env, "AWS_ROLE_ARN"); AWS_STATIC_STRING_FROM_LITERAL(s_role_session_name_config, "role_session_name"); @@ -956,7 +955,13 @@ static struct sts_web_identity_parameters *s_parameters_new( parameters->allocator = allocator; bool success = false; - struct aws_string *region = s_check_or_get_with_env(allocator, s_region_env, options->region); + struct aws_string *region = NULL; + if (options->region.len > 0) { + region = aws_string_new_from_cursor(allocator, &options->region); + } else { + region = aws_credentials_provider_resolve_region_from_env(allocator); + } + struct aws_string *role_arn = s_check_or_get_with_env(allocator, s_role_arn_env, options->role_arn); struct aws_string *role_session_name = s_check_or_get_with_env(allocator, s_role_session_name_env, options->role_session_name); diff --git a/source/credentials_utils.c b/source/credentials_utils.c index 8282c3a0..07903594 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -399,3 +399,19 @@ int aws_credentials_provider_construct_regional_endpoint( aws_byte_buf_clean_up(out_endpoint); return AWS_OP_ERR; } + +AWS_STATIC_STRING_FROM_LITERAL(s_region_env, "AWS_REGION"); +AWS_STATIC_STRING_FROM_LITERAL(s_default_region_env, "AWS_DEFAULT_REGION"); + +struct aws_string *aws_credentials_provider_resolve_region_from_env(struct aws_allocator *allocator) { + struct aws_string *region = NULL; + + /* check AWS_REGION environment variable first */ + aws_get_environment_value(allocator, s_region_env, ®ion); + if (region != NULL && region->len > 0) { + return region; + } + + aws_get_environment_value(allocator, s_default_region_env, ®ion); + return region; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8986df13..50d8d574 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,6 +85,7 @@ add_net_test_case(credentials_provider_sts_web_identity_real_new_destroy) add_net_test_case(credentials_provider_sts_direct_config_succeeds) add_net_test_case(credentials_provider_sts_direct_config_with_region_succeeds) +add_net_test_case(credentials_provider_sts_direct_config_with_default_region_succeeds) add_net_test_case(credentials_provider_sts_direct_config_with_region_from_config_succeeds) add_net_test_case(credentials_provider_sts_direct_config_succeeds_after_retry) add_net_test_case(credentials_provider_sts_direct_config_invalid_doc) diff --git a/tests/credentials_provider_sts_tests.c b/tests/credentials_provider_sts_tests.c index f095ffd4..c44fe223 100644 --- a/tests/credentials_provider_sts_tests.c +++ b/tests/credentials_provider_sts_tests.c @@ -263,7 +263,8 @@ static struct aws_auth_http_system_vtable s_mock_function_table = { .aws_http_connection_close = s_aws_http_connection_close_mock}; AWS_STATIC_STRING_FROM_LITERAL(s_config_file_path_env_variable_name, "AWS_CONFIG_FILE"); -AWS_STATIC_STRING_FROM_LITERAL(s_region_env_variable_name, "AWS_DEFAULT_REGION"); +AWS_STATIC_STRING_FROM_LITERAL(s_region_env_variable_name, "AWS_REGION"); +AWS_STATIC_STRING_FROM_LITERAL(s_region_default_env_variable_name, "AWS_DEFAULT_REGION"); static int s_aws_sts_tester_init(struct aws_allocator *allocator) { AWS_ZERO_STRUCT(s_tester); @@ -307,6 +308,7 @@ static int s_aws_sts_tester_init(struct aws_allocator *allocator) { * set the environment so that it doesn't mess with tests */ aws_unset_environment_value(s_region_env_variable_name); + aws_unset_environment_value(s_region_default_env_variable_name); struct aws_string *cur_directory = aws_string_new_from_c_str(allocator, "."); aws_set_environment_value(s_config_file_path_env_variable_name, cur_directory); aws_string_destroy(cur_directory); @@ -500,8 +502,12 @@ static int s_credentials_provider_sts_direct_config_with_region_succeeds_fn( (void)ctx; s_aws_sts_tester_init(allocator); + /* verify that it picks the s_region_env_variable_name if both are set */ struct aws_string *region = aws_string_new_from_c_str(allocator, "us-west-2"); + struct aws_string *default_region = aws_string_new_from_c_str(allocator, "us-east-1"); aws_set_environment_value(s_region_env_variable_name, region); + aws_set_environment_value(s_region_default_env_variable_name, default_region); + aws_string_destroy(default_region); aws_string_destroy(region); struct aws_credentials_provider_static_options static_options = { @@ -577,6 +583,89 @@ AWS_TEST_CASE( credentials_provider_sts_direct_config_with_region_succeeds, s_credentials_provider_sts_direct_config_with_region_succeeds_fn) +static int s_credentials_provider_sts_direct_config_with_default_region_succeeds_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + s_aws_sts_tester_init(allocator); + struct aws_string *default_region = aws_string_new_from_c_str(allocator, "us-east-1"); + aws_set_environment_value(s_region_default_env_variable_name, default_region); + aws_string_destroy(default_region); + + struct aws_credentials_provider_static_options static_options = { + .access_key_id = s_access_key_cur, + .secret_access_key = s_secret_key_cur, + .session_token = s_session_token_cur, + }; + struct aws_credentials_provider *static_provider = aws_credentials_provider_new_static(allocator, &static_options); + + struct aws_credentials_provider_sts_options options = { + .creds_provider = static_provider, + .bootstrap = s_tester.bootstrap, + .tls_ctx = s_tester.tls_ctx, + .role_arn = s_role_arn_cur, + .session_name = s_session_name_cur, + .duration_seconds = 0, + .function_table = &s_mock_function_table, + .system_clock_fn = mock_aws_get_system_time, + }; + + mock_aws_set_system_time(0); + + aws_array_list_push_back(&s_tester.response_data_callbacks, &s_success_creds_doc); + s_tester.mock_response_code = 200; + + struct aws_credentials_provider *sts_provider = aws_credentials_provider_new_sts(allocator, &options); + + aws_credentials_provider_get_credentials(sts_provider, s_get_credentials_callback, NULL); + + s_aws_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(s_tester.credentials)); + ASSERT_TRUE(aws_credentials_get_expiration_timepoint_seconds(s_tester.credentials) == 900); + + const char *expected_method = "POST"; + ASSERT_BIN_ARRAYS_EQUALS( + expected_method, + strlen(expected_method), + s_tester.mocked_requests[0].method.buffer, + s_tester.mocked_requests[0].method.len); + + const char *expected_path = "/"; + ASSERT_BIN_ARRAYS_EQUALS( + expected_path, + strlen(expected_path), + s_tester.mocked_requests[0].path.buffer, + s_tester.mocked_requests[0].path.len); + + ASSERT_TRUE(s_tester.mocked_requests[0].had_auth_header); + + const char *expected_host_header = "sts.us-east-1.amazonaws.com"; + ASSERT_BIN_ARRAYS_EQUALS( + expected_host_header, + strlen(expected_host_header), + s_tester.mocked_requests[0].host_header.buffer, + s_tester.mocked_requests[0].host_header.len); + + ASSERT_BIN_ARRAYS_EQUALS( + s_expected_payload.ptr, + s_expected_payload.len, + s_tester.mocked_requests[0].body.buffer, + s_tester.mocked_requests[0].body.len); + + aws_credentials_provider_release(sts_provider); + s_aws_wait_for_provider_shutdown_callback(); + aws_credentials_provider_release(static_provider); + ASSERT_SUCCESS(s_aws_sts_tester_cleanup()); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + credentials_provider_sts_direct_config_with_default_region_succeeds, + s_credentials_provider_sts_direct_config_with_default_region_succeeds_fn) + static int s_credentials_provider_sts_direct_config_with_region_from_config_succeeds_fn( struct aws_allocator *allocator, void *ctx) {