googleapis / googleapis/google-auth-library-ruby
Support AWS ECS task role credentials in ExternalAccount::AwsCredentials
- 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
Assessment
This issue has not been assessed yet.