Skip to content

Commit 9d7be1f

Browse files
PREQ-3880 AWS credentials refactor
1 parent 293e49e commit 9d7be1f

File tree

3 files changed

+85
-66
lines changed

3 files changed

+85
-66
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Adaptive cache action that automatically chooses the appropriate caching backend
1010

1111
## Requirements
1212

13+
- `aws` (AWS CLI)
1314
- `jq`
1415

1516
## Usage
@@ -111,21 +112,22 @@ Each environment has its own preconfigured S3 bucket and AWS Cognito pool for is
111112

112113
### AWS Credential Isolation
113114

114-
This action creates a dedicated AWS profile (`gh-action-cache-<run_id>`) to store its credentials.
115-
This ensures that cache operations work correctly even when you configure your own AWS credentials later in the workflow.
115+
This action configures a dedicated AWS profile (`gh-action-cache`) backed by `credential_process`.
116+
Credentials are fetched on demand using GitHub OIDC + Cognito and never exported to `GITHUB_ENV`.
117+
This ensures cache operations work correctly even when you configure your own AWS credentials later in the workflow.
116118

117119
**Why this matters**: The cache save operation happens in a GitHub Actions post-step (after your job completes).
118120
If you use `aws-actions/configure-aws-credentials` during your job, it would normally override the cache action's credentials,
119-
causing cache save to fail.
121+
causing cache save to fail. The cache profile stays isolated and is only used by the cache step.
120122

121123
**Example workflow that works correctly**:
122124

