Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package aws-c-auth for openSUSE:Factory checked in at 2024-08-01 22:06:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-c-auth (Old) and /work/SRC/openSUSE:Factory/.aws-c-auth.new.7232 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "aws-c-auth" Thu Aug 1 22:06:05 2024 rev:7 rq:1191008 version:0.7.23 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-c-auth/aws-c-auth.changes 2024-06-14 19:07:22.487039173 +0200 +++ /work/SRC/openSUSE:Factory/.aws-c-auth.new.7232/aws-c-auth.changes 2024-08-01 22:06:50.538438626 +0200 @@ -1,0 +2,10 @@ +Wed Jul 31 09:10:10 UTC 2024 - John Paul Adrian Glaubitz <[email protected]> + +- Update to version 0.7.23 + * Ecforce ECS Credentials Provider IP Rules by @waahm7 in (#238) + * clang-format 18 by @graebm in (#242) + * Update MacOS to arm64 by @waahm7 in (#243) + * Support "external_id" in config file, + for STS AssumeRole by @graebm in (#244) + +------------------------------------------------------------------- Old: ---- v0.7.22.tar.gz New: ---- v0.7.23.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-c-auth.spec ++++++ --- /var/tmp/diff_new_pack.IgR9PM/_old 2024-08-01 22:06:51.082461075 +0200 +++ /var/tmp/diff_new_pack.IgR9PM/_new 2024-08-01 22:06:51.086461240 +0200 @@ -20,7 +20,7 @@ %define library_pkg 1_0_0 %define library_soversion 1 Name: aws-c-auth -Version: 0.7.22 +Version: 0.7.23 Release: 0 Summary: AWS C99 library implementation of AWS client-side authentication License: Apache-2.0 ++++++ v0.7.22.tar.gz -> v0.7.23.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/.github/workflows/ci.yml new/aws-c-auth-0.7.23/.github/workflows/ci.yml --- old/aws-c-auth-0.7.22/.github/workflows/ci.yml 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/.github/workflows/ci.yml 2024-07-30 22:21:36.000000000 +0200 @@ -6,7 +6,7 @@ - 'main' env: - BUILDER_VERSION: v0.9.55 + BUILDER_VERSION: v0.9.62 BUILDER_SOURCE: releases BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net PACKAGE_NAME: aws-c-auth @@ -20,6 +20,7 @@ linux-compat: runs-on: ubuntu-20.04 # latest strategy: + fail-fast: false matrix: image: - manylinux1-x64 @@ -40,6 +41,7 @@ linux-compiler-compat: runs-on: ubuntu-20.04 # latest strategy: + fail-fast: false matrix: compiler: - clang-3 @@ -80,6 +82,7 @@ windows-vc14: runs-on: windows-2019 # windows-2019 is last env with Visual Studio 2015 (v14.0) strategy: + fail-fast: false matrix: arch: [x86, x64] steps: @@ -107,8 +110,17 @@ run: | python .\aws-c-auth\build\deps\aws-c-common\scripts\appverifier_ctest.py --build_directory .\aws-c-auth\build\aws-c-auth - osx: - runs-on: macos-12 # latest + macos: + runs-on: macos-14 # latest + steps: + - name: Build ${{ env.PACKAGE_NAME }} + consumers + run: | + python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" + chmod a+x builder + ./builder build -p ${{ env.PACKAGE_NAME }} + + macos-x64: + runs-on: macos-14-large # latest steps: - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/.github/workflows/clang-format.yml new/aws-c-auth-0.7.23/.github/workflows/clang-format.yml --- old/aws-c-auth-0.7.22/.github/workflows/clang-format.yml 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/.github/workflows/clang-format.yml 2024-07-30 22:21:36.000000000 +0200 @@ -5,14 +5,12 @@ jobs: clang-format: - runs-on: ubuntu-20.04 # latest + runs-on: ubuntu-24.04 # latest steps: - name: Checkout Sources - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: clang-format lint - uses: DoozyX/[email protected] - with: - # List of extensions to check - extensions: c,h + run: | + ./format-check.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/format-check.py new/aws-c-auth-0.7.23/format-check.py --- old/aws-c-auth-0.7.22/format-check.py 1970-01-01 01:00:00.000000000 +0100 +++ new/aws-c-auth-0.7.23/format-check.py 2024-07-30 22:21:36.000000000 +0200 @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +import argparse +import os +from pathlib import Path +import re +from subprocess import list2cmdline, run +from tempfile import NamedTemporaryFile + +CLANG_FORMAT_VERSION = '18.1.6' + +INCLUDE_REGEX = re.compile( + r'^(include|source|tests|verification)/.*\.(c|h|inl)$') +EXCLUDE_REGEX = re.compile(r'^$') + +arg_parser = argparse.ArgumentParser(description="Check with clang-format") +arg_parser.add_argument('-i', '--inplace-edit', action='store_true', + help="Edit files inplace") +args = arg_parser.parse_args() + +os.chdir(Path(__file__).parent) + +# create file containing list of all files to format +filepaths_file = NamedTemporaryFile(delete=False) +for dirpath, dirnames, filenames in os.walk('.'): + for filename in filenames: + # our regexes expect filepath to use forward slash + filepath = Path(dirpath, filename).as_posix() + if not INCLUDE_REGEX.match(filepath): + continue + if EXCLUDE_REGEX.match(filepath): + continue + + filepaths_file.write(f"{filepath}\n".encode()) +filepaths_file.close() + +# use pipx to run clang-format from PyPI +# this is a simple way to run the same clang-format version regardless of OS +cmd = ['pipx', 'run', f'clang-format=={CLANG_FORMAT_VERSION}', + f'--files={filepaths_file.name}'] +if args.inplace_edit: + cmd += ['-i'] +else: + cmd += ['--Werror', '--dry-run'] + +print(f"{Path.cwd()}$ {list2cmdline(cmd)}") +if run(cmd).returncode: + exit(1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/format-check.sh new/aws-c-auth-0.7.23/format-check.sh --- old/aws-c-auth-0.7.22/format-check.sh 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/format-check.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,24 +0,0 @@ -#!/bin/bash - -if [[ -z $CLANG_FORMAT ]] ; then - CLANG_FORMAT=clang-format -fi - -if NOT type $CLANG_FORMAT 2> /dev/null ; then - echo "No appropriate clang-format found." - exit 1 -fi - -FAIL=0 -SOURCE_FILES=`find source include tests -type f \( -name '*.h' -o -name '*.c' \)` -for i in $SOURCE_FILES -do - $CLANG_FORMAT -output-replacements-xml $i | grep -c "<replacement " > /dev/null - if [ $? -ne 1 ] - then - echo "$i failed clang-format check." - FAIL=1 - fi -done - -exit $FAIL diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/include/aws/auth/auth.h new/aws-c-auth-0.7.23/include/aws/auth/auth.h --- old/aws-c-auth-0.7.22/include/aws/auth/auth.h 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/include/aws/auth/auth.h 2024-07-30 22:21:36.000000000 +0200 @@ -52,6 +52,7 @@ AWS_AUTH_IMDS_CLIENT_SOURCE_FAILURE, AWS_AUTH_PROFILE_STS_CREDENTIALS_PROVIDER_CYCLE_FAILURE, AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_TOKEN_FILE_PATH, + AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_HOST, AWS_AUTH_ERROR_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_AUTH_PACKAGE_ID) }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/include/aws/auth/credentials.h new/aws-c-auth-0.7.23/include/aws/auth/credentials.h --- old/aws-c-auth-0.7.22/include/aws/auth/credentials.h 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/include/aws/auth/credentials.h 2024-07-30 22:21:36.000000000 +0200 @@ -232,6 +232,11 @@ * AWS_CONTAINER_CREDENTIALS_RELATIVE_URI * AWS_CONTAINER_CREDENTIALS_FULL_URI * + *`AWS_CONTAINER_CREDENTIALS_FULL_URI` URL must satisfy one of the following: + * 1. The URL begins with `https`. + * 2. The resolved IP address is within the loopback CIDR (IPv4 127.0.0.0/8, IPv6 ::1/128), ECS container address + * (169.254.170.2), or EKS Pod Identity address (169.254.170.23 or fd00:ec2::23). + * * For the Authorization token, there are two ways (in order of priority): * 1. AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (an env var which contains the absolute path to the token file. The file * will be re-read for each call to get credentials.) @@ -261,6 +266,11 @@ * Configuration options for the provider that sources credentials from ECS container metadata. * This options struct doesn't read anything from the environment and requires everything to be explicitly passed in. If * you need to read properties from the environment, use the `aws_credentials_provider_ecs_environment_options`. + * + *`host` must satisfy one of the following: + * 1. tls_context is set + * 2. The resolved IP address is within the loopback CIDR (IPv4 127.0.0.0/8, IPv6 ::1/128), ECS container address + * (169.254.170.2), or EKS Pod Identity address (169.254.170.23 or fd00:ec2::23). */ struct aws_credentials_provider_ecs_options { struct aws_credentials_provider_shutdown_options shutdown_options; @@ -500,6 +510,11 @@ struct aws_byte_cursor session_name; /* + * (Optional) Unique identifier for assuming a role in another account + */ + struct aws_byte_cursor external_id; + + /* * How long sourced credentials should remain valid for, in seconds. 900 is the minimum allowed value. */ uint16_t duration_seconds; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/include/aws/auth/private/credentials_utils.h new/aws-c-auth-0.7.23/include/aws/auth/private/credentials_utils.h --- old/aws-c-auth-0.7.22/include/aws/auth/private/credentials_utils.h 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/include/aws/auth/private/credentials_utils.h 2024-07-30 22:21:36.000000000 +0200 @@ -30,9 +30,9 @@ void *user_data; }; -typedef struct aws_http_connection_manager *(aws_http_connection_manager_new_fn)( - struct aws_allocator *allocator, - const struct aws_http_connection_manager_options *options); +typedef struct aws_http_connection_manager *( + aws_http_connection_manager_new_fn)(struct aws_allocator *allocator, + const struct aws_http_connection_manager_options *options); typedef void(aws_http_connection_manager_release_fn)(struct aws_http_connection_manager *manager); typedef void(aws_http_connection_manager_acquire_connection_fn)( struct aws_http_connection_manager *manager, @@ -41,9 +41,9 @@ typedef int(aws_http_connection_manager_release_connection_fn)( struct aws_http_connection_manager *manager, struct aws_http_connection *connection); -typedef struct aws_http_stream *(aws_http_connection_make_request_fn)( - struct aws_http_connection *client_connection, - const struct aws_http_make_request_options *options); +typedef struct aws_http_stream *( + aws_http_connection_make_request_fn)(struct aws_http_connection *client_connection, + const struct aws_http_make_request_options *options); typedef int(aws_http_stream_activate_fn)(struct aws_http_stream *stream); typedef struct aws_http_connection *(aws_http_stream_get_connection_fn)(const struct aws_http_stream *stream); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/source/auth.c new/aws-c-auth-0.7.23/source/auth.c --- old/aws-c-auth-0.7.22/source/auth.c 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/source/auth.c 2024-07-30 22:21:36.000000000 +0200 @@ -112,6 +112,9 @@ AWS_DEFINE_ERROR_INFO_AUTH( AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_TOKEN_FILE_PATH, "Failed to read the ECS token file specified in the AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE environment variable."), + AWS_DEFINE_ERROR_INFO_AUTH( + AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_HOST, + "Failed to establish connection. The specified host is not allowed. It must be a loopback address, ECS/EKS container host, or use HTTPS."), }; /* clang-format on */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/source/credentials_provider_ecs.c new/aws-c-auth-0.7.23/source/credentials_provider_ecs.c --- old/aws-c-auth-0.7.22/source/credentials_provider_ecs.c 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/source/credentials_provider_ecs.c 2024-07-30 22:21:36.000000000 +0200 @@ -10,11 +10,14 @@ #include <aws/common/clock.h> #include <aws/common/date_time.h> #include <aws/common/environment.h> +#include <aws/common/host_utils.h> #include <aws/common/string.h> #include <aws/http/connection.h> #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> @@ -45,6 +48,8 @@ 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; }; /* @@ -459,6 +464,89 @@ s_ecs_query_task_role_credentials(ecs_user_data); } +/* + * The resolved IP address must satisfy one of the following: + * 1. within the loopback CIDR (IPv4 127.0.0.0/8, IPv6 ::1/128) + * 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(const struct aws_host_address *host_address) { + bool result = false; + struct aws_byte_cursor address = aws_byte_cursor_from_string(host_address->address); + if (host_address->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"); + + result |= aws_byte_cursor_starts_with(&address, &ipv4_loopback_address_prefix); + result |= aws_byte_cursor_eq(&address, &ecs_container_host_address); + result |= aws_byte_cursor_eq(&address, &eks_container_host_address); + + } else if (host_address->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"); + const struct aws_byte_cursor eks_container_host_ipv6_address_verbose = + aws_byte_cursor_from_c_str("fd00:ec2:0:0:0:0:0:23"); + + result |= aws_byte_cursor_eq(&address, &ipv6_loopback_address); + result |= aws_byte_cursor_eq(&address, &ipv6_loopback_address_verbose); + result |= aws_byte_cursor_eq(&address, &eks_container_host_ipv6_address); + result |= aws_byte_cursor_eq(&address, &eks_container_host_ipv6_address_verbose); + } + + 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; + } + 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); +} + static int s_credentials_provider_ecs_get_credentials_async( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, @@ -474,10 +562,19 @@ 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); - + /* No need to verify the host IP address if the connection is using HTTPS or the ECS container host (relative URI) + */ + 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: @@ -497,6 +594,7 @@ 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 @@ -554,7 +652,7 @@ 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) { @@ -568,6 +666,7 @@ aws_error_debug_str(aws_last_error())); goto on_error; } + impl->is_https = true; } struct aws_socket_options socket_options; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/source/credentials_provider_profile.c new/aws-c-auth-0.7.23/source/credentials_provider_profile.c --- old/aws-c-auth-0.7.22/source/credentials_provider_profile.c 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/source/credentials_provider_profile.c 2024-07-30 22:21:36.000000000 +0200 @@ -23,6 +23,7 @@ AWS_STRING_FROM_LITERAL(s_role_arn_name, "role_arn"); AWS_STRING_FROM_LITERAL(s_role_session_name_name, "role_session_name"); +AWS_STRING_FROM_LITERAL(s_external_id_name, "external_id"); AWS_STRING_FROM_LITERAL(s_credential_source_name, "credential_source"); AWS_STRING_FROM_LITERAL(s_source_profile_name, "source_profile"); AWS_STRING_FROM_LITERAL(s_access_key_id_profile_var, "aws_access_key_id"); @@ -235,6 +236,7 @@ struct aws_hash_table *source_profiles_table) { struct aws_credentials_provider *provider = NULL; + /* role_arn */ AWS_LOGF_INFO( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile %s has role_arn property is set to %s, attempting to " @@ -247,6 +249,7 @@ const struct aws_profile_property *credential_source_property = aws_profile_get_property(profile, s_credential_source_name); + /* role_session_name */ const struct aws_profile_property *role_session_name = aws_profile_get_property(profile, s_role_session_name_name); char session_name_array[MAX_SESSION_NAME_LEN + 1]; AWS_ZERO_ARRAY(session_name_array); @@ -274,6 +277,13 @@ AWS_LOGF_DEBUG(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: computed session_name as %s", session_name_array); + /* external_id */ + struct aws_byte_cursor external_id = {0}; + const struct aws_profile_property *external_id_property = aws_profile_get_property(profile, s_external_id_name); + if (external_id_property) { + external_id = aws_byte_cursor_from_string(aws_profile_property_get_value(external_id_property)); + } + /* Automatically create a TLS context if necessary. We'd prefer that users pass one in, but can't force * them to because aws_credentials_provider_profile_options didn't always have a tls_ctx member. */ struct aws_tls_ctx *tls_ctx = NULL; @@ -301,6 +311,7 @@ .bootstrap = options->bootstrap, .tls_ctx = tls_ctx, .role_arn = aws_byte_cursor_from_string(aws_profile_property_get_value(role_arn_property)), + .external_id = external_id, .session_name = aws_byte_cursor_from_c_str(session_name_array), .profile_name_override = options->profile_name_override, .config_file_name_override = options->config_file_name_override, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/source/credentials_provider_sts.c new/aws-c-auth-0.7.23/source/credentials_provider_sts.c --- old/aws-c-auth-0.7.22/source/credentials_provider_sts.c 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/source/credentials_provider_sts.c 2024-07-30 22:21:36.000000000 +0200 @@ -57,6 +57,7 @@ struct aws_http_connection_manager *connection_manager; struct aws_string *assume_role_profile; struct aws_string *role_session_name; + struct aws_string *external_id; struct aws_string *endpoint; struct aws_string *region; uint16_t duration_seconds; @@ -157,6 +158,18 @@ return AWS_OP_ERR; } + if (provider_impl->external_id != NULL) { + working_cur = aws_byte_cursor_from_c_str("&ExternalId="); + if (aws_byte_buf_append_dynamic(body, &working_cur)) { + return AWS_OP_ERR; + } + + struct aws_byte_cursor external_id_cur = aws_byte_cursor_from_string(provider_impl->external_id); + if (aws_byte_buf_append_encoding_uri_param(body, &external_id_cur)) { + return AWS_OP_ERR; + } + } + working_cur = aws_byte_cursor_from_c_str("&DurationSeconds="); if (aws_byte_buf_append_dynamic(body, &working_cur)) { return AWS_OP_ERR; @@ -652,6 +665,7 @@ aws_string_destroy(impl->role_session_name); aws_string_destroy(impl->assume_role_profile); + aws_string_destroy(impl->external_id); aws_string_destroy(impl->endpoint); aws_string_destroy(impl->region); aws_mem_release(provider->allocator, provider); @@ -739,6 +753,18 @@ return NULL; } + if (options->role_arn.len == 0) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "role_arn is necessary for querying STS"); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + if (options->session_name.len == 0) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "role_session_name is necessary for querying STS"); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + struct aws_credentials_provider *provider = NULL; struct aws_credentials_provider_sts_impl *impl = NULL; int result = AWS_OP_ERR; @@ -780,29 +806,28 @@ impl->role_session_name = aws_string_new_from_array(allocator, options->session_name.ptr, options->session_name.len); - - if (!impl->role_session_name) { - goto on_done; - } - AWS_LOGF_DEBUG( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): using session_name %s", + "(id=%p): using role_session_name '%s'", (void *)provider, aws_string_c_str(impl->role_session_name)); impl->assume_role_profile = aws_string_new_from_array(allocator, options->role_arn.ptr, options->role_arn.len); - - if (!impl->assume_role_profile) { - goto on_done; - } - AWS_LOGF_DEBUG( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): using assume_role_arn %s", + "(id=%p): using role_arn '%s'", (void *)provider, aws_string_c_str(impl->assume_role_profile)); + if (options->external_id.len > 0) { + impl->external_id = aws_string_new_from_cursor(allocator, &options->external_id); + AWS_LOGF_DEBUG( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): using external_id '%s'", + (void *)provider, + aws_string_c_str(impl->external_id)); + } + impl->duration_seconds = options->duration_seconds; if (options->system_clock_fn != NULL) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/tests/CMakeLists.txt new/aws-c-auth-0.7.23/tests/CMakeLists.txt --- old/aws-c-auth-0.7.22/tests/CMakeLists.txt 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/tests/CMakeLists.txt 2024-07-30 22:21:36.000000000 +0200 @@ -45,16 +45,17 @@ add_test_case(credentials_provider_imds_real_success) endif() -add_test_case(credentials_provider_ecs_new_destroy) -add_test_case(credentials_provider_ecs_connect_failure) -add_test_case(credentials_provider_ecs_request_failure) -add_test_case(credentials_provider_ecs_bad_document_failure) -add_test_case(credentials_provider_ecs_basic_success) -add_test_case(credentials_provider_ecs_basic_success_token_file) +add_net_test_case(credentials_provider_ecs_new_destroy) +add_net_test_case(credentials_provider_ecs_connect_failure) +add_net_test_case(credentials_provider_ecs_request_failure) +add_net_test_case(credentials_provider_ecs_bad_document_failure) +add_net_test_case(credentials_provider_ecs_bad_host_failure) +add_net_test_case(credentials_provider_ecs_basic_success) +add_net_test_case(credentials_provider_ecs_basic_success_token_file) add_net_test_case(credentials_provider_ecs_basic_success_uri_env) -add_test_case(credentials_provider_ecs_no_auth_token_success) -add_test_case(credentials_provider_ecs_success_multi_part_doc) -add_test_case(credentials_provider_ecs_real_new_destroy) +add_net_test_case(credentials_provider_ecs_no_auth_token_success) +add_net_test_case(credentials_provider_ecs_success_multi_part_doc) +add_net_test_case(credentials_provider_ecs_real_new_destroy) if(AWS_BUILDING_ON_ECS) add_test_case(credentials_provider_ecs_real_success) @@ -84,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_external_id_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) @@ -92,6 +94,7 @@ add_net_test_case(credentials_provider_sts_direct_config_connection_failed) add_net_test_case(credentials_provider_sts_direct_config_service_fails) add_net_test_case(credentials_provider_sts_from_profile_config_succeeds) +add_net_test_case(credentials_provider_sts_from_profile_config_with_external_id) add_net_test_case(credentials_provider_sts_from_profile_config_with_chain) add_net_test_case(credentials_provider_sts_from_profile_config_with_ecs_credentials_source) add_net_test_case(credentials_provider_sts_from_profile_config_with_chain_and_profile_creds) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/tests/credentials_provider_ecs_tests.c new/aws-c-auth-0.7.23/tests/credentials_provider_ecs_tests.c --- old/aws-c-auth-0.7.22/tests/credentials_provider_ecs_tests.c 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/tests/credentials_provider_ecs_tests.c 2024-07-30 22:21:36.000000000 +0200 @@ -54,6 +54,7 @@ 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 @@ }; 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 @@ 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 @@ .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 @@ 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 @@ .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 @@ 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 @@ .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 @@ 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); @@ -488,6 +499,49 @@ AWS_TEST_CASE(credentials_provider_ecs_bad_document_failure, s_credentials_provider_ecs_bad_document_failure); +static int s_credentials_provider_ecs_bad_host_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + s_aws_ecs_tester_init(allocator); + + struct aws_credentials_provider_ecs_options options = { + .bootstrap = s_tester.bootstrap, + .function_table = &s_mock_function_table, + .shutdown_options = + { + .shutdown_callback = s_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .host = aws_byte_cursor_from_c_str("www.google.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"), + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); + + aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); + + s_aws_wait_for_credentials_result(); + + aws_mutex_lock(&s_tester.lock); + ASSERT_NULL(s_tester.credentials); + ASSERT_INT_EQUALS(AWS_AUTH_CREDENTIALS_PROVIDER_ECS_INVALID_HOST, s_tester.error_code); + aws_mutex_unlock(&s_tester.lock); + + aws_credentials_provider_release(provider); + + s_aws_wait_for_provider_shutdown_callback(); + + /* Because we mock the http connection manager, we never get a callback back from it */ + aws_mem_release(provider->allocator, provider); + + s_aws_ecs_tester_cleanup(); + + return 0; +} + +AWS_TEST_CASE(credentials_provider_ecs_bad_host_failure, s_credentials_provider_ecs_bad_host_failure); + AWS_STATIC_STRING_FROM_LITERAL( s_good_response, "{\"AccessKeyId\":\"SuccessfulAccessKey\", \n \"SecretAccessKey\":\"SuccessfulSecret\", \n " @@ -655,12 +709,13 @@ .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 +752,7 @@ .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 +763,7 @@ 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 +782,7 @@ 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); @@ -819,14 +875,33 @@ .auth_token_file_content = "testToken", .expected_auth_token = "testToken", }, + /* IPv4 loopback address */ + { + .full_uri = "http://127.1.2.3:456/credentials", + .expected_uri = "http://127.1.2.3:456/credentials", + }, + /* IPv4 EKS container host address */ + { + .full_uri = "http://169.254.170.23:8080/credentials", + .expected_uri = "http://169.254.170.23:8080/credentials", + }, + /* IPv4 ECS container host address */ + { + .full_uri = "http://169.254.170.2:8080/credentials", + .expected_uri = "http://169.254.170.2:8080/credentials", + }, + /* IPv6 loopback address */ + { + .full_uri = "http://[::1]:8080/credentials", + .expected_uri = "http://[::1]:8080/credentials", + }, + /* IPv6 EKS host */ + { + .full_uri = "http://[fd00:ec2::23]:8080/credentials", + .expected_uri = "http://[fd00:ec2::23]:8080/credentials", + }, }; - /* 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 +927,7 @@ .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 +943,6 @@ 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 +966,13 @@ }, .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:443/path/to/resource/?a=b&c=d" /*expected_uri*/, NULL /*expected_token*/)); s_aws_ecs_tester_cleanup(); @@ -935,6 +1009,7 @@ .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); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-auth-0.7.22/tests/credentials_provider_sts_tests.c new/aws-c-auth-0.7.23/tests/credentials_provider_sts_tests.c --- old/aws-c-auth-0.7.22/tests/credentials_provider_sts_tests.c 2024-05-15 20:46:22.000000000 +0200 +++ new/aws-c-auth-0.7.23/tests/credentials_provider_sts_tests.c 2024-07-30 22:21:36.000000000 +0200 @@ -388,6 +388,7 @@ static struct aws_byte_cursor s_role_arn_cur = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("arn:aws:iam::67895:role/test_role"); static struct aws_byte_cursor s_session_name_cur = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("test_session"); +static struct aws_byte_cursor s_external_id_cur = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("externalId123_+=,.@:\\/-"); static struct aws_byte_cursor s_success_creds_doc = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("<AssumeRoleResponse xmlns=\"who cares\">\n" @@ -410,6 +411,14 @@ AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Version=2011-06-15&Action=AssumeRole&RoleArn=arn%3Aaws%3Aiam%3A%3A67895%" "3Arole%2Ftest_role&RoleSessionName=test_session&DurationSeconds=900"); +static struct aws_byte_cursor s_expected_payload_with_external_id = + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Version=2011-06-15" + "&Action=AssumeRole" + "&RoleArn=arn%3Aaws%3Aiam%3A%3A67895%3Arole%2Ftest_role" + "&RoleSessionName=test_session" + "&ExternalId=externalId123_%2B%3D%2C.%40%3A%5C%2F-" + "&DurationSeconds=900"); + AWS_STATIC_STRING_FROM_LITERAL(s_access_key_id_response, "accessKeyIdResp"); AWS_STATIC_STRING_FROM_LITERAL(s_secret_access_key_response, "secretKeyResp"); AWS_STATIC_STRING_FROM_LITERAL(s_session_token_response, "sessionTokenResp"); @@ -508,6 +517,61 @@ AWS_TEST_CASE(credentials_provider_sts_direct_config_succeeds, s_credentials_provider_sts_direct_config_succeeds_fn) +static int s_credentials_provider_sts_direct_config_with_external_id_succeeds_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + s_aws_sts_tester_init(allocator); + + 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, + .external_id = s_external_id_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_BIN_ARRAYS_EQUALS( + s_expected_payload_with_external_id.ptr, + s_expected_payload_with_external_id.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_external_id_succeeds, + s_credentials_provider_sts_direct_config_with_external_id_succeeds_fn) + static int s_credentials_provider_sts_direct_config_with_region_succeeds_fn( struct aws_allocator *allocator, void *ctx) { @@ -1043,31 +1107,41 @@ credentials_provider_sts_direct_config_service_fails, s_credentials_provider_sts_direct_config_service_fails_fn) -static const char *s_soure_profile_config_file = "[default]\n" - "aws_access_key_id=BLAHBLAH\n" - "aws_secret_access_key=BLAHBLAHBLAH\n" - "\n" - "[roletest]\n" - "role_arn=arn:aws:iam::67895:role/test_role\n" - "source_profile=default\n" - "role_session_name=test_session"; - -static const char *s_soure_profile_chain_config_file = "[default]\n" - "aws_access_key_id=BLAHBLAH\n" - "aws_secret_access_key=BLAHBLAHBLAH\n" - "\n" - "[roletest]\n" - "role_arn=arn:aws:iam::67895:role/test_role\n" - "source_profile=roletest2\n" - "role_session_name=test_session\n" - "[roletest2]\n" - "role_arn=arn:aws:iam::67896:role/test_role\n" - "source_profile=roletest3\n" - "role_session_name=test_session2\n" - "[roletest3]\n" - "role_arn=arn:aws:iam::67897:role/test_role\n" - "source_profile=default\n" - "role_session_name=test_session3\n"; +static const char *s_source_profile_config_file = "[default]\n" + "aws_access_key_id=BLAHBLAH\n" + "aws_secret_access_key=BLAHBLAHBLAH\n" + "\n" + "[roletest]\n" + "role_arn=arn:aws:iam::67895:role/test_role\n" + "source_profile=default\n" + "role_session_name=test_session"; + +static const char *s_source_profile_external_id_config_file = "[default]\n" + "aws_access_key_id=BLAHBLAH\n" + "aws_secret_access_key=BLAHBLAHBLAH\n" + "\n" + "[roletest]\n" + "role_arn=arn:aws:iam::67895:role/test_role\n" + "source_profile=default\n" + "role_session_name=test_session\n" + "external_id=externalId123_+=,.@:\\/-\n"; + +static const char *s_source_profile_chain_config_file = "[default]\n" + "aws_access_key_id=BLAHBLAH\n" + "aws_secret_access_key=BLAHBLAHBLAH\n" + "\n" + "[roletest]\n" + "role_arn=arn:aws:iam::67895:role/test_role\n" + "source_profile=roletest2\n" + "role_session_name=test_session\n" + "[roletest2]\n" + "role_arn=arn:aws:iam::67896:role/test_role\n" + "source_profile=roletest3\n" + "role_session_name=test_session2\n" + "[roletest3]\n" + "role_arn=arn:aws:iam::67897:role/test_role\n" + "source_profile=default\n" + "role_session_name=test_session3\n"; static int s_credentials_provider_sts_from_profile_config_with_chain_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -1078,7 +1152,7 @@ s_aws_sts_tester_init(allocator); s_tester.expected_connection_manager_shutdown_callback_count = 3; - struct aws_string *config_contents = aws_string_new_from_c_str(allocator, s_soure_profile_chain_config_file); + struct aws_string *config_contents = aws_string_new_from_c_str(allocator, s_source_profile_chain_config_file); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_string *creds_file_str = aws_create_process_unique_file_name(allocator); @@ -1676,7 +1750,7 @@ s_aws_sts_tester_init(allocator); - struct aws_string *config_contents = aws_string_new_from_c_str(allocator, s_soure_profile_config_file); + struct aws_string *config_contents = aws_string_new_from_c_str(allocator, s_source_profile_config_file); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_string *creds_file_str = aws_create_process_unique_file_name(allocator); @@ -1763,6 +1837,63 @@ credentials_provider_sts_from_profile_config_manual_tls_succeeds, credentials_provider_sts_from_profile_config_manual_tls_succeeds_fn) +static int s_credentials_provider_sts_from_profile_config_with_external_id_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + aws_unset_environment_value(s_default_profile_env_variable_name); + aws_unset_environment_value(s_default_config_path_env_variable_name); + aws_unset_environment_value(s_default_credentials_path_env_variable_name); + + s_aws_sts_tester_init(allocator); + + struct aws_string *config_contents = aws_string_new_from_c_str(allocator, s_source_profile_external_id_config_file); + + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + struct aws_string *creds_file_str = aws_create_process_unique_file_name(allocator); + + ASSERT_SUCCESS(aws_create_profile_file(creds_file_str, config_contents)); + aws_string_destroy(config_contents); + + struct aws_credentials_provider_profile_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .credentials_file_name_override = aws_byte_cursor_from_string(creds_file_str), + .profile_name_override = aws_byte_cursor_from_c_str("roletest"), + .bootstrap = s_tester.bootstrap, + .tls_ctx = s_tester.tls_ctx, + .function_table = &s_mock_function_table, + }; + + aws_array_list_push_back(&s_tester.response_data_callbacks, &s_success_creds_doc); + s_tester.mock_response_code = 200; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_profile(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_string_destroy(config_file_str); + aws_string_destroy(creds_file_str); + + aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); + + s_aws_wait_for_credentials_result(); + + ASSERT_BIN_ARRAYS_EQUALS( + s_expected_payload_with_external_id.ptr, + s_expected_payload_with_external_id.len, + s_tester.mocked_requests[0].body.buffer, + s_tester.mocked_requests[0].body.len); + + aws_credentials_provider_release(provider); + s_aws_wait_for_provider_shutdown_callback(); + ASSERT_SUCCESS(s_aws_sts_tester_cleanup()); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE( + credentials_provider_sts_from_profile_config_with_external_id, + s_credentials_provider_sts_from_profile_config_with_external_id_fn) + static const char *s_env_source_config_file = "[default]\n" "aws_access_key_id=BLAHBLAH\n" "aws_secret_access_key=BLAHBLAHBLAH\n"
