googleapis / googleapis/google-auth-library-ruby

Support AWS ECS task role credentials in ExternalAccount::AwsCredentials

Open
#567 0 comments 8 reactions 1 assignee Claimed by @viacheslav-rostovtsev View on GitHub
Dominant language
Ruby
Stars
515
Forks
261
PR merge metrics
No merged PRs in 30d

Description

## 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](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html), 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:

```ruby
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

- **Java**: https://github.com/googleapis/google-cloud-java/issues/12605, https://github.com/googleapis/google-cloud-java/issues/12595
- **Python**: https://github.com/googleapis/google-auth-library-python/issues/1099

There is also a third-party Python package [`aws-ecs-gcp-workload-identity-federation`](https://pypi.org/project/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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.