123125
```yaml
124126
jobs:
125127
build:
126128
steps:
127-
# Cache action authenticates and stores credentials in isolated profile
128-
- uses: SonarSource/gh-action-cache@v1
129+
# Cache action authenticates and uses isolated profile
130+
- uses: SonarSource/gh-action_cache@v1
129131
with:
130132
path: ~/.cache
131133
key: my-cache-${{ hashFiles('**/lockfile') }}

action.yml

Lines changed: 14 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -84,75 +84,25 @@ runs:
8484
lookup-only: ${{ inputs.lookup-only }}
8585

8686
# Cache with S3 (private/internal repos)
87-
- name: Authenticate to AWS
87+
- name: Configure cache credential profile
8888
if: steps.cache-backend.outputs.cache-backend == 's3'
89-
id: aws-auth
9089
shell: bash
9190
env:
9291
POOL_ID: ${{ inputs.environment == 'prod' && 'eu-central-1:511fe374-ae4f-46d0-adb7-9246e570c7f4' || 'eu-central-1:3221c6ea-3f67-4fd8-a7ff-7426f96add89' }}
9392
AWS_ACCOUNT_ID: ${{ inputs.environment == 'prod' && '275878209202' || '460386131003' }}
9493
IDENTITY_PROVIDER_NAME: token.actions.githubusercontent.com
9594
AUDIENCE: cognito-identity.amazonaws.com
9695
AWS_REGION: eu-central-1
97-
GITHUB_RUN_ID: ${{ github.run_id }}
9896
run: |
99-
# Get GitHub Actions ID token using script
100-
ACCESS_TOKEN=$("$GITHUB_ACTION_PATH/scripts/get-github-token.sh")
101-
echo "::add-mask::$ACCESS_TOKEN"
102-
103-
# Get Identity ID
104-
identityId=$(aws cognito-identity get-id \
105-
--identity-pool-id "$POOL_ID" \
106-
--account-id "$AWS_ACCOUNT_ID" \
107-
--logins '{"'"$IDENTITY_PROVIDER_NAME"'":"'"$ACCESS_TOKEN"'"}' \
108-
--query 'IdentityId' --output text)
109-
110-
# Validate Identity ID was obtained
111-
if [[ "$identityId" == "null" || -z "$identityId" ]]; then
112-
echo "::error::Failed to obtain Identity ID from Cognito Identity Pool"
113-
echo "::error::Check identity pool configuration and IAM roles"
114-
exit 1
115-
fi
116-
117-
# Get and validate AWS credentials
118-
awsCredentials=$(aws cognito-identity get-credentials-for-identity \
119-
--identity-id "$identityId" \
120-
--logins '{"'"$IDENTITY_PROVIDER_NAME"'":"'"$ACCESS_TOKEN"'"}')
121-
122-
AWS_ACCESS_KEY_ID=$(echo "$awsCredentials" | jq -r ".Credentials.AccessKeyId")
123-
AWS_SECRET_ACCESS_KEY=$(echo "$awsCredentials" | jq -r ".Credentials.SecretKey")
124-
AWS_SESSION_TOKEN=$(echo "$awsCredentials" | jq -r ".Credentials.SessionToken")
125-
if [[ "$AWS_ACCESS_KEY_ID" == "null" || -z "$AWS_ACCESS_KEY_ID" ]]; then
126-
echo "::error::Failed to obtain AWS Access Key ID"
127-
exit 1
128-
fi
129-
if [[ "$AWS_SECRET_ACCESS_KEY" == "null" || -z "$AWS_SECRET_ACCESS_KEY" ]]; then
130-
echo "::error::Failed to obtain AWS Secret Access Key"
131-
exit 1
132-
fi
133-
if [[ "$AWS_SESSION_TOKEN" == "null" || -z "$AWS_SESSION_TOKEN" ]]; then
134-
echo "::error::Failed to obtain AWS Session Token"
135-
exit 1
136-
fi
137-
echo "::add-mask::$AWS_ACCESS_KEY_ID"
138-
echo "::add-mask::$AWS_SECRET_ACCESS_KEY"
139-
echo "::add-mask::$AWS_SESSION_TOKEN"
140-
141-
# Create a unique AWS profile to isolate credentials from user-configured AWS credentials
142-
# This prevents credential override when users call aws-actions/configure-aws-credentials
143-
# between the cache restore (main step) and cache save (post step)
144-
PROFILE_NAME="gh-action-cache-${GITHUB_RUN_ID}"
145-
97+
PROFILE_NAME="gh-action-cache"
14698
mkdir -p ~/.aws
14799
chmod 700 ~/.aws
148100
149-
# Write credentials to a dedicated profile using AWS CLI (handles file format and permissions correctly)
150-
aws configure set aws_access_key_id "$AWS_ACCESS_KEY_ID" --profile "$PROFILE_NAME"
151-
aws configure set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" --profile "$PROFILE_NAME"
152-
aws configure set aws_session_token "$AWS_SESSION_TOKEN" --profile "$PROFILE_NAME"
153-
aws configure set region eu-central-1 --profile "$PROFILE_NAME"
154-
echo "Created AWS profile: $PROFILE_NAME"
155-
echo "AWS_PROFILE=$PROFILE_NAME" >> "$GITHUB_OUTPUT"
101+
cat <<CONFIG >> ~/.aws/config
102+
[profile ${PROFILE_NAME}]
103+
region = ${AWS_REGION}
104+
credential_process = ${GITHUB_ACTION_PATH}/scripts/cache-credential-process.sh
105+
CONFIG
156106
157107
- name: Prepare cache keys
158108
if: steps.cache-backend.outputs.cache-backend == 's3'
@@ -174,10 +124,13 @@ runs:
174124
RUNS_ON_S3_BUCKET_CACHE: sonarsource-s3-cache-${{ inputs.environment }}-bucket
175125
AWS_DEFAULT_REGION: eu-central-1
176126
AWS_REGION: eu-central-1
177-
# Use AWS profile instead of direct credentials to prevent override issues
178-
# When users configure their own AWS credentials mid-job, the profile remains isolated
179-
AWS_PROFILE: ${{ steps.aws-auth.outputs.AWS_PROFILE }}
180-
AWS_DEFAULT_PROFILE: ${{ steps.aws-auth.outputs.AWS_PROFILE }}
127+
AWS_SDK_LOAD_CONFIG: 1
128+
AWS_PROFILE: gh-action-cache
129+
AWS_DEFAULT_PROFILE: gh-action-cache
130+
POOL_ID: ${{ inputs.environment == 'prod' && 'eu-central-1:511fe374-ae4f-46d0-adb7-9246e570c7f4' || 'eu-central-1:3221c6ea-3f67-4fd8-a7ff-7426f96add89' }}
131+
AWS_ACCOUNT_ID: ${{ inputs.environment == 'prod' && '275878209202' || '460386131003' }}
132+
IDENTITY_PROVIDER_NAME: token.actions.githubusercontent.com
133+
AUDIENCE: cognito-identity.amazonaws.com
181134
with:
182135
path: ${{ inputs.path }}
183136
key: ${{ steps.prepare-keys.outputs.branch-key }}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
5+
6+
: "${POOL_ID:?}" "${AWS_ACCOUNT_ID:?}" "${IDENTITY_PROVIDER_NAME:?}" "${AUDIENCE:?}" "${AWS_REGION:?}"
7+
8+
if ! command -v aws >/dev/null 2>&1; then
9+
echo "::error title=AWS CLI is required for credential_process"
10+
exit 1
11+
fi
12+
if ! command -v jq >/dev/null 2>&1; then
13+
echo "::error title=jq is required for credential_process"
14+
exit 1
15+
fi
16+
17+
ACCESS_TOKEN=$("$script_dir/get-github-token.sh")
18+
19+
AWS_EC2_METADATA_DISABLED=true
20+
AWS_MAX_ATTEMPTS=3
21+
AWS_RETRY_MODE=standard
22+
23+
identity_id=$(timeout 30s aws --no-sign-request cognito-identity get-id \
24+
--identity-pool-id "$POOL_ID" \
25+
--account-id "$AWS_ACCOUNT_ID" \
26+
--logins "{\"${IDENTITY_PROVIDER_NAME}\":\"${ACCESS_TOKEN}\"}" \
27+
--query 'IdentityId' --output text)
28+
29+
if [[ "$identity_id" == "null" || -z "$identity_id" ]]; then
30+
echo "::error title=Failed to obtain Identity ID from Cognito Identity Pool"
31+
exit 1
32+
fi
33+
34+
aws_credentials=$(timeout 30s aws --no-sign-request cognito-identity get-credentials-for-identity \
35+
--identity-id "$identity_id" \
36+
--logins "{\"${IDENTITY_PROVIDER_NAME}\":\"${ACCESS_TOKEN}\"}")
37+
38+
access_key_id=$(echo "$aws_credentials" | jq -r ".Credentials.AccessKeyId")
39+
secret_access_key=$(echo "$aws_credentials" | jq -r ".Credentials.SecretKey")
40+
session_token=$(echo "$aws_credentials" | jq -r ".Credentials.SessionToken")
41+
expiration=$(echo "$aws_credentials" | jq -r ".Credentials.Expiration")
42+
43+
if [[ "$access_key_id" == "null" || -z "$access_key_id" ]]; then
44+
echo "::error title=Failed to obtain AWS Access Key ID"
45+
exit 1
46+
fi
47+
if [[ "$secret_access_key" == "null" || -z "$secret_access_key" ]]; then
48+
echo "::error title=Failed to obtain AWS Secret Access Key"
49+
exit 1
50+
fi
51+
if [[ "$session_token" == "null" || -z "$session_token" ]]; then
52+
echo "::error title=Failed to obtain AWS Session Token"
53+
exit 1
54+
fi
55+
56+
cat <<JSON
57+
{
58+
"Version": 1,
59+
"AccessKeyId": "${access_key_id}",
60+
"SecretAccessKey": "${secret_access_key}",
61+
"SessionToken": "${session_token}",
62+
"Expiration": "${expiration}"
63+
}
64+
JSON

0 commit comments

Comments
 (0)