diff --git a/source/credentials_provider_ecs.c b/source/credentials_provider_ecs.c index 458ab90e..130850f1 100644 --- a/source/credentials_provider_ecs.c +++ b/source/credentials_provider_ecs.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -46,6 +48,7 @@ struct aws_credentials_provider_ecs_impl { struct aws_string *path_and_query; struct aws_string *auth_token_file_path; struct aws_string *auth_token; + struct aws_client_bootstrap *bootstrap; bool is_https; }; @@ -467,25 +470,10 @@ static void s_ecs_on_acquire_connection(struct aws_http_connection *connection, * * 2. corresponds to the ECS container host 169.254.170.2 * * 3. corresponds to the EKS container host IPs (IPv4 169.254.170.23, IPv6 fd00:ec2::23) * */ -static bool s_is_valid_remote_host_ip( - struct aws_credentials_provider_ecs_user_data *ecs_user_data, - struct aws_http_connection *connection) { - struct aws_credentials_provider_ecs_impl *impl = ecs_user_data->ecs_provider->impl; - - if (impl->is_https) { - return true; - } - +static bool s_is_valid_remote_host_ip(struct aws_host_address *host_address_ptr) { bool result = false; - - const struct aws_byte_cursor address = - aws_byte_cursor_from_c_str(aws_http_connection_get_remote_endpoint(connection)->address); - AWS_LOGF_INFO( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: the ip address of connected remove endpoint is " PRInSTR "", - (void *)ecs_user_data->ecs_provider, - AWS_BYTE_CURSOR_PRI(address)); - if (aws_host_utils_is_ipv4(address)) { + struct aws_byte_cursor address = aws_byte_cursor_from_string(host_address_ptr->address); + if (host_address_ptr->record_type == AWS_ADDRESS_RECORD_TYPE_A) { const struct aws_byte_cursor ipv4_loopback_address_prefix = aws_byte_cursor_from_c_str("127."); const struct aws_byte_cursor ecs_container_host_address = aws_byte_cursor_from_c_str("169.254.170.2"); const struct aws_byte_cursor eks_container_host_address = aws_byte_cursor_from_c_str("169.254.170.23"); @@ -494,8 +482,9 @@ static bool s_is_valid_remote_host_ip( result |= aws_byte_cursor_eq(&address, &ecs_container_host_address); result |= aws_byte_cursor_eq(&address, &eks_container_host_address); - } else if (aws_host_utils_is_ipv6( - address, false)) { /* Check for both the short form and long form of an IPv6 address to be safe. */ + } else if (host_address_ptr->record_type == AWS_ADDRESS_RECORD_TYPE_AAAA) { /* Check for both the short form and + long + form of an IPv6 address to be safe. */ const struct aws_byte_cursor ipv6_loopback_address = aws_byte_cursor_from_c_str("::1"); const struct aws_byte_cursor ipv6_loopback_address_verbose = aws_byte_cursor_from_c_str("0:0:0:0:0:0:0:1"); const struct aws_byte_cursor eks_container_host_ipv6_address = aws_byte_cursor_from_c_str("fd00:ec2::23"); @@ -511,6 +500,56 @@ static bool s_is_valid_remote_host_ip( return result; } +static void s_on_host_resolved( + struct aws_host_resolver *resolver, + const struct aws_string *host_name, + int error_code, + const struct aws_array_list *host_addresses, + void *user_data) { + (void)resolver; + (void)host_name; + + struct aws_credentials_provider_ecs_user_data *ecs_user_data = user_data; + if (error_code) { + AWS_LOGF_WARN( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: ECS provider failed to resolve host, error code %d(%s)", + (void *)ecs_user_data->ecs_provider, + error_code, + aws_error_str(error_code)); + ecs_user_data->error_code = error_code; + s_ecs_finalize_get_credentials_query(ecs_user_data); + return; + } + size_t host_addresses_len = aws_array_list_length(host_addresses); + if (!host_addresses_len) { + goto on_error; + } + AWS_FATAL_ASSERT(host_addresses_len > 0); + for (size_t i = 0; i < host_addresses_len; ++i) { + struct aws_host_address *host_address_ptr = NULL; + aws_array_list_get_at_ptr(host_addresses, (void **)&host_address_ptr, i); + if (!s_is_valid_remote_host_ip(host_address_ptr)) { + goto on_error; + } + } + struct aws_credentials_provider_ecs_impl *impl = ecs_user_data->ecs_provider->impl; + impl->function_table->aws_http_connection_manager_acquire_connection( + impl->connection_manager, s_ecs_on_acquire_connection, ecs_user_data); + + return; +on_error: + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: ECS provider failed to resolve address to an allowed ip address with error %d(%s)", + (void *)ecs_user_data->ecs_provider, + AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_HOST, + aws_error_str(AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_HOST)); + ecs_user_data->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_HOST; + s_ecs_finalize_get_credentials_query(ecs_user_data); + return; +} + static int s_credentials_provider_ecs_get_credentials_async( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, @@ -526,10 +565,17 @@ static int s_credentials_provider_ecs_get_credentials_async( if (wrapped_user_data == NULL) { goto error; } - - impl->function_table->aws_http_connection_manager_acquire_connection( - impl->connection_manager, s_ecs_on_acquire_connection, wrapped_user_data); - + if (impl->is_https || aws_string_eq(impl->host, s_ecs_host)) { + impl->function_table->aws_http_connection_manager_acquire_connection( + impl->connection_manager, s_ecs_on_acquire_connection, wrapped_user_data); + } else if (aws_host_resolver_resolve_host( + impl->bootstrap->host_resolver, + impl->host, + s_on_host_resolved, + &impl->bootstrap->host_resolver_config, + wrapped_user_data)) { + goto error; + } return AWS_OP_SUCCESS; error: @@ -549,6 +595,7 @@ static void s_credentials_provider_ecs_destroy(struct aws_credentials_provider * aws_string_destroy(impl->auth_token); aws_string_destroy(impl->auth_token_file_path); aws_string_destroy(impl->host); + aws_client_bootstrap_release(impl->bootstrap); /* aws_http_connection_manager_release will eventually leads to call of s_on_connection_manager_shutdown, * which will do memory release for provider and impl. So We should be freeing impl @@ -606,7 +653,7 @@ struct aws_credentials_provider *aws_credentials_provider_new_ecs( AWS_ZERO_STRUCT(*impl); aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_ecs_vtable, impl); - + impl->bootstrap = aws_client_bootstrap_acquire(options->bootstrap); struct aws_tls_connection_options tls_connection_options; AWS_ZERO_STRUCT(tls_connection_options); if (options->tls_ctx) { diff --git a/tests/credentials_provider_ecs_tests.c b/tests/credentials_provider_ecs_tests.c index d698c399..7eb63a87 100644 --- a/tests/credentials_provider_ecs_tests.c +++ b/tests/credentials_provider_ecs_tests.c @@ -54,6 +54,7 @@ struct aws_mock_ecs_tester { struct aws_event_loop_group *el_group; struct aws_host_resolver *host_resolver; struct aws_client_bootstrap *bootstrap; + struct aws_tls_ctx *tls_ctx; }; static struct aws_mock_ecs_tester s_tester; @@ -259,6 +260,13 @@ static int s_aws_ecs_tester_init(struct aws_allocator *allocator) { }; s_tester.bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + struct aws_tls_ctx_options tls_options; + aws_tls_ctx_options_init_default_client(&tls_options, allocator); + struct aws_tls_ctx *tls_ctx = aws_tls_client_ctx_new(allocator, &tls_options); + ASSERT_NOT_NULL(tls_ctx); + s_tester.tls_ctx = tls_ctx; + aws_tls_ctx_options_clean_up(&tls_options); + /* ensure pre-existing environment doesn't interfere with tests */ aws_unset_environment_value(s_ecs_creds_env_relative_uri); aws_unset_environment_value(s_ecs_creds_env_full_uri); @@ -279,6 +287,7 @@ static void s_aws_ecs_tester_reset(void) { aws_client_bootstrap_release(s_tester.bootstrap); aws_host_resolver_release(s_tester.host_resolver); aws_event_loop_group_release(s_tester.el_group); + aws_tls_ctx_release(s_tester.tls_ctx); AWS_ZERO_STRUCT(s_tester); } @@ -362,6 +371,7 @@ static int s_credentials_provider_ecs_connect_failure(struct aws_allocator *allo .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), + .tls_ctx = s_tester.tls_ctx, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); @@ -373,7 +383,7 @@ static int s_credentials_provider_ecs_connect_failure(struct aws_allocator *allo aws_mutex_lock(&s_tester.lock); ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials == NULL); - ASSERT_UINT_EQUALS(80, s_tester.selected_port); + ASSERT_UINT_EQUALS(443, s_tester.selected_port); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); @@ -407,6 +417,7 @@ static int s_credentials_provider_ecs_request_failure(struct aws_allocator *allo .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), + .tls_ctx = s_tester.tls_ctx, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); @@ -419,7 +430,7 @@ static int s_credentials_provider_ecs_request_failure(struct aws_allocator *allo ASSERT_STR_EQUALS("/path/to/resource/?a=b&c=d", aws_string_c_str(s_tester.request_path_and_query)); ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials == NULL); - ASSERT_UINT_EQUALS(80, s_tester.selected_port); + ASSERT_UINT_EQUALS(443, s_tester.selected_port); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); @@ -457,7 +468,7 @@ static int s_credentials_provider_ecs_bad_document_failure(struct aws_allocator .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), - .port = 555, + .tls_ctx = s_tester.tls_ctx, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); @@ -471,7 +482,7 @@ static int s_credentials_provider_ecs_bad_document_failure(struct aws_allocator ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials == NULL); - ASSERT_UINT_EQUALS(555, s_tester.selected_port); + ASSERT_UINT_EQUALS(443, s_tester.selected_port); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); @@ -655,12 +666,13 @@ static int s_credentials_provider_ecs_basic_success(struct aws_allocator *alloca .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), + .tls_ctx = s_tester.tls_ctx, }; ASSERT_SUCCESS(s_do_ecs_success_test( allocator, &options, - "http://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d" /*expected_uri*/, + "https://www.xxx123321testmocknonexsitingawsservice.com:443/path/to/resource/?a=b&c=d" /*expected_uri*/, "test-token-1234-abcd" /*expected_token*/)); s_aws_ecs_tester_cleanup(); @@ -697,6 +709,7 @@ static int s_credentials_provider_ecs_basic_success_token_file(struct aws_alloca .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_string(auth_token), .auth_token_file_path = aws_byte_cursor_from_string(token_file_path), + .tls_ctx = s_tester.tls_ctx, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); @@ -707,7 +720,7 @@ static int s_credentials_provider_ecs_basic_success_token_file(struct aws_alloca aws_mutex_lock(&s_tester.lock); ASSERT_SUCCESS(s_check_ecs_tester_request_uri_and_authorization( - "http://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d", + "https://www.xxx123321testmocknonexsitingawsservice.com:443/path/to/resource/?a=b&c=d", aws_string_c_str(auth_token))); aws_string_destroy(s_tester.request_path_and_query); @@ -726,7 +739,7 @@ static int s_credentials_provider_ecs_basic_success_token_file(struct aws_alloca aws_mutex_lock(&s_tester.lock); ASSERT_SUCCESS(s_check_ecs_tester_request_uri_and_authorization( - "http://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d", + "https://www.xxx123321testmocknonexsitingawsservice.com:443/path/to/resource/?a=b&c=d", aws_string_c_str(auth_token2))); aws_mutex_unlock(&s_tester.lock); @@ -792,8 +805,8 @@ static int s_credentials_provider_ecs_basic_success_uri_env(struct aws_allocator }, /* auth token is properly set */ { - .full_uri = "http://127.0.0.1:8080/credentials", - .expected_uri = "http://127.0.0.1:8080/credentials", + .full_uri = "http://127.1.2.3:8080/credentials", + .expected_uri = "http://127.1.2.3:8080/credentials", .auth_token = "testToken", .expected_auth_token = "testToken", }, @@ -806,27 +819,21 @@ static int s_credentials_provider_ecs_basic_success_uri_env(struct aws_allocator }, /* auth_token_file_path is respected */ { - .full_uri = "http://127.0.0.1:8080/credentials", - .expected_uri = "http://127.0.0.1:8080/credentials", + .full_uri = "http://169.254.170.23:8080/credentials", + .expected_uri = "http://169.254.170.23:8080/credentials", .auth_token_file_content = "testToken", .expected_auth_token = "testToken", }, /* auth_token_file_path is preferred */ { - .full_uri = "http://127.0.0.1:8080/credentials", - .expected_uri = "http://127.0.0.1:8080/credentials", + .full_uri = "http://169.254.170.2:8080/credentials", + .expected_uri = "http://169.254.170.2:8080/credentials", .auth_token = "BadToken", .auth_token_file_content = "testToken", .expected_auth_token = "testToken", }, }; - /* Provide tls_ctx, in case FULL_URI scheme is "https://" */ - struct aws_tls_ctx_options tls_options; - aws_tls_ctx_options_init_default_client(&tls_options, allocator); - struct aws_tls_ctx *tls_ctx = aws_tls_client_ctx_new(allocator, &tls_options); - ASSERT_NOT_NULL(tls_ctx); - for (size_t case_idx = 0; case_idx < AWS_ARRAY_SIZE(test_cases); ++case_idx) { struct test_case case_i = test_cases[case_idx]; printf( @@ -852,7 +859,7 @@ static int s_credentials_provider_ecs_basic_success_uri_env(struct aws_allocator .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, - .tls_ctx = tls_ctx, + .tls_ctx = s_tester.tls_ctx, }; ASSERT_SUCCESS(s_do_ecs_env_success_test( @@ -868,8 +875,6 @@ static int s_credentials_provider_ecs_basic_success_uri_env(struct aws_allocator s_aws_ecs_tester_reset(); } - aws_tls_ctx_release(tls_ctx); - aws_tls_ctx_options_clean_up(&tls_options); s_aws_ecs_tester_cleanup(); return 0; } @@ -893,12 +898,13 @@ static int s_credentials_provider_ecs_no_auth_token_success(struct aws_allocator }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), + .tls_ctx = s_tester.tls_ctx, }; ASSERT_SUCCESS(s_do_ecs_success_test( allocator, &options, - "http://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d" /*expected_uri*/, + "https://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d" /*expected_uri*/, NULL /*expected_token*/)); s_aws_ecs_tester_cleanup(); @@ -935,6 +941,7 @@ static int s_credentials_provider_ecs_success_multi_part_doc(struct aws_allocato .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), + .tls_ctx = s_tester.tls_ctx, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options);