diff --git a/include/fluent-bit/flb_aws_credentials.h b/include/fluent-bit/flb_aws_credentials.h index 5bcda676b5b..05ad0f6c743 100644 --- a/include/fluent-bit/flb_aws_credentials.h +++ b/include/fluent-bit/flb_aws_credentials.h @@ -257,18 +257,28 @@ struct flb_aws_provider *flb_aws_env_provider_create(); * used by host and path. */ struct flb_aws_provider *flb_http_provider_create(struct flb_config *config, - flb_sds_t host, + flb_sds_t endpoint, flb_sds_t path, + flb_sds_t auth_token, struct flb_aws_client_generator *generator); +struct flb_aws_provider *flb_local_http_provider_create(struct flb_config *config, + flb_sds_t endpoint, + flb_sds_t auth_token, + struct + flb_aws_client_generator + *generator); + + + /* - * ECS Provider - * The ECS Provider is just a wrapper around the HTTP Provider - * with the ECS credentials endpoint. + * Container Provider + * The Container Provider is just a wrapper around the HTTP Provider + * with the ECS/EKS credentials endpoint. */ -struct flb_aws_provider *flb_ecs_provider_create(struct flb_config *config, +struct flb_aws_provider *flb_container_provider_create(struct flb_config *config, struct flb_aws_client_generator *generator); @@ -349,6 +359,27 @@ int try_lock_provider(struct flb_aws_provider *provider); void unlock_provider(struct flb_aws_provider *provider); +/* + * HTTP Credentials Provider - retrieve credentials from a local http server + * Used to implement the Container Credentials provider. + * Equivalent to: + * https://github.com/aws/aws-sdk-go/tree/master/aws/credentials/endpointcreds + */ + +struct flb_aws_provider_http { + struct flb_aws_credentials *creds; + time_t next_refresh; + + struct flb_aws_client *client; + + /* Endpoint to request credentials */ + flb_sds_t endpoint; + flb_sds_t path; + + /* Auth token */ + flb_sds_t auth_token; + flb_sds_t auth_token_file; +}; #endif #endif /* FLB_HAVE_AWS */ diff --git a/include/fluent-bit/flb_aws_util.h b/include/fluent-bit/flb_aws_util.h index ac978e6e23c..560f3a54208 100644 --- a/include/fluent-bit/flb_aws_util.h +++ b/include/fluent-bit/flb_aws_util.h @@ -144,13 +144,21 @@ flb_sds_t flb_aws_xml_error(char *response, size_t response_len); flb_sds_t flb_aws_error(char *response, size_t response_len); /* - * Similar to 'flb_aws_error', except it prints the JSON error type and message - * to the user in a error log. + * Similar to 'flb_aws_error', except it prints the JSON error __type and message + * field values to the user in a error log. * 'api' is the name of the API that was called; this is used in the error log. */ void flb_aws_print_error(char *response, size_t response_len, char *api, struct flb_output_instance *ins); +/* + * Similar to 'flb_aws_error', except it prints the JSON error Code and Message + * field values to the user in a error log. + * 'api' is the name of the API that was called; this is used in the error log. + */ +void flb_aws_print_error_code(char *response, size_t response_len, + char *api); + /* Similar to 'flb_aws_print_error', but for APIs that return XML */ void flb_aws_print_xml_error(char *response, size_t response_len, char *api, struct flb_output_instance *ins); diff --git a/src/aws/flb_aws_credentials.c b/src/aws/flb_aws_credentials.c index 824a535d021..d3cb3d1c9e7 100644 --- a/src/aws/flb_aws_credentials.c +++ b/src/aws/flb_aws_credentials.c @@ -581,11 +581,11 @@ static struct flb_aws_provider *standard_chain_create(struct flb_config } } - sub_provider = flb_ecs_provider_create(config, generator); + sub_provider = flb_container_provider_create(config, generator); if (sub_provider) { - /* ECS Provider will fail creation if we are not running in ECS */ + /* HTTP Provider will fail creation if we are not running in ECS/EKS */ mk_list_add(&sub_provider->_head, &implementation->sub_providers); - flb_debug("[aws_credentials] Initialized ECS Provider in standard chain"); + flb_debug("[aws_credentials] Initialized HTTP Provider in standard chain"); } sub_provider = flb_ec2_provider_create(config, generator); diff --git a/src/aws/flb_aws_credentials_http.c b/src/aws/flb_aws_credentials_http.c index d9095935ce6..d3c647b5bcd 100644 --- a/src/aws/flb_aws_credentials_http.c +++ b/src/aws/flb_aws_credentials_http.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -34,9 +36,19 @@ #define AWS_HTTP_RESPONSE_TOKEN "Token" #define AWS_CREDENTIAL_RESPONSE_EXPIRATION "Expiration" -#define ECS_CREDENTIALS_HOST "169.254.170.2" -#define ECS_CREDENTIALS_HOST_LEN 13 -#define ECS_CREDENTIALS_PATH_ENV_VAR "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" +#define ECS_CONTAINER_ENDPOINT "http://169.254.170.2" +#define ECS_CONTAINER_ENDPOINT_LEN 20 +#define ECS_CREDENTIALS_HOST "169.254.170.2" +#define ECS_CREDENTIALS_HOST_LEN 13 +#define EKS_CREDENTIALS_HOST "169.254.170.23" +#define EKS_CREDENTIALS_HOST_LEN 14 +#define EKS_CREDENTIALS_HOST_IPV6 "fd00:ec2::23" +#define EKS_CREDENTIALS_HOST_IPV6_LEN 12 + +#define AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE_ENV_VAR "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE" +#define AWS_CONTAINER_AUTHORIZATION_TOKEN_ENV_VAR "AWS_CONTAINER_AUTHORIZATION_TOKEN" +#define AWS_CONTAINER_CREDENTIALS_RELATIVE_URI_ENV_VAR "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" +#define AWS_CONTAINER_CREDENTIALS_FULL_URI_ENV_VAR "AWS_CONTAINER_CREDENTIALS_FULL_URI" /* Declarations */ @@ -44,26 +56,13 @@ struct flb_aws_provider_http; static int http_credentials_request(struct flb_aws_provider_http *implementation); - -/* - * HTTP Credentials Provider - retrieve credentials from a local http server - * Used to implement the ECS Credentials provider. - * Equivalent to: - * https://github.com/aws/aws-sdk-go/tree/master/aws/credentials/endpointcreds - */ - -struct flb_aws_provider_http { - struct flb_aws_credentials *creds; - time_t next_refresh; - - struct flb_aws_client *client; - - /* Host and Path to request credentials */ - flb_sds_t host; - flb_sds_t path; +static struct flb_aws_header authorization_header = { + .key = "Authorization", + .key_len = 13, + .val = "", + .val_len = 0, }; - struct flb_aws_credentials *get_credentials_fn_http(struct flb_aws_provider *provider) { @@ -136,6 +135,47 @@ struct flb_aws_credentials *get_credentials_fn_http(struct flb_aws_provider return NULL; } +/* + * If the resolved URI’s scheme is HTTPS, its hostname may be used in the request. + * Otherwise, implementations MUST fail to resolve when the URI hostname + * does not satisfy any of the following conditions: + * is within the loopback CIDR (IPv4 127.0.0.0/8, IPv6 ::1/128) + * is the ECS container host 169.254.170.2 + * is the EKS Pod Identity Agent (IPv4 169.254.170.23, IPv6 fd00:ec2::23) + */ +static int flb_aws_allowed_ip(char *prot, char *ip_address) { + if (strncmp(prot, "https", 5) == 0) { + return FLB_TRUE; + } + + if (strcmp(ip_address, ECS_CREDENTIALS_HOST) == 0) { + return FLB_TRUE; // ECS container host + } else if (strcmp(ip_address, EKS_CREDENTIALS_HOST) == 0 || + strcmp(ip_address, EKS_CREDENTIALS_HOST_IPV6) == 0) { + return FLB_TRUE; // EKS container host + } + + // Loopback + // IPv4 + if (strncmp(ip_address, "127.0.0.", 8) == 0 && + strlen(ip_address) > 8 && + strlen(ip_address) <= 11) { + ip_address += 8; + if (atoi(ip_address) > 255) { + return FLB_FALSE; + } + return FLB_TRUE; + } + + // IPv6 + if (strcmp(ip_address, "::1") == 0 || + strcmp(ip_address, "0:0:0:0:0:0:0:1") == 0) { + return FLB_TRUE; + } + + return FLB_FALSE; +} + int refresh_fn_http(struct flb_aws_provider *provider) { struct flb_aws_provider_http *implementation = provider->implementation; int ret = -1; @@ -204,14 +244,22 @@ void destroy_fn_http(struct flb_aws_provider *provider) { flb_aws_client_destroy(implementation->client); } - if (implementation->host) { - flb_sds_destroy(implementation->host); + if (implementation->endpoint) { + flb_sds_destroy(implementation->endpoint); } if (implementation->path) { flb_sds_destroy(implementation->path); } + if (implementation->auth_token) { + flb_sds_destroy(implementation->auth_token); + } + + if (implementation->auth_token_file) { + flb_sds_destroy(implementation->auth_token_file); + } + flb_free(implementation); provider->implementation = NULL; } @@ -230,18 +278,29 @@ static struct flb_aws_provider_vtable http_provider_vtable = { }; struct flb_aws_provider *flb_http_provider_create(struct flb_config *config, - flb_sds_t host, + flb_sds_t endpoint, flb_sds_t path, + flb_sds_t auth_token, struct flb_aws_client_generator *generator) { + char *auth_token_file = NULL; struct flb_aws_provider_http *implementation = NULL; struct flb_aws_provider *provider = NULL; struct flb_upstream *upstream = NULL; - flb_debug("[aws_credentials] Configuring HTTP provider with %s:80%s", - host, path); + if (auth_token) { + if (strstr(auth_token, "\\r\\n")) { + flb_error("[aws_credentials] unable to configure HTTP provider: " + "auth token contains invalid characters (\\r\\n)"); + flb_errno(); + return NULL; + } + } + + flb_debug("[aws_credentials] Configuring HTTP provider with %s", + endpoint); provider = flb_calloc(1, sizeof(struct flb_aws_provider)); @@ -263,10 +322,21 @@ struct flb_aws_provider *flb_http_provider_create(struct flb_config *config, provider->provider_vtable = &http_provider_vtable; provider->implementation = implementation; - implementation->host = host; + auth_token_file = getenv(AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE_ENV_VAR); + if (auth_token_file) { + implementation->auth_token_file = flb_sds_create(auth_token_file); + if (!implementation->auth_token_file) { + flb_aws_provider_destroy(provider); + flb_errno(); + return NULL; + } + } + + implementation->auth_token = auth_token; + implementation->endpoint = endpoint; implementation->path = path; - upstream = flb_upstream_create(config, host, 80, FLB_IO_TCP, NULL); + upstream = flb_upstream_create_url(config, endpoint, FLB_IO_TCP, NULL); if (!upstream) { flb_aws_provider_destroy(provider); @@ -289,7 +359,6 @@ struct flb_aws_provider *flb_http_provider_create(struct flb_config *config, implementation->client->provider = NULL; implementation->client->region = NULL; implementation->client->service = NULL; - implementation->client->port = 80; implementation->client->flags = 0; implementation->client->proxy = NULL; implementation->client->upstream = upstream; @@ -297,65 +366,231 @@ struct flb_aws_provider *flb_http_provider_create(struct flb_config *config, return provider; } +struct flb_aws_provider *flb_local_http_provider_create(struct flb_config *config, + flb_sds_t endpoint, + flb_sds_t auth_token, + struct + flb_aws_client_generator + *generator) +{ + int ret; + char *prot = NULL; + char *host = NULL; + char *port = NULL; + char *uri = NULL; + flb_sds_t path = NULL; + struct flb_aws_provider *p = NULL; + + ret = flb_utils_url_split(endpoint, &prot, &host, &port, &uri); + if (ret == -1) { + flb_error("[aws_credentials] HTTP Provider: invalid URL: %s", endpoint); + return NULL; + } + + if (strlen(host) == 0) { + flb_error("[aws_credentials] HTTP Provider: " + "unable to parse host from local HTTP cred provider URL"); + goto out; + } + + if (!flb_aws_allowed_ip(prot, host)) { + flb_error("[aws_credentials] HTTP Provider: " + "invalid endpoint %s, only https or loopback/ecs/eks hosts are allowed", + endpoint); + goto out; + } + + ret = atoi(port); + if (ret == 0) { + flb_error("[aws_credentials] HTTP Provider: " + "invalid endpoint port %s", port); + goto out; + } + + if (strlen(uri) > 0) { + path = flb_sds_create(uri); + if (!path) { + flb_error("[aws_credentials] HTTP Provider: " + "unable to get path for provider"); + goto out; + } + } + + p = flb_http_provider_create(config, endpoint, path, auth_token, generator); + + out: + if (prot) { + flb_free(prot); + } + if (host) { + flb_free(host); + } + if (port) { + flb_free(port); + } + if (uri) { + flb_free(uri); + } + + return p; +} + /* - * ECS Provider - * The ECS Provider is just a wrapper around the HTTP Provider - * with the ECS credentials endpoint. + * Container Provider + * The Container Provider is just a wrapper around the HTTP Provider + * with the ECS/EKS credentials endpoint. */ - struct flb_aws_provider *flb_ecs_provider_create(struct flb_config *config, + struct flb_aws_provider *flb_container_provider_create(struct flb_config *config, struct flb_aws_client_generator *generator) { - flb_sds_t host = NULL; + flb_sds_t endpoint = NULL; flb_sds_t path = NULL; + flb_sds_t token = NULL; + char *endpoint_var = NULL; char *path_var = NULL; + char *token_var = NULL; + struct flb_aws_provider *provider; - host = flb_sds_create_len(ECS_CREDENTIALS_HOST, ECS_CREDENTIALS_HOST_LEN); - if (!host) { - flb_errno(); - return NULL; + token_var = getenv(AWS_CONTAINER_AUTHORIZATION_TOKEN_ENV_VAR); + if (token_var && strlen(token_var) > 0) { + token = flb_sds_create(token_var); + if (!token) { + flb_errno(); + return NULL; + } } - path_var = getenv(ECS_CREDENTIALS_PATH_ENV_VAR); + endpoint_var = getenv(AWS_CONTAINER_CREDENTIALS_FULL_URI_ENV_VAR); + if (endpoint_var && strlen(endpoint_var) > 0) { + endpoint = flb_sds_create(endpoint_var); + if (!endpoint) { + flb_errno(); + goto err; + } + + provider = flb_local_http_provider_create(config, endpoint, token, generator); + if (!provider) { + flb_errno(); + goto err; + } + + return provider; + } + + path_var = getenv(AWS_CONTAINER_CREDENTIALS_RELATIVE_URI_ENV_VAR); if (path_var && strlen(path_var) > 0) { path = flb_sds_create(path_var); if (!path) { flb_errno(); - flb_free(host); - return NULL; + goto err; + } + + endpoint = flb_sds_create_len(ECS_CONTAINER_ENDPOINT, ECS_CONTAINER_ENDPOINT_LEN); + if (!endpoint) { + flb_errno(); + goto err; } - return flb_http_provider_create(config, host, path, generator); + provider = flb_http_provider_create(config, endpoint, path, token, generator); + if (!provider) { + flb_errno(); + goto err; + } } else { - flb_debug("[aws_credentials] Not initializing ECS Provider because" - " %s is not set", ECS_CREDENTIALS_PATH_ENV_VAR); - flb_sds_destroy(host); - return NULL; + flb_debug("[aws_credentials] Not initializing HTTP Provider because" + " %s is not set", AWS_CONTAINER_CREDENTIALS_RELATIVE_URI_ENV_VAR); + goto err; + } + + return provider; + + err: + if (token) { + flb_sds_destroy(token); + } + + if (path) { + flb_sds_destroy(path); } + if (endpoint) { + flb_sds_destroy(endpoint); + } + + return NULL; } static int http_credentials_request(struct flb_aws_provider_http *implementation) { + int ret; + int headers_len = 0; char *response = NULL; + char *auth_token = NULL; + size_t auth_token_size; size_t response_len; time_t expiration; struct flb_aws_credentials *creds = NULL; struct flb_aws_client *client = implementation->client; + struct flb_aws_header *headers = NULL; struct flb_http_client *c = NULL; + if (implementation->auth_token_file) { + flb_debug("[aws_credentials] loading auth token file"); + ret = flb_read_file(implementation->auth_token_file, &auth_token, + &auth_token_size); + if (ret < 0) { + flb_debug("[aws_credentials] failed to retrieve token file"); + flb_errno(); + return -1; + } + if (strstr(auth_token, "\\r\\n")) { + flb_error("[aws_credentials] unable to retreive credentials: " + "auth token file contains invalid characters (\\r\\n)"); + flb_errno(); + flb_free(auth_token); + return -1; + } + } else if (implementation->auth_token) { + auth_token_size = flb_sds_len(implementation->auth_token); + auth_token = flb_strndup(implementation->auth_token, auth_token_size); + } + + if (auth_token) { + headers = flb_calloc(1, sizeof(struct flb_aws_header)); + if (headers == NULL) { + flb_errno(); + flb_free(auth_token); + return -1; + } + + headers[0] = authorization_header; + headers[0].val = auth_token; + headers[0].val_len = auth_token_size; + headers_len = 1; + } + c = client->client_vtable->request(client, FLB_HTTP_GET, implementation->path, NULL, 0, - NULL, 0); + headers, headers_len); + + flb_free(headers); if (!c || c->resp.status != 200) { flb_debug("[aws_credentials] http credentials request failed"); if (c) { + if (c->resp.payload_size > 0) { + flb_aws_print_error_code(c->resp.payload, c->resp.payload_size, + "ContainerCredentialsLocalServer"); + } flb_http_client_destroy(c); } + if (auth_token) { + flb_free(auth_token); + } return -1; } @@ -365,6 +600,9 @@ static int http_credentials_request(struct flb_aws_provider_http creds = flb_parse_http_credentials(response, response_len, &expiration); if (!creds) { flb_http_client_destroy(c); + if (auth_token) { + flb_free(auth_token); + } return -1; } @@ -375,6 +613,11 @@ static int http_credentials_request(struct flb_aws_provider_http implementation->creds = creds; implementation->next_refresh = expiration - FLB_AWS_REFRESH_WINDOW; flb_http_client_destroy(c); + + if (auth_token) { + flb_free(auth_token); + } + return 0; } @@ -532,12 +775,12 @@ struct flb_aws_credentials *flb_parse_json_credentials(char *response, goto error; } *expiration = flb_aws_cred_expiration(tmp); - flb_sds_destroy(tmp); if (*expiration < 0) { flb_warn("[aws_credentials] '%s' was invalid or " "could not be parsed. Disabling auto-refresh of " - "credentials.", AWS_CREDENTIAL_RESPONSE_EXPIRATION); + "credentials.", tmp); } + flb_sds_destroy(tmp); } } diff --git a/src/aws/flb_aws_util.c b/src/aws/flb_aws_util.c index 48cf8b3d89e..d1454bf51d7 100644 --- a/src/aws/flb_aws_util.c +++ b/src/aws/flb_aws_util.c @@ -343,6 +343,12 @@ struct flb_http_client *request_do(struct flb_aws_client *aws_client, goto error; } + /* Remove port from host header, EKS Pod Identities breaks without this */ + ret = flb_http_strip_port_from_host(c); + if (ret != 0) { + flb_warn("[aws_http_client] failed to remove port from Host header"); + } + /* Increase the maximum HTTP response buffer size to fit large responses from AWS services */ ret = flb_http_buffer_size(c, FLB_MAX_AWS_RESP_BUFFER_SIZE); if (ret != 0) { @@ -573,6 +579,10 @@ flb_sds_t flb_aws_xml_get_val(char *response, size_t response_len, char *tag, ch return val; } +/* + * Error parsing for json APIs that respond with an + * __type and message fields for error responses. + */ void flb_aws_print_error(char *response, size_t response_len, char *api, struct flb_output_instance *ins) { @@ -600,6 +610,37 @@ void flb_aws_print_error(char *response, size_t response_len, flb_sds_destroy(error); } +/* + * Error parsing for json APIs that respond with a + * Code and Message fields for error responses. + */ +void flb_aws_print_error_code(char *response, size_t response_len, + char *api) +{ + flb_sds_t error; + flb_sds_t message; + + error = flb_json_get_val(response, response_len, "Code"); + if (!error) { + /* error can not be parsed, print raw response */ + flb_warn("%s: Raw response: %s", api, response); + return; + } + + message = flb_json_get_val(response, response_len, "Message"); + if (!message) { + /* just print the error */ + flb_error("%s API responded with code='%s'", api, error); + } + else { + flb_error("%s API responded with code='%s', message='%s'", + api, error, message); + flb_sds_destroy(message); + } + + flb_sds_destroy(error); +} + /* parses AWS JSON API error responses and returns the value of the __type field */ flb_sds_t flb_aws_error(char *response, size_t response_len) { diff --git a/src/flb_utils.c b/src/flb_utils.c index 2f445980d84..6cc341160be 100644 --- a/src/flb_utils.c +++ b/src/flb_utils.c @@ -1036,6 +1036,8 @@ int flb_utils_url_split(const char *in_url, char **out_protocol, char *p; char *tmp; char *sep; + char *v6_start; + char *v6_end; /* Protocol */ p = strstr(in_url, "://"); @@ -1057,9 +1059,26 @@ int flb_utils_url_split(const char *in_url, char **out_protocol, /* Check for first '/' */ sep = strchr(p, '/'); - tmp = strchr(p, ':'); + v6_start = strchr(p, '['); + v6_end = strchr(p, ']'); + + /* + * Validate port separator is found before the first slash, + * If IPv6, ensure it is after the ']', + * but only if before the first slash + */ + if (v6_start && v6_end) { + if (sep && v6_end > sep) { + tmp = strchr(p, ':'); + } + else { + tmp = strchr(v6_end, ':'); + } + } + else { + tmp = strchr(p, ':'); + } - /* Validate port separator is found before the first slash */ if (sep && tmp) { if (tmp > sep) { tmp = NULL; diff --git a/tests/internal/aws_credentials_http.c b/tests/internal/aws_credentials_http.c index 55912da3a60..e07eb19048e 100644 --- a/tests/internal/aws_credentials_http.c +++ b/tests/internal/aws_credentials_http.c @@ -12,24 +12,13 @@ #include "flb_tests_internal.h" -#define ACCESS_KEY_HTTP "http_akid" -#define SECRET_KEY_HTTP "http_skid" -#define TOKEN_HTTP "http_token" +#include "../include/aws_client_mock.h" +#include "../include/aws_client_mock.c" -#define HTTP_CREDENTIALS_RESPONSE "{\n\ - \"AccessKeyId\": \"http_akid\",\n\ - \"Expiration\": \"2025-10-24T23:00:23Z\",\n\ - \"RoleArn\": \"TASK_ROLE_ARN\",\n\ - \"SecretAccessKey\": \"http_skid\",\n\ - \"Token\": \"http_token\"\n\ -}" +#include "aws_credentials_test_internal.h" + +#define HTTP_TOKEN_FILE AWS_TEST_DATA_PATH("http_token_file.txt") -/* - * Unexpected/invalid HTTP response. The goal of this is not to test anything - * that might happen in production, but rather to test the error handling - * code for the providers. This helps ensure all code paths are tested and - * the error handling code does not introduce memory leaks. - */ #define HTTP_RESPONSE_MALFORMED "{\n\ \"AccessKeyId\": \"http_akid\",\n\ \"partially-correct\": \"json\",\n\ @@ -37,200 +26,383 @@ \"but incomplete\": \"and not terminated with a closing brace\",\n\ \"Token\": \"http_token\"" - /* - * Global Variable that allows us to check the number of calls - * made in each test + * Setup test & Initialize test environment */ -int g_request_count; - -struct flb_http_client *request_happy_case(struct flb_aws_client *aws_client, - int method, const char *uri) -{ - struct flb_http_client *c = NULL; +void setup_test(struct flb_aws_client_mock_request_chain *request_chain, + struct flb_aws_provider **out_provider, struct flb_config **out_config) { + struct flb_aws_provider *provider; + struct flb_config *config; - TEST_CHECK(method == FLB_HTTP_GET); + /* Initialize test environment */ + config = flb_config_init(); + TEST_ASSERT(config != NULL); - TEST_CHECK(strstr(uri, "happy-case") != NULL); + flb_aws_client_mock_configure_generator(request_chain); - /* create an http client so that we can set the response */ - c = flb_calloc(1, sizeof(struct flb_http_client)); - if (!c) { - flb_errno(); - return NULL; - } - mk_list_init(&c->headers); + /* Init provider */ + provider = flb_container_provider_create(config, flb_aws_client_get_mock_generator()); + TEST_ASSERT(provider != NULL); - c->resp.status = 200; - c->resp.payload = HTTP_CREDENTIALS_RESPONSE; - c->resp.payload_size = strlen(HTTP_CREDENTIALS_RESPONSE); + *out_config = config; + *out_provider = provider; +} - return c; +/* Test clean up */ +void cleanup_test(struct flb_aws_provider *provider, struct flb_config *config) { + flb_aws_client_mock_destroy_generator(); + if (provider != NULL) { + ((struct flb_aws_provider_http *) (provider->implementation))->client = NULL; + flb_aws_provider_destroy(provider); + provider = NULL; + } + if (config != NULL) { + flb_config_exit(config); + config = NULL; + } } -/* unexpected output test- see description for HTTP_RESPONSE_MALFORMED */ -struct flb_http_client *request_malformed(struct flb_aws_client *aws_client, - int method, const char *uri) +/* + * Unexpected/invalid HTTP response. The goal of this is not to test anything + * that might happen in production, but rather to test the error handling + * code for the providers. This helps ensure all code paths are tested and + * the error handling code does not introduce memory leaks. + */ +static void test_http_provider_malformed_response() { - struct flb_http_client *c = NULL; + struct flb_aws_provider *provider; + struct flb_aws_credentials *creds; + struct flb_config *config; + int ret; - TEST_CHECK(method == FLB_HTTP_GET); + setenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "/iam_credentials/pod1", 1); + + setup_test(FLB_AWS_CLIENT_MOCK( + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER_COUNT, 0), + set(STATUS, 200), + set(PAYLOAD, HTTP_RESPONSE_MALFORMED), + set(PAYLOAD_SIZE, strlen(HTTP_RESPONSE_MALFORMED)) + ), + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER_COUNT, 0), + set(STATUS, 200), + set(PAYLOAD, HTTP_RESPONSE_MALFORMED), + set(PAYLOAD_SIZE, strlen(HTTP_RESPONSE_MALFORMED)) + ), + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + set(STATUS, 200), + set(PAYLOAD, HTTP_RESPONSE_MALFORMED), + set(PAYLOAD_SIZE, strlen(HTTP_RESPONSE_MALFORMED)) + ) + ), &provider, &config); + + flb_time_msleep(1000); - TEST_CHECK(strstr(uri, "malformed") != NULL); + /* get_credentials will fail */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_CHECK(creds == NULL); - /* create an http client so that we can set the response */ - c = flb_calloc(1, sizeof(struct flb_http_client)); - if (!c) { - flb_errno(); - return NULL; - } - mk_list_init(&c->headers); + creds = provider->provider_vtable->get_credentials(provider); + TEST_CHECK(creds == NULL); + + /* refresh should return -1 (failure) */ + ret = provider->provider_vtable->refresh(provider); + TEST_CHECK(ret < 0); - c->resp.status = 200; - c->resp.payload = HTTP_RESPONSE_MALFORMED; - c->resp.payload_size = strlen(HTTP_RESPONSE_MALFORMED); + /* + * Request count should be 3: + * - Each call to get_credentials and refresh invokes the client's + * request method and returns a request failure. + */ + TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0); - return c; + cleanup_test(provider, config); } -struct flb_http_client *request_error_case(struct flb_aws_client *aws_client, - int method, const char *uri) +static void test_http_provider_ecs_case() { - struct flb_http_client *c = NULL; + struct flb_aws_provider *provider; + struct flb_aws_credentials *creds; + struct flb_config *config; + int ret; - TEST_CHECK(method == FLB_HTTP_GET); + setenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "/iam_credentials/pod1", 1); + + setup_test(FLB_AWS_CLIENT_MOCK( + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER_COUNT, 0), + set(STATUS, 200), + set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n" + " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEKSXXX\",\n \"SecretAccessKey\"" + " : \"XSECRETEKSXXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEKSXXXXXXXXXXXXXXX==\",\n" + " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), + set(PAYLOAD_SIZE, 257) + ), + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + set(STATUS, 200), + set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n" + " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEKSXXX\",\n \"SecretAccessKey\"" + " : \"YSECRETEKSXXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEKSXXXXXXXXXXXXXXX==\",\n" + " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), // Expires Year 3021 + set(PAYLOAD_SIZE, 257) + ) + ), &provider, &config); + + flb_time_msleep(1000); + + /* Repeated calls to get credentials should return the same set */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("XACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("XSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("XTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); - TEST_CHECK(strstr(uri, "error-case") != NULL); + flb_aws_credentials_destroy(creds); - /* create an http client so that we can set the response */ - c = flb_calloc(1, sizeof(struct flb_http_client)); - if (!c) { - flb_errno(); - return NULL; - } - mk_list_init(&c->headers); + /* Retrieve from cache */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("XACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("XSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("XTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); + + flb_aws_credentials_destroy(creds); + + /* refresh should return 0 (success) */ + ret = provider->provider_vtable->refresh(provider); + TEST_CHECK(ret == 0); - c->resp.status = 400; - c->resp.payload = NULL; - c->resp.payload_size = 0; + /* Retrieve refreshed credentials from cache */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("YACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("YSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("YTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); + + flb_aws_credentials_destroy(creds); - return c; + /* Check we have exhausted our response list */ + TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0); + + cleanup_test(provider, config); } -/* test/mock version of the flb_aws_client request function */ -struct flb_http_client *test_http_client_request(struct flb_aws_client *aws_client, - int method, const char *uri, - const char *body, size_t body_len, - struct flb_aws_header *dynamic_headers, - size_t dynamic_headers_len) +static void test_http_provider_eks_with_token() { - g_request_count++; - /* - * route to the correct test case fn using the uri - */ - if (strstr(uri, "happy-case") != NULL) { - return request_happy_case(aws_client, method, uri); - } else if (strstr(uri, "error-case") != NULL) { - return request_error_case(aws_client, method, uri); - } else if (strstr(uri, "malformed") != NULL) { - return request_malformed(aws_client, method, uri); - } + struct flb_aws_provider *provider; + struct flb_aws_credentials *creds; + struct flb_config *config; + int ret; + + setenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "/iam_credentials/pod1", 1); + setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN", "password", 1); + + setup_test(FLB_AWS_CLIENT_MOCK( + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER, "Authorization", "password"), + set(STATUS, 200), + set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n" + " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEKSXXX\",\n \"SecretAccessKey\"" + " : \"XSECRETEKSXXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEKSXXXXXXXXXXXXXXX==\",\n" + " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), + set(PAYLOAD_SIZE, 257) + ), + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER, "Authorization", "password"), + set(STATUS, 200), + set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n" + " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEKSXXX\",\n \"SecretAccessKey\"" + " : \"YSECRETEKSXXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEKSXXXXXXXXXXXXXXX==\",\n" + " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), + set(PAYLOAD_SIZE, 257) + ) + ), &provider, &config); + + flb_time_msleep(1000); + + /* Repeated calls to get credentials should return the same set */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("XACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("XSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("XTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); - /* uri should match one of the above conditions */ - flb_errno(); - return NULL; + flb_aws_credentials_destroy(creds); -} + /* Retrieve from cache */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("XACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("XSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("XTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); -/* Test/mock flb_aws_client */ -static struct flb_aws_client_vtable test_vtable = { - .request = test_http_client_request, -}; + flb_aws_credentials_destroy(creds); -struct flb_aws_client *test_http_client_create() -{ - struct flb_aws_client *client = flb_calloc(1, - sizeof(struct flb_aws_client)); - if (!client) { - flb_errno(); - return NULL; - } - client->client_vtable = &test_vtable; - return client; -} + /* refresh should return 0 (success) */ + ret = provider->provider_vtable->refresh(provider); + TEST_CHECK(ret == 0); -/* Generator that returns clients with the test vtable */ -static struct flb_aws_client_generator test_generator = { - .create = test_http_client_create, -}; + /* Retrieve refreshed credentials from cache */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("YACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("YSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("YTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); -struct flb_aws_client_generator *generator_in_test() -{ - return &test_generator; + flb_aws_credentials_destroy(creds); + + /* Check we have exhausted our response list */ + TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0); + + cleanup_test(provider, config); } -/* http and ecs providers */ -static void test_http_provider() +static void test_http_provider_eks_with_token_file() { struct flb_aws_provider *provider; struct flb_aws_credentials *creds; - int ret; struct flb_config *config; - flb_sds_t host; - flb_sds_t path; + int ret; - g_request_count = 0; + /* + * tests validation of valid non-default local loopback IP + * tests token file takes precedence over token variable + */ + setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "http://127.0.0.7:80/iam_credentials/pod1", 1); + setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN", "password", 1); + setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", HTTP_TOKEN_FILE, 1); + + setup_test(FLB_AWS_CLIENT_MOCK( + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER, "Authorization", "this-is-a-fake-http-jwt"), + set(STATUS, 200), + set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n" + " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEKSXXX\",\n \"SecretAccessKey\"" + " : \"XSECRETEKSXXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEKSXXXXXXXXXXXXXXX==\",\n" + " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), + set(PAYLOAD_SIZE, 257) + ), + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER, "Authorization", "this-is-a-fake-http-jwt"), + set(STATUS, 200), + set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n" + " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEKSXXX\",\n \"SecretAccessKey\"" + " : \"YSECRETEKSXXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEKSXXXXXXXXXXXXXXX==\",\n" + " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), + set(PAYLOAD_SIZE, 257) + ) + ), &provider, &config); + + flb_time_msleep(1000); + + /* Repeated calls to get credentials should return the same set */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("XACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("XSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("XTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); - config = flb_config_init(); + flb_aws_credentials_destroy(creds); - if (config == NULL) { - return; - } + /* Retrieve from cache */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("XACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("XSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("XTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); - host = flb_sds_create("127.0.0.1"); - if (!host) { - flb_errno(); - flb_config_exit(config); - return; - } - path = flb_sds_create("/happy-case"); - if (!path) { - flb_errno(); - flb_config_exit(config); - return; - } + flb_aws_credentials_destroy(creds); - provider = flb_http_provider_create(config, host, path, - generator_in_test()); + /* refresh should return 0 (success) */ + ret = provider->provider_vtable->refresh(provider); + TEST_CHECK(ret == 0); - if (!provider) { - flb_errno(); - flb_config_exit(config); - return; - } + /* Retrieve refreshed credentials from cache */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("YACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("YSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("YTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); + + flb_aws_credentials_destroy(creds); + + /* Check we have exhausted our response list */ + TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0); + + cleanup_test(provider, config); +} + +static void test_http_provider_https_endpoint() +{ + struct flb_aws_provider *provider; + struct flb_aws_credentials *creds; + struct flb_config *config; + int ret; - /* repeated calls to get credentials should return the same set */ + setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "https://customers-vpc-credential-vending-server/iam_credentials/pod1", 1); + setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", HTTP_TOKEN_FILE, 1); + + setup_test(FLB_AWS_CLIENT_MOCK( + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER, "Authorization", "this-is-a-fake-http-jwt"), + set(STATUS, 200), + set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n" + " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEKSXXX\",\n \"SecretAccessKey\"" + " : \"XSECRETEKSXXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEKSXXXXXXXXXXXXXXX==\",\n" + " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), + set(PAYLOAD_SIZE, 257) + ), + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER, "Authorization", "this-is-a-fake-http-jwt"), + set(STATUS, 200), + set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n" + " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEKSXXX\",\n \"SecretAccessKey\"" + " : \"YSECRETEKSXXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEKSXXXXXXXXXXXXXXX==\",\n" + " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), + set(PAYLOAD_SIZE, 257) + ) + ), &provider, &config); + + flb_time_msleep(1000); + + /* Repeated calls to get credentials should return the same set */ creds = provider->provider_vtable->get_credentials(provider); - if (!creds) { - flb_errno(); - flb_config_exit(config); - return; - } - TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0); - TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0); - TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("XACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("XSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("XTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); flb_aws_credentials_destroy(creds); + /* Retrieve from cache */ creds = provider->provider_vtable->get_credentials(provider); - if (!creds) { - flb_errno(); - flb_config_exit(config); - return; - } - TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0); - TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0); - TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("XACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("XSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("XTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); flb_aws_credentials_destroy(creds); @@ -238,145 +410,141 @@ static void test_http_provider() ret = provider->provider_vtable->refresh(provider); TEST_CHECK(ret == 0); - /* - * Request count should be 2: - * - One for the first call to get_credentials (2nd should hit cred cache) - * - One for the call to refresh - */ - TEST_CHECK(g_request_count == 2); + /* Retrieve refreshed credentials from cache */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds != NULL); + TEST_CHECK(strcmp("YACCESSEKSXXX", creds->access_key_id) == 0); + TEST_CHECK(strcmp("YSECRETEKSXXXXXXXXXXXXXX", creds->secret_access_key) == 0); + TEST_CHECK(strcmp("YTOKENEKSXXXXXXXXXXXXXXX==", creds->session_token) == 0); + + flb_aws_credentials_destroy(creds); - flb_aws_provider_destroy(provider); - flb_config_exit(config); + /* Check we have exhausted our response list */ + TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0); + + cleanup_test(provider, config); } -static void test_http_provider_error_case() +static void test_http_provider_server_failure() { struct flb_aws_provider *provider; struct flb_aws_credentials *creds; - int ret; struct flb_config *config; - flb_sds_t host; - flb_sds_t path; - - g_request_count = 0; + int ret; - config = flb_config_init(); + setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "https://customers-vpc-credential-vending-server/iam_credentials/pod1", 1); + setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", HTTP_TOKEN_FILE, 1); + + setup_test(FLB_AWS_CLIENT_MOCK( + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER, "Authorization", "this-is-a-fake-http-jwt"), + set(STATUS, 400), + set(PAYLOAD, "{\"Message\": \"Invalid Authorization token\",\"Code\": \"ClientError\"}"), + set(PAYLOAD_SIZE, 64) + ), + response( + expect(URI, "/iam_credentials/pod1"), + expect(METHOD, FLB_HTTP_GET), + expect(HEADER, "Authorization", "this-is-a-fake-http-jwt"), + set(STATUS, 500), + set(PAYLOAD, "{\"Message\": \"Internal Server Error\",\"Code\": \"ServerError\"}"), + set(PAYLOAD_SIZE, 58) + ) + ), &provider, &config); + + flb_time_msleep(1000); + + /* Endpoint failure, no creds returnd */ + creds = provider->provider_vtable->get_credentials(provider); + TEST_ASSERT(creds == NULL); - if (config == NULL) { - return; - } + /* refresh should return 0 (success) */ + ret = provider->provider_vtable->refresh(provider); + TEST_CHECK(ret != 0); - host = flb_sds_create("127.0.0.1"); - if (!host) { - flb_errno(); - flb_config_exit(config); - return; - } - path = flb_sds_create("/error-case"); - if (!path) { - flb_errno(); - flb_config_exit(config); - return; - } + /* Check we have exhausted our response list */ + TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0); - provider = flb_http_provider_create(config, host, path, - generator_in_test()); + cleanup_test(provider, config); +} - if (!provider) { - flb_errno(); - flb_config_exit(config); - return; - } +static void test_http_validator_invalid_auth_token() +{ + struct flb_aws_provider *provider; + struct flb_config *config; - /* get_credentials will fail */ - creds = provider->provider_vtable->get_credentials(provider); - TEST_CHECK(creds == NULL); + setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "http://169.254.70.2:80/iam_credentials/pod1", 1); + setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN", "password\\r\\n", 1); - creds = provider->provider_vtable->get_credentials(provider); - TEST_CHECK(creds == NULL); + flb_aws_client_mock_configure_generator(NULL); - /* refresh should return -1 (failure) */ - ret = provider->provider_vtable->refresh(provider); - TEST_CHECK(ret < 0); + config = flb_calloc(1, sizeof(struct flb_config)); + TEST_ASSERT(config != NULL); + mk_list_init(&config->upstreams); - /* - * Request count should be 3: - * - Each call to get_credentials and refresh invokes the client's - * request method and returns a request failure. - */ - TEST_CHECK(g_request_count == 3); + /* provider creation will fail with error message indicating port was invalid */ + provider = flb_container_provider_create(config, flb_aws_client_get_mock_generator()); + TEST_ASSERT(provider == NULL); - flb_aws_provider_destroy(provider); - flb_config_exit(config); + flb_aws_client_mock_destroy_generator(); + flb_free(config); } -static void test_http_provider_malformed_response() +static void test_http_validator_invalid_host() { struct flb_aws_provider *provider; - struct flb_aws_credentials *creds; - int ret; struct flb_config *config; - flb_sds_t host; - flb_sds_t path; - g_request_count = 0; + setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "http://104.156.107.142:80/iam_credentials/pod1", 1); + setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN", "password", 1); - config = flb_config_init(); - - if (config == NULL) { - return; - } + flb_aws_client_mock_configure_generator(NULL); + config = flb_calloc(1, sizeof(struct flb_config)); + TEST_ASSERT(config != NULL); mk_list_init(&config->upstreams); - host = flb_sds_create("127.0.0.1"); - if (!host) { - flb_errno(); - flb_config_exit(config); - return; - } - path = flb_sds_create("/malformed"); - if (!path) { - flb_errno(); - flb_config_exit(config); - return; - } + /* provider creation will fail with error message indicating host was invalid */ + provider = flb_container_provider_create(config, flb_aws_client_get_mock_generator()); + TEST_ASSERT(provider == NULL); - provider = flb_http_provider_create(config, host, path, - generator_in_test()); + flb_aws_client_mock_destroy_generator(); + flb_free(config); +} - if (!provider) { - flb_errno(); - flb_config_exit(config); - return; - } +static void test_http_validator_invalid_port() +{ + struct flb_aws_provider *provider; + struct flb_config *config; - /* get_credentials will fail */ - creds = provider->provider_vtable->get_credentials(provider); - TEST_CHECK(creds == NULL); + setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "http://169.254.70.2:AA/iam_credentials/pod1", 1); + setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN", "password", 1); - creds = provider->provider_vtable->get_credentials(provider); - TEST_CHECK(creds == NULL); + flb_aws_client_mock_configure_generator(NULL); - /* refresh should return -1 (failure) */ - ret = provider->provider_vtable->refresh(provider); - TEST_CHECK(ret < 0); + config = flb_calloc(1, sizeof(struct flb_config)); + TEST_ASSERT(config != NULL); + mk_list_init(&config->upstreams); - /* - * Request count should be 3: - * - Each call to get_credentials and refresh invokes the client's - * request method and returns a request failure. - */ - TEST_CHECK(g_request_count == 3); + /* provider creation will fail with error message indicating port was invalid */ + provider = flb_container_provider_create(config, flb_aws_client_get_mock_generator()); + TEST_ASSERT(provider == NULL); - flb_aws_provider_destroy(provider); - flb_config_exit(config); + flb_aws_client_mock_destroy_generator(); + flb_free(config); } TEST_LIST = { - { "test_http_provider" , test_http_provider}, - { "test_http_provider_error_case" , test_http_provider_error_case}, - { "test_http_provider_malformed_response" , - test_http_provider_malformed_response}, + { "test_http_provider_malformed_response" , test_http_provider_malformed_response}, + { "test_http_provider_ecs_case" , test_http_provider_ecs_case}, + { "test_http_provider_eks_with_token" , test_http_provider_eks_with_token}, + { "test_http_provider_eks_with_token_file" , test_http_provider_eks_with_token_file}, + { "test_http_provider_https_endpoint" , test_http_provider_https_endpoint}, + { "test_http_provider_server_failure" , test_http_provider_server_failure}, + { "test_http_validator_invalid_auth_token" , test_http_validator_invalid_auth_token}, + { "test_http_validator_invalid_host" , test_http_validator_invalid_host}, + { "test_http_validator_invalid_port" , test_http_validator_invalid_port}, { 0 } }; diff --git a/tests/internal/data/aws_credentials/http_token_file.txt b/tests/internal/data/aws_credentials/http_token_file.txt new file mode 100644 index 00000000000..a6884e3eacf --- /dev/null +++ b/tests/internal/data/aws_credentials/http_token_file.txt @@ -0,0 +1 @@ +this-is-a-fake-http-jwt \ No newline at end of file diff --git a/tests/internal/utils.c b/tests/internal/utils.c index bcb9053d86f..73591f634ef 100644 --- a/tests/internal/utils.c +++ b/tests/internal/utils.c @@ -35,6 +35,10 @@ struct url_check url_checks[] = { {0, "https://fluentbit.io:1234", "https", "fluentbit.io", "1234", "/"}, {0, "https://fluentbit.io:1234/", "https", "fluentbit.io", "1234", "/"}, {0, "https://fluentbit.io:1234/v", "https", "fluentbit.io", "1234", "/v"}, + {0, "http://[fd00:ec2::23]", "http", "fd00:ec2::23", "80", "/"}, + {0, "http://[fd00:ec2::23]:81", "http", "fd00:ec2::23", "81", "/"}, + {0, "http://[fd00:ec2::23]:81/something", "http", "fd00:ec2::23", "81", "/something"}, + {0, "http://[fd00:ec2::23]/something", "http", "fd00:ec2::23", "80", "/something"}, {-1, "://", NULL, NULL, NULL, NULL}, };