Skip to content

Support AWS ECS task role credentials in ExternalAccount::AwsCredentials #567

@closer

Description

@closer

Summary

Google::Auth::ExternalAccount::AwsCredentials#fetch_security_credentials currently resolves AWS credentials from only two sources:

  1. Environment variables (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)
  2. EC2 instance metadata (IMDS)

When running on AWS ECS (Fargate or EC2 launch type), credentials are provided via the ECS credential provider, which uses AWS_CONTAINER_CREDENTIALS_RELATIVE_URI or AWS_CONTAINER_CREDENTIALS_FULL_URI environment variables to point to a local metadata endpoint. This endpoint is not the EC2 IMDS endpoint and has a different API format, so the existing EC2 metadata fallback does not work.

Expected behavior

fetch_security_credentials should check for ECS task role credentials (via AWS_CONTAINER_CREDENTIALS_RELATIVE_URI / AWS_CONTAINER_CREDENTIALS_FULL_URI) between the environment variable check and the EC2 metadata fallback.

Resolution order should be:

  1. ENV vars (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) — current behavior
  2. ECS task role via AWS_CONTAINER_CREDENTIALS_RELATIVE_URI — new
  3. EC2 instance metadata via credential_source.url — current fallback

Current workaround

We are using prepend to monkey-patch fetch_security_credentials with ECS support:

module AwsEcsCredentialSupport
  private

  def fetch_security_credentials
    env_key = ENV[Google::Auth::CredentialsLoader::AWS_ACCESS_KEY_ID_VAR]
    env_secret = ENV[Google::Auth::CredentialsLoader::AWS_SECRET_ACCESS_KEY_VAR]

    if env_key && env_secret
      return {
        access_key_id: env_key,
        secret_access_key: env_secret,
        session_token: ENV[Google::Auth::CredentialsLoader::AWS_SESSION_TOKEN_VAR]
      }
    end

    if ENV["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"] || ENV["AWS_CONTAINER_CREDENTIALS_FULL_URI"]
      creds = Aws::ECSCredentials.new.credentials
      if creds.access_key_id && creds.secret_access_key
        return {
          access_key_id: creds.access_key_id,
          secret_access_key: creds.secret_access_key,
          session_token: creds.session_token
        }
      end
    end

    super
  end
end

Google::Auth::ExternalAccount::AwsCredentials.prepend(AwsEcsCredentialSupport)

Related issues in other languages

There is also a third-party Python package aws-ecs-gcp-workload-identity-federation that works around this limitation.

Use case

We use GCP Workload Identity Federation to allow AWS ECS tasks to access Google Cloud resources (BigQuery) without long-lived service account keys. The ECS tasks run on Fargate, where the only credential source available is the ECS task role metadata endpoint.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions