Skip to content

fix(aws): configure S3 Express session provider grants #739

fix(aws): configure S3 Express session provider grants

fix(aws): configure S3 Express session provider grants #739

Workflow file for this run

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
name: AWS V4 Test
on:
push:
branches:
- main
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
env:
RUST_LOG: DEBUG
RUST_BACKTRACE: full
permissions: {}
jobs:
# Unit tests - always run, no secrets needed
unit_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Run unit tests
run: |
echo "::group::Running aws-core unit tests"
cargo test -p reqsign-aws-core --lib --no-fail-fast
echo "::endgroup::"
echo "::group::Running aws-v4 unit tests"
cargo test -p reqsign-aws-v4 --lib --no-fail-fast
cargo test -p reqsign-aws-v4 --doc --no-fail-fast
echo "::endgroup::"
# Check if we can run integration tests
check_secrets:
runs-on: ubuntu-latest
outputs:
has_secrets: ${{ steps.check.outputs.has_secrets }}
steps:
- name: Check if secrets are available
id: check
run: |
if [[ "${{ github.event_name }}" == "push" || ( "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.head.repo.fork }}" == "false" && "${{ github.actor }}" != "dependabot[bot]" ) ]]; then
echo "has_secrets=true" >> $GITHUB_OUTPUT
echo "::notice::Integration tests will be executed (base repository and non-dependabot PR)"
else
echo "has_secrets=false" >> $GITHUB_OUTPUT
echo "::warning::Integration tests will be skipped (forked repository, dependabot PR, or no secrets available)"
fi
# StaticCredentialProvider test
test_static_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
export-env: true
env:
AWS_ACCESS_KEY_ID: op://reqsign/aws-v4/access_key
AWS_SECRET_ACCESS_KEY: op://reqsign/aws-v4/secret_key
AWS_REGION: op://reqsign/aws-v4/region
- name: Test StaticCredentialProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_STATIC: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
run: cargo test test_static_credential_provider_live --no-fail-fast -- --nocapture
# DefaultCredentialProvider test
test_default_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
export-env: true
env:
AWS_ACCESS_KEY_ID: op://reqsign/aws-v4/access_key
AWS_SECRET_ACCESS_KEY: op://reqsign/aws-v4/secret_key
AWS_REGION: op://reqsign/aws-v4/region
- name: Test DefaultCredentialProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_DEFAULT: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
run: cargo test test_default_credential_provider_live --no-fail-fast -- --nocapture
# ProcessCredentialProvider test
test_process_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
export-env: true
env:
AWS_ACCESS_KEY_ID: op://reqsign/aws-v4/access_key
AWS_SECRET_ACCESS_KEY: op://reqsign/aws-v4/secret_key
AWS_REGION: op://reqsign/aws-v4/region
- name: Create credential_process profile
shell: bash
run: |
process_config="$RUNNER_TEMP/reqsign-process-config"
{
echo '[profile reqsign-live-process]'
echo "credential_process = python3 $GITHUB_WORKSPACE/services/aws-v4/tests/mocks/credential_process_helper.py"
echo "region = $AWS_REGION"
} > "$process_config"
echo "REQSIGN_PROCESS_CONFIG=$process_config" >> "$GITHUB_ENV"
- name: Test ProcessCredentialProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_PROCESS: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
run: |
export REQSIGN_AWS_PROCESS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID"
export REQSIGN_AWS_PROCESS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY"
export AWS_PROFILE=reqsign-live-process
export AWS_CONFIG_FILE="$REQSIGN_PROCESS_CONFIG"
cargo test test_process_credential_provider --no-fail-fast -- --nocapture
- name: Remove credential_process profile
if: always()
run: rm -f -- "${REQSIGN_PROCESS_CONFIG:-}"
# CognitoIdentityCredentialProvider test
test_cognito_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
env:
AWS_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
AWS_DEFAULT_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Test CognitoIdentityCredentialProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_COGNITO: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
AWS_COGNITO_IDENTITY_POOL_ID: ${{ vars.REQSIGN_AWS_LIVE_COGNITO_POOL_ID }}
run: cargo test test_cognito_identity_credential_provider --no-fail-fast -- --nocapture
# S3AccessGrantsGranter test
test_s3_access_grants_granter:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
env:
AWS_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
AWS_DEFAULT_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Get a GitHub OIDC token
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const token = await core.getIDToken('sts.amazonaws.com');
core.setSecret(token);
require('fs').writeFileSync('/tmp/reqsign-aws-role-token', token, { mode: 0o600 });
- name: Assume the reqsign live-test role
env:
ROLE_ARN: ${{ vars.REQSIGN_AWS_LIVE_ROLE_ARN }}
ROLE_SESSION_NAME: reqsign-access-grants-${{ github.run_id }}
shell: bash
run: |
credentials="$(aws sts assume-role-with-web-identity \
--role-arn "$ROLE_ARN" \
--role-session-name "$ROLE_SESSION_NAME" \
--web-identity-token "$(tr -d '\n' < /tmp/reqsign-aws-role-token)" \
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
--output text)"
read -r access_key secret_key session_token <<< "$credentials"
test -n "$access_key" && test -n "$secret_key" && test -n "$session_token"
echo "::add-mask::$access_key"
echo "::add-mask::$secret_key"
echo "::add-mask::$session_token"
{
echo "AWS_ACCESS_KEY_ID=$access_key"
echo "AWS_SECRET_ACCESS_KEY=$secret_key"
echo "AWS_SESSION_TOKEN=$session_token"
} >> "$GITHUB_ENV"
- name: Test S3AccessGrantsGranter
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_S3_ACCESS_GRANTS: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
REQSIGN_AWS_V4_ACCOUNT_ID: ${{ vars.REQSIGN_AWS_LIVE_ACCOUNT_ID }}
REQSIGN_AWS_V4_S3_ACCESS_GRANTS_BUCKET: ${{ vars.REQSIGN_AWS_LIVE_TEST_BUCKET }}
REQSIGN_AWS_V4_S3_ACCESS_GRANTS_PREFIX: access-grants/
run: cargo test test_s3_access_grants_granter_live --no-fail-fast -- --nocapture
- name: Remove OIDC token
if: always()
run: rm -f -- /tmp/reqsign-aws-role-token
# Signing tests against Amazon S3
signing_test:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
export-env: true
env:
REQSIGN_AWS_V4_ACCESS_KEY: op://reqsign/aws-v4/access_key
REQSIGN_AWS_V4_SECRET_KEY: op://reqsign/aws-v4/secret_key
REQSIGN_AWS_V4_REGION: op://reqsign/aws-v4/region
REQSIGN_AWS_V4_SERVICE: op://reqsign/aws-v4/service
REQSIGN_AWS_V4_URL: op://reqsign/aws-v4/url
- name: Test signing
working-directory: ./services/aws-v4
env:
# Gate flags must live on the test step. 1Password export-env only
# forwards op:// secrets, so literals set on Load secrets are dropped.
REQSIGN_AWS_V4_TEST: on
run: |
test "${REQSIGN_AWS_V4_TEST}" = "on"
echo "::group::Running signing tests"
cargo test signing:: --no-fail-fast -- --no-capture
echo "::endgroup::"
# EnvCredentialProvider test
test_env_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
export-env: true
env:
AWS_ACCESS_KEY_ID: op://reqsign/aws-v4/access_key
AWS_SECRET_ACCESS_KEY: op://reqsign/aws-v4/secret_key
AWS_REGION: op://reqsign/aws-v4/region
- name: Test EnvCredentialProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_ENV: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
run: |
test "${REQSIGN_AWS_V4_TEST_ENV}" = "on"
echo "::group::Testing EnvCredentialProvider"
cargo test test_env_credential_provider --no-fail-fast -- --no-capture
echo "::endgroup::"
# ProfileCredentialProvider test
test_profile_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
id: load_secret
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
env:
AWS_ACCESS_KEY_ID: op://reqsign/aws-v4/access_key
AWS_SECRET_ACCESS_KEY: op://reqsign/aws-v4/secret_key
- name: Setup test AWS profile
run: |
mkdir -p ~/.aws
cat > ~/.aws/credentials << EOF
[default]
aws_access_key_id = ${STEPS_LOAD_SECRETS_OUTPUTS_AWS_ACCESS_KEY_ID}
aws_secret_access_key = ${STEPS_LOAD_SECRETS_OUTPUTS_AWS_SECRET_ACCESS_KEY}
[test-profile]
aws_access_key_id = ${STEPS_LOAD_SECRETS_OUTPUTS_AWS_ACCESS_KEY_ID}
aws_secret_access_key = ${STEPS_LOAD_SECRETS_OUTPUTS_AWS_SECRET_ACCESS_KEY}
EOF
cat > ~/.aws/config << EOF
[default]
region = us-east-1
[profile test-profile]
region = us-west-1
output = json
EOF
env:
STEPS_LOAD_SECRETS_OUTPUTS_AWS_ACCESS_KEY_ID: ${{ steps.load_secret.outputs.AWS_ACCESS_KEY_ID }}
STEPS_LOAD_SECRETS_OUTPUTS_AWS_SECRET_ACCESS_KEY: ${{ steps.load_secret.outputs.AWS_SECRET_ACCESS_KEY }}
- name: Test ProfileCredentialProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_PROFILE: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
AWS_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
run: |
test "${REQSIGN_AWS_V4_TEST_PROFILE}" = "on"
echo "::group::Testing ProfileCredentialProvider"
cargo test test_profile_credential_provider --no-fail-fast -- --no-capture
echo "::endgroup::"
# AssumeRoleCredentialProvider test
test_assume_role_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
export-env: true
env:
REQSIGN_AWS_V4_ASSUME_ROLE_ARN: op://reqsign/aws-v4/assume_role_arn
AWS_ACCESS_KEY_ID: op://reqsign/aws-v4/access_key
AWS_SECRET_ACCESS_KEY: op://reqsign/aws-v4/secret_key
AWS_REGION: op://reqsign/aws-v4/region
- name: Test AssumeRoleCredentialProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_ASSUME_ROLE: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
run: |
test "${REQSIGN_AWS_V4_TEST_ASSUME_ROLE}" = "on"
echo "::group::Testing AssumeRoleCredentialProvider"
cargo test test_assume_role_credential_provider --no-fail-fast -- --no-capture
cargo test test_assume_role_granter --no-fail-fast -- --no-capture
echo "::endgroup::"
# AssumeRoleWithWebIdentityCredentialProvider test
test_web_identity_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Get GitHub OIDC token
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
id: idtoken
with:
script: |
let id_token = await core.getIDToken('sts.amazonaws.com')
core.setSecret(id_token)
// Write token to file
const fs = require('fs');
fs.writeFileSync('/tmp/web_identity_token', id_token);
console.log('GitHub OIDC token obtained and saved');
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
export-env: true
env:
AWS_ROLE_ARN: op://reqsign/aws-v4/web_identity_role_arn
AWS_REGION: op://reqsign/aws-v4/region
- name: Test AssumeRoleWithWebIdentityCredentialProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_WEB_IDENTITY: on
REQSIGN_AWS_V4_VALIDATE_CREDENTIALS: on
AWS_WEB_IDENTITY_TOKEN_FILE: /tmp/web_identity_token
run: |
test "${REQSIGN_AWS_V4_TEST_WEB_IDENTITY}" = "on"
echo "::group::Testing AssumeRoleWithWebIdentityCredentialProvider"
cargo test test_assume_role_with_web_identity_credential_provider --no-fail-fast -- --no-capture
echo "::endgroup::"
# S3ExpressSessionProvider test
test_s3_express_provider:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup 1Password Connect
uses: 1Password/load-secrets-action/configure@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
connect-host: ${{ secrets.OP_CONNECT_HOST }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
uses: 1Password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c # v4.1.1
with:
export-env: true
env:
REQSIGN_AWS_V4_S3_EXPRESS_BUCKET: op://reqsign/aws-v4-s3-express/bucket
AWS_ACCESS_KEY_ID: op://reqsign/aws-v4/access_key
AWS_SECRET_ACCESS_KEY: op://reqsign/aws-v4/secret_key
AWS_REGION: op://reqsign/aws-v4-s3-express/region
- name: Test S3ExpressSessionProvider
working-directory: ./services/aws-v4
env:
REQSIGN_AWS_V4_TEST_S3_EXPRESS: on
run: |
test "${REQSIGN_AWS_V4_TEST_S3_EXPRESS}" = "on"
echo "::group::Testing S3ExpressSessionProvider"
cargo test test_s3_express --no-fail-fast -- --no-capture
echo "::endgroup::"
build_live_image:
needs: check_secrets
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
env:
AWS_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
AWS_DEFAULT_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.image.outputs.image }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Get a GitHub OIDC token
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const token = await core.getIDToken('sts.amazonaws.com');
core.setSecret(token);
require('fs').writeFileSync('/tmp/reqsign-aws-role-token', token, { mode: 0o600 });
- name: Assume the reqsign live-test role
env:
ROLE_ARN: ${{ vars.REQSIGN_AWS_LIVE_ROLE_ARN }}
ROLE_SESSION_NAME: reqsign-image-${{ github.run_id }}
shell: bash
run: |
credentials="$(aws sts assume-role-with-web-identity \
--role-arn "$ROLE_ARN" \
--role-session-name "$ROLE_SESSION_NAME" \
--web-identity-token "$(tr -d '\n' < /tmp/reqsign-aws-role-token)" \
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
--output text)"
read -r access_key secret_key session_token <<< "$credentials"
test -n "$access_key" && test -n "$secret_key" && test -n "$session_token"
echo "::add-mask::$access_key"
echo "::add-mask::$secret_key"
echo "::add-mask::$session_token"
{
echo "AWS_ACCESS_KEY_ID=$access_key"
echo "AWS_SECRET_ACCESS_KEY=$secret_key"
echo "AWS_SESSION_TOKEN=$session_token"
} >> "$GITHUB_ENV"
- name: Build and push the live-test image
id: image
env:
ECR_REPOSITORY_URI: ${{ vars.REQSIGN_AWS_LIVE_ECR_REPOSITORY }}
IMAGE_TAG: sha-${{ github.sha }}
shell: bash
run: |
repository_name="${ECR_REPOSITORY_URI#*/}"
if ! aws ecr describe-images --repository-name "$repository_name" --image-ids "imageTag=$IMAGE_TAG" >/dev/null 2>&1; then
aws ecr get-login-password | docker login --username AWS --password-stdin "${ECR_REPOSITORY_URI%/*}"
docker build --platform linux/amd64 --file .github/aws-live-tests/Dockerfile --tag "$ECR_REPOSITORY_URI:$IMAGE_TAG" .
docker push "$ECR_REPOSITORY_URI:$IMAGE_TAG"
fi
echo "image=$ECR_REPOSITORY_URI:$IMAGE_TAG" >> "$GITHUB_OUTPUT"
test_ecs_provider:
needs:
- check_secrets
- build_live_image
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
env:
AWS_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
AWS_DEFAULT_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
permissions:
contents: read
id-token: write
steps:
- name: Get a GitHub OIDC token
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const token = await core.getIDToken('sts.amazonaws.com');
core.setSecret(token);
require('fs').writeFileSync('/tmp/reqsign-aws-role-token', token, { mode: 0o600 });
- name: Assume the reqsign live-test role
env:
ROLE_ARN: ${{ vars.REQSIGN_AWS_LIVE_ROLE_ARN }}
ROLE_SESSION_NAME: reqsign-ecs-${{ github.run_id }}
shell: bash
run: |
credentials="$(aws sts assume-role-with-web-identity \
--role-arn "$ROLE_ARN" \
--role-session-name "$ROLE_SESSION_NAME" \
--web-identity-token "$(tr -d '\n' < /tmp/reqsign-aws-role-token)" \
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
--output text)"
read -r access_key secret_key session_token <<< "$credentials"
test -n "$access_key" && test -n "$secret_key" && test -n "$session_token"
echo "::add-mask::$access_key"
echo "::add-mask::$secret_key"
echo "::add-mask::$session_token"
{
echo "AWS_ACCESS_KEY_ID=$access_key"
echo "AWS_SECRET_ACCESS_KEY=$secret_key"
echo "AWS_SESSION_TOKEN=$session_token"
} >> "$GITHUB_ENV"
- name: Register and run the Fargate task
env:
IMAGE_URI: ${{ needs.build_live_image.outputs.image }}
CLUSTER: ${{ vars.REQSIGN_AWS_LIVE_ECS_CLUSTER }}
EXECUTION_ROLE_ARN: ${{ vars.REQSIGN_AWS_LIVE_ECS_EXECUTION_ROLE_ARN }}
TASK_ROLE_ARN: ${{ vars.REQSIGN_AWS_LIVE_ECS_TASK_ROLE_ARN }}
LOG_GROUP: ${{ vars.REQSIGN_AWS_LIVE_ECS_LOG_GROUP }}
SUBNET_ID: ${{ vars.REQSIGN_AWS_LIVE_SUBNET_ID }}
SECURITY_GROUP_ID: ${{ vars.REQSIGN_AWS_LIVE_SECURITY_GROUP_ID }}
shell: bash
run: |
container_definitions="$(jq -n \
--arg image "$IMAGE_URI" \
--arg region "$AWS_REGION" \
--arg log_group "$LOG_GROUP" \
'[{
name: "reqsign-live-test",
image: $image,
essential: true,
command: ["test_ecs_credential_provider", "--nocapture"],
environment: [
{name: "AWS_REGION", value: $region},
{name: "REQSIGN_AWS_V4_TEST_ECS", value: "on"},
{name: "REQSIGN_AWS_V4_VALIDATE_CREDENTIALS", value: "on"},
{name: "RUST_LOG", value: "info"}
],
logConfiguration: {
logDriver: "awslogs",
options: {
"awslogs-group": $log_group,
"awslogs-region": $region,
"awslogs-stream-prefix": "ecs"
}
}
}]')"
task_definition_arn="$(aws ecs register-task-definition \
--family reqsign-live-test \
--requires-compatibilities FARGATE \
--network-mode awsvpc \
--runtime-platform cpuArchitecture=X86_64,operatingSystemFamily=LINUX \
--cpu 256 \
--memory 512 \
--execution-role-arn "$EXECUTION_ROLE_ARN" \
--task-role-arn "$TASK_ROLE_ARN" \
--container-definitions "$container_definitions" \
--query 'taskDefinition.taskDefinitionArn' \
--output text)"
echo "TASK_DEFINITION_ARN=$task_definition_arn" >> "$GITHUB_ENV"
task_arn="$(aws ecs run-task \
--cluster "$CLUSTER" \
--task-definition "$task_definition_arn" \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[$SUBNET_ID],securityGroups=[$SECURITY_GROUP_ID],assignPublicIp=ENABLED}" \
--started-by "reqsign-${GITHUB_RUN_ID}" \
--query 'tasks[0].taskArn' \
--output text)"
test "$task_arn" != "None"
echo "TASK_ARN=$task_arn" >> "$GITHUB_ENV"
aws ecs wait tasks-stopped --cluster "$CLUSTER" --tasks "$task_arn"
task_json="$(aws ecs describe-tasks --cluster "$CLUSTER" --tasks "$task_arn")"
printf '%s\n' "$task_json" | jq '{stoppedReason: .tasks[0].stoppedReason, containers: [.tasks[0].containers[] | {name, exitCode, reason}]}'
task_id="${task_arn##*/}"
log_stream="ecs/reqsign-live-test/$task_id"
for _ in {1..12}; do
if aws logs get-log-events --log-group-name "$LOG_GROUP" --log-stream-name "$log_stream" --start-from-head --query 'events[].message' --output text; then
break
fi
sleep 5
done
test "$(printf '%s\n' "$task_json" | jq -r '.tasks[0].containers[0].exitCode')" = "0"
- name: Clean up the Fargate task
if: always()
env:
CLUSTER: ${{ vars.REQSIGN_AWS_LIVE_ECS_CLUSTER }}
shell: bash
run: |
if [[ -n "${TASK_ARN:-}" ]]; then
last_status="$(aws ecs describe-tasks --cluster "$CLUSTER" --tasks "$TASK_ARN" --query 'tasks[0].lastStatus' --output text 2>/dev/null || true)"
if [[ "$last_status" == "RUNNING" || "$last_status" == "PENDING" || "$last_status" == "PROVISIONING" || "$last_status" == "ACTIVATING" ]]; then
aws ecs stop-task --cluster "$CLUSTER" --task "$TASK_ARN" --reason "GitHub Actions cleanup" >/dev/null
fi
fi
if [[ -n "${TASK_DEFINITION_ARN:-}" ]]; then
aws ecs deregister-task-definition --task-definition "$TASK_DEFINITION_ARN" >/dev/null
fi
test_imds_provider:
needs:
- check_secrets
- build_live_image
if: needs.check_secrets.outputs.has_secrets == 'true'
runs-on: ubuntu-latest
env:
AWS_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
AWS_DEFAULT_REGION: ${{ vars.REQSIGN_AWS_LIVE_REGION }}
permissions:
contents: read
id-token: write
steps:
- name: Get a GitHub OIDC token
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const token = await core.getIDToken('sts.amazonaws.com');
core.setSecret(token);
require('fs').writeFileSync('/tmp/reqsign-aws-role-token', token, { mode: 0o600 });
- name: Assume the reqsign live-test role
env:
ROLE_ARN: ${{ vars.REQSIGN_AWS_LIVE_ROLE_ARN }}
ROLE_SESSION_NAME: reqsign-imds-${{ github.run_id }}
shell: bash
run: |
credentials="$(aws sts assume-role-with-web-identity \
--role-arn "$ROLE_ARN" \
--role-session-name "$ROLE_SESSION_NAME" \
--web-identity-token "$(tr -d '\n' < /tmp/reqsign-aws-role-token)" \
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
--output text)"
read -r access_key secret_key session_token <<< "$credentials"
test -n "$access_key" && test -n "$secret_key" && test -n "$session_token"
echo "::add-mask::$access_key"
echo "::add-mask::$secret_key"
echo "::add-mask::$session_token"
{
echo "AWS_ACCESS_KEY_ID=$access_key"
echo "AWS_SECRET_ACCESS_KEY=$secret_key"
echo "AWS_SESSION_TOKEN=$session_token"
} >> "$GITHUB_ENV"
- name: Launch the ephemeral IMDSv2 test instance
env:
IMAGE_URI: ${{ needs.build_live_image.outputs.image }}
INSTANCE_PROFILE: ${{ vars.REQSIGN_AWS_LIVE_EC2_INSTANCE_PROFILE }}
SUBNET_ID: ${{ vars.REQSIGN_AWS_LIVE_SUBNET_ID }}
SECURITY_GROUP_ID: ${{ vars.REQSIGN_AWS_LIVE_SECURITY_GROUP_ID }}
RESULT_BUCKET: ${{ vars.REQSIGN_AWS_LIVE_TEST_BUCKET }}
shell: bash
run: |
result_key="live-tests/results/${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}/imds.log"
echo "IMDS_RESULT_KEY=$result_key" >> "$GITHUB_ENV"
cat > "$RUNNER_TEMP/reqsign-imds-user-data.sh" <<EOF
#!/bin/bash
log_file=/var/log/reqsign-live-test.log
(
set -euo pipefail
dnf install --assumeyes docker
systemctl enable --now docker
aws ecr get-login-password --region '${AWS_REGION}' | docker login --username AWS --password-stdin '${IMAGE_URI%/*}'
docker run --rm --network host \
--env AWS_REGION='${AWS_REGION}' \
--env REQSIGN_AWS_V4_TEST_IMDS=on \
--env REQSIGN_AWS_V4_VALIDATE_CREDENTIALS=on \
--env RUST_LOG=info \
'${IMAGE_URI}' test_imds_v2_credential_provider --nocapture
) > "\$log_file" 2>&1
test_status=\$?
printf '\nREQSIGN_TEST_EXIT_CODE=%s\n' "\$test_status" >> "\$log_file"
aws s3 cp "\$log_file" 's3://${RESULT_BUCKET}/${result_key}' --region '${AWS_REGION}'
shutdown -h now
EOF
chmod 700 "$RUNNER_TEMP/reqsign-imds-user-data.sh"
ami_id="$(aws ssm get-parameter \
--name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query 'Parameter.Value' \
--output text)"
expires_at="$(date -u -d '+2 hours' '+%Y-%m-%dT%H:%M:%SZ')"
instance_id="$(aws ec2 run-instances \
--image-id "$ami_id" \
--instance-type t3.nano \
--count 1 \
--iam-instance-profile "Name=$INSTANCE_PROFILE" \
--subnet-id "$SUBNET_ID" \
--security-group-ids "$SECURITY_GROUP_ID" \
--associate-public-ip-address \
--metadata-options 'HttpEndpoint=enabled,HttpTokens=required,HttpPutResponseHopLimit=2,InstanceMetadataTags=disabled' \
--instance-initiated-shutdown-behavior terminate \
--user-data "file://$RUNNER_TEMP/reqsign-imds-user-data.sh" \
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=reqsign-live-test-imds},{Key=Project,Value=reqsign},{Key=Purpose,Value=live-test},{Key=GitHubRunId,Value=${GITHUB_RUN_ID}},{Key=ExpiresAt,Value=$expires_at}]" \
--query 'Instances[0].InstanceId' \
--output text)"
echo "INSTANCE_ID=$instance_id" >> "$GITHUB_ENV"
result_found=false
for _ in {1..120}; do
if aws s3api head-object --bucket "$RESULT_BUCKET" --key "$result_key" >/dev/null 2>&1; then
result_found=true
break
fi
sleep 10
done
test "$result_found" = "true"
aws s3 cp "s3://$RESULT_BUCKET/$result_key" "$RUNNER_TEMP/reqsign-imds-result.log"
sed -E 's/(AccessKeyId|SecretAccessKey|Token).*/\1: [REDACTED]/I' "$RUNNER_TEMP/reqsign-imds-result.log"
grep -qx 'REQSIGN_TEST_EXIT_CODE=0' "$RUNNER_TEMP/reqsign-imds-result.log"
- name: Clean up the IMDSv2 test instance
if: always()
env:
RESULT_BUCKET: ${{ vars.REQSIGN_AWS_LIVE_TEST_BUCKET }}
shell: bash
run: |
if [[ -n "${INSTANCE_ID:-}" ]]; then
aws ec2 terminate-instances --instance-ids "$INSTANCE_ID" >/dev/null 2>&1 || true
fi
if [[ -n "${IMDS_RESULT_KEY:-}" ]]; then
aws s3api delete-object --bucket "$RESULT_BUCKET" --key "$IMDS_RESULT_KEY" >/dev/null 2>&1 || true
fi
# Summary report
summary:
if: ${{ always() && !cancelled() }}
needs:
- unit_test
- check_secrets
- test_static_provider
- test_env_provider
- test_default_provider
- test_profile_provider
- test_process_provider
- test_assume_role_provider
- test_web_identity_provider
- test_cognito_provider
- test_s3_access_grants_granter
- test_s3_express_provider
- test_ecs_provider
- test_imds_provider
- signing_test
- build_live_image
runs-on: ubuntu-latest
steps:
- name: Test Summary
env:
UNIT_TEST_RESULT: ${{ needs.unit_test.result }}
LIVE_TESTS_ENABLED: ${{ needs.check_secrets.outputs.has_secrets }}
STATIC_RESULT: ${{ needs.test_static_provider.result }}
ENV_RESULT: ${{ needs.test_env_provider.result }}
DEFAULT_RESULT: ${{ needs.test_default_provider.result }}
PROFILE_RESULT: ${{ needs.test_profile_provider.result }}
PROCESS_RESULT: ${{ needs.test_process_provider.result }}
ASSUME_ROLE_RESULT: ${{ needs.test_assume_role_provider.result }}
WEB_IDENTITY_RESULT: ${{ needs.test_web_identity_provider.result }}
COGNITO_RESULT: ${{ needs.test_cognito_provider.result }}
S3_ACCESS_GRANTS_RESULT: ${{ needs.test_s3_access_grants_granter.result }}
S3_EXPRESS_RESULT: ${{ needs.test_s3_express_provider.result }}
ECS_RESULT: ${{ needs.test_ecs_provider.result }}
IMDS_RESULT: ${{ needs.test_imds_provider.result }}
SIGNING_RESULT: ${{ needs.signing_test.result }}
IMAGE_RESULT: ${{ needs.build_live_image.result }}
shell: bash
run: |
{
echo "## AWS V4 Test Summary"
echo
echo "| Coverage | Real AWS acceptance | Result |"
echo "|----------|---------------------|--------|"
echo "| Unit tests | Local | $UNIT_TEST_RESULT |"
if [[ "$LIVE_TESTS_ENABLED" == "true" ]]; then
echo "| StaticCredentialProvider | STS GetCallerIdentity | $STATIC_RESULT |"
echo "| EnvCredentialProvider | STS GetCallerIdentity | $ENV_RESULT |"
echo "| DefaultCredentialProvider | STS GetCallerIdentity | $DEFAULT_RESULT |"
echo "| ProfileCredentialProvider | STS GetCallerIdentity | $PROFILE_RESULT |"
echo "| ProcessCredentialProvider | credential_process and STS GetCallerIdentity | $PROCESS_RESULT |"
echo "| AssumeRoleCredentialProvider and granter | STS AssumeRole and GetCallerIdentity | $ASSUME_ROLE_RESULT |"
echo "| AssumeRoleWithWebIdentityCredentialProvider | GitHub OIDC, STS, and GetCallerIdentity | $WEB_IDENTITY_RESULT |"
echo "| CognitoIdentityCredentialProvider | Cognito Identity and STS GetCallerIdentity | $COGNITO_RESULT |"
echo "| S3AccessGrantsGranter | S3 Access Grants GetDataAccess and STS GetCallerIdentity | $S3_ACCESS_GRANTS_RESULT |"
echo "| S3ExpressSessionProvider and granter | S3 Express CreateSession | $S3_EXPRESS_RESULT |"
echo "| ECSCredentialProvider | Fargate task credentials and STS GetCallerIdentity | $ECS_RESULT |"
echo "| IMDSv2CredentialProvider | EC2 IMDSv2 credentials and STS GetCallerIdentity | $IMDS_RESULT |"
echo "| AWS SigV4 signing | Amazon S3 | $SIGNING_RESULT |"
echo "| ECS and IMDS test image | Local Docker build and Amazon ECR | $IMAGE_RESULT |"
echo
echo "SSOCredentialProvider has no real-service CI job; unattended SSO authorization remains tracked in #825."
else
echo "| Live service integration tests | Skipped for an untrusted change | skipped |"
fi
} >> "$GITHUB_STEP_SUMMARY"
test "$UNIT_TEST_RESULT" = "success"
if [[ "$LIVE_TESTS_ENABLED" == "true" ]]; then
results=(
"$STATIC_RESULT"
"$ENV_RESULT"
"$DEFAULT_RESULT"
"$PROFILE_RESULT"
"$PROCESS_RESULT"
"$ASSUME_ROLE_RESULT"
"$WEB_IDENTITY_RESULT"
"$COGNITO_RESULT"
"$S3_ACCESS_GRANTS_RESULT"
"$S3_EXPRESS_RESULT"
"$ECS_RESULT"
"$IMDS_RESULT"
"$SIGNING_RESULT"
"$IMAGE_RESULT"
)
for result in "${results[@]}"; do
test "$result" = "success"
done
fi