Skip to content

Commit

Permalink
host resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
waahm7 committed May 8, 2024
1 parent fde7ad1 commit 422775b
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 48 deletions.
97 changes: 72 additions & 25 deletions source/credentials_provider_ecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include <aws/http/connection_manager.h>
#include <aws/http/request_response.h>
#include <aws/http/status_code.h>
#include <aws/io/channel_bootstrap.h>
#include <aws/io/host_resolver.h>
#include <aws/io/logging.h>
#include <aws/io/socket.h>
#include <aws/io/tls_channel_handler.h>
Expand Down Expand Up @@ -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;
};

Expand Down Expand Up @@ -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");
Expand All @@ -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");
Expand All @@ -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,
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
53 changes: 30 additions & 23 deletions tests/credentials_provider_ecs_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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",
},
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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;
}
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 422775b

Please sign in to comment.