mirror of
https://github.com/aws-actions/configure-aws-credentials.git
synced 2026-08-24 04:25:05 +09:00
Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6835cc9216 | |||
| bb38f46f0f | |||
| e55fab9d37 | |||
| 740d8a44f8 | |||
| baf85d8be9 | |||
| b3a87c1114 | |||
| 9ba95ecaf9 | |||
| 2e0b693663 | |||
| 2f596bbeba | |||
| 9a78866a56 | |||
| 58784d3648 | |||
| e22426a453 | |||
| fe3da2c1fd | |||
| 67b6c7c1a9 | |||
| 7ab2a28ca9 | |||
| 77a21a780c | |||
| ae509cd90a | |||
| f04843b510 | |||
| 5b6b815585 | |||
| 1a820d1dd7 | |||
| cdd78886ae | |||
| 053a0e8b78 | |||
| 863af956d9 | |||
| f4f3929463 | |||
| 96febc6582 | |||
| da082bd937 | |||
| 554e695b56 | |||
| 06ce89fcf6 | |||
| 684c75b685 | |||
| 442c1ba794 | |||
| c787feb964 | |||
| 58b282f00f | |||
| 472e549195 | |||
| 187737abc2 | |||
| 8d8bc098cc | |||
| fa078d278f | |||
| 9f7f57b093 | |||
| 09758fb6fe | |||
| 38878c03af | |||
| ab6dfc4459 | |||
| 9d7a893ad4 | |||
| 48c7de6aec | |||
| 2371778031 | |||
| aec054896e | |||
| 9c93b3b896 | |||
| 0c2c1f7c12 | |||
| 2d01f93692 | |||
| 7d7a0bc951 | |||
| 92f9ac93ef | |||
| d91230b55a | |||
| 00fabbe944 | |||
| 0b4d37d15b | |||
| 6222ef53ed | |||
| 4dffe9935f | |||
| 7a20e4d87a | |||
| d0f92ec22a | |||
| c4c5364d3e | |||
| 5c99022f7d | |||
| 573b434a39 | |||
| 4162c23c46 | |||
| 1116bc7043 | |||
| 32a3a7cc43 | |||
| 35a0f3fe6c | |||
| 0a0b8e52b4 | |||
| b77dc22838 | |||
| 40fbf99f9b | |||
| bc03572061 | |||
| c831837ecb | |||
| 9a3b1bcbca | |||
| e798aff9bb | |||
| f99a28d036 | |||
| 96ec5c4c14 | |||
| 8ee391aeef | |||
| a679b609cc | |||
| c9874b9244 | |||
| 3e2ba00093 | |||
| fc72bd38db | |||
| a633ed5e02 | |||
| c8c5fb1fc0 | |||
| cf6b87e99e | |||
| 5ce6d03987 | |||
| 3d568d2c43 | |||
| 75aa8f1e41 | |||
| dee935fabb | |||
| a20ed60252 | |||
| ee662900a5 | |||
| db6655e0af | |||
| 82c08e4055 | |||
| 816f5cc0cf | |||
| a5771a059f | |||
| dc0dd17052 | |||
| 9f5d66e392 | |||
| fb645e2d6b | |||
| 64d137fefc | |||
| af8cbf10db | |||
| da090bd22d | |||
| 3c4f95f73f | |||
| 96c6f7e07b | |||
| 47312577c9 | |||
| 1c435bbd5e | |||
| 4d0082acf8 | |||
| 058322d68a |
@@ -1,5 +0,0 @@
|
||||
version: 1
|
||||
update_configs:
|
||||
- package_manager: "javascript"
|
||||
directory: "/"
|
||||
update_schedule: "monthly"
|
||||
@@ -0,0 +1,8 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: tuesday
|
||||
open-pull-requests-limit: 10
|
||||
@@ -0,0 +1,37 @@
|
||||
pull_request_rules:
|
||||
- name: Automatically merge on CI success and review approval
|
||||
conditions:
|
||||
- base~=master|integ-tests
|
||||
- "#approved-reviews-by>=1"
|
||||
- approved-reviews-by=@aws-actions/aws-ecs-devx
|
||||
- -approved-reviews-by~=author
|
||||
- status-success=Run Unit Tests
|
||||
- status-success=Semantic Pull Request
|
||||
- label!=work-in-progress
|
||||
- -title~=(WIP|wip)
|
||||
- -merged
|
||||
- -closed
|
||||
- author!=dependabot[bot]
|
||||
actions:
|
||||
merge:
|
||||
method: squash
|
||||
strict: smart
|
||||
strict_method: merge
|
||||
|
||||
- name: Automatically approve and merge Dependabot PRs
|
||||
conditions:
|
||||
- base=master
|
||||
- author=dependabot[bot]
|
||||
- status-success=Run Unit Tests
|
||||
- status-success=Semantic Pull Request
|
||||
- -title~=(WIP|wip)
|
||||
- -label~=(blocked|do-not-merge)
|
||||
- -merged
|
||||
- -closed
|
||||
actions:
|
||||
review:
|
||||
type: APPROVE
|
||||
merge:
|
||||
method: squash
|
||||
strict: smart+fasttrack
|
||||
strict_method: merge
|
||||
@@ -2,6 +2,75 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [1.4.3](https://github.com/aws-actions/configure-aws-credentials/compare/v1.4.2...v1.4.3) (2020-07-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Make tagging optional ([#92](https://github.com/aws-actions/configure-aws-credentials/issues/92)) ([baf85d8](https://github.com/aws-actions/configure-aws-credentials/commit/baf85d8be969f190df9bc9153f06958c32ef3828))
|
||||
|
||||
### [1.4.2](https://github.com/aws-actions/configure-aws-credentials/compare/v1.4.1...v1.4.2) (2020-06-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add comma to set of special characters ([#78](https://github.com/aws-actions/configure-aws-credentials/issues/78)) ([f04843b](https://github.com/aws-actions/configure-aws-credentials/commit/f04843b510a6c8adf77eed907a616cf00a99970d))
|
||||
|
||||
### [1.4.1](https://github.com/aws-actions/configure-aws-credentials/compare/v1.4.0...v1.4.1) (2020-06-09)
|
||||
|
||||
## [1.4.0](https://github.com/aws-actions/configure-aws-credentials/compare/v1.3.5...v1.4.0) (2020-06-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Refresh and validate credentials after setting env var creds ([#71](https://github.com/aws-actions/configure-aws-credentials/issues/71)) ([472e549](https://github.com/aws-actions/configure-aws-credentials/commit/472e549195ba1f153e9fb72e39dc2a094e5de13e))
|
||||
|
||||
### [1.3.5](https://github.com/aws-actions/configure-aws-credentials/compare/v1.3.4...v1.3.5) (2020-05-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* clear session token env var if present for non-session credentials ([#65](https://github.com/aws-actions/configure-aws-credentials/issues/65)) ([0c2c1f7](https://github.com/aws-actions/configure-aws-credentials/commit/0c2c1f7c129971b6f433551b1f4ba4a6a9cc8b70))
|
||||
|
||||
### [1.3.4](https://github.com/aws-actions/configure-aws-credentials/compare/v1.3.3...v1.3.4) (2020-05-18)
|
||||
|
||||
### [1.3.3](https://github.com/aws-actions/configure-aws-credentials/compare/v1.3.2...v1.3.3) (2020-04-02)
|
||||
|
||||
### [1.3.2](https://github.com/aws-actions/configure-aws-credentials/compare/v1.3.1...v1.3.2) (2020-03-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* let the AWS SDK determine the STS regional endpoint ([#48](https://github.com/aws-actions/configure-aws-credentials/issues/48)) ([fc72bd3](https://github.com/aws-actions/configure-aws-credentials/commit/fc72bd38dbe25493f5113760c9c6e1ef2f6f9a0e))
|
||||
|
||||
### [1.3.1](https://github.com/aws-actions/configure-aws-credentials/compare/v1.3.0...v1.3.1) (2020-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* validate region input string ([#44](https://github.com/aws-actions/configure-aws-credentials/issues/44)) ([3d568d2](https://github.com/aws-actions/configure-aws-credentials/commit/3d568d2c4359304d46d9bd1b4d9f69e088ccbf7b))
|
||||
|
||||
## [1.3.0](https://github.com/aws-actions/configure-aws-credentials/compare/v1.2.0...v1.3.0) (2020-03-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* don't require access key credentials for self-hosted runners ([#42](https://github.com/aws-actions/configure-aws-credentials/issues/42)) ([a20ed60](https://github.com/aws-actions/configure-aws-credentials/commit/a20ed6025224ca999786c8d4e687f119cfedec65))
|
||||
|
||||
## [1.2.0](https://github.com/aws-actions/configure-aws-credentials/compare/v1.1.2...v1.2.0) (2020-03-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add option to provide external ID ([#32](https://github.com/aws-actions/configure-aws-credentials/issues/32)) ([1c435bb](https://github.com/aws-actions/configure-aws-credentials/commit/1c435bbd5e1f1d36cdd703da5c4d6ee1ad91efac)), closes [#28](https://github.com/aws-actions/configure-aws-credentials/issues/28)
|
||||
* Have an ability to configure session name ([#29](https://github.com/aws-actions/configure-aws-credentials/issues/29)) ([4d0082a](https://github.com/aws-actions/configure-aws-credentials/commit/4d0082acf8b4102597f2570a056a320194f13e63))
|
||||
* infer role ARN if given role name ([#35](https://github.com/aws-actions/configure-aws-credentials/issues/35)) ([96c6f7e](https://github.com/aws-actions/configure-aws-credentials/commit/96c6f7e07b5fabc5a907fce84745ea625eeb005d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* mask both source and role credentials ([#40](https://github.com/aws-actions/configure-aws-credentials/issues/40)) ([816f5cc](https://github.com/aws-actions/configure-aws-credentials/commit/816f5cc0cf79541b2a6d639e8f93ae43aadaf09c))
|
||||
|
||||
### [1.1.2](https://github.com/aws-actions/configure-aws-credentials/compare/v1.1.1...v1.1.2) (2020-02-12)
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
|
||||
Configure AWS credential and region environment variables for use in other GitHub Actions. The environment variables will be detected by both the AWS SDKs and the AWS CLI to determine the credentials and region to use for AWS API calls.
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
- [Usage](#usage)
|
||||
- [Credentials](#credentials)
|
||||
- [Assuming a Role](#assuming-a-role)
|
||||
+ [Permissions for assuming a role](#permissions-for-assuming-a-role)
|
||||
+ [Session tagging](#session-tagging)
|
||||
- [Self-Hosted Runners](#self-hosted-runners)
|
||||
- [License Summary](#license-summary)
|
||||
- [Security Disclosures](#security-disclosures)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Usage
|
||||
|
||||
Add the following step to your workflow:
|
||||
@@ -15,6 +30,42 @@ Add the following step to your workflow:
|
||||
aws-region: us-east-2
|
||||
```
|
||||
|
||||
For example, you can use this action with the AWS CLI available in [GitHub's hosted virtual environments](https://help.github.com/en/actions/reference/software-installed-on-github-hosted-runners).
|
||||
You can also run this action multiple times to use different AWS accounts, regions, or IAM roles in the same GitHub Actions workflow job.
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
name: Upload to Amazon S3
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Configure AWS credentials from Test account
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.TEST_AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.TEST_AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-1
|
||||
|
||||
- name: Copy files to the test website with the AWS CLI
|
||||
run: |
|
||||
aws s3 sync . s3://my-s3-test-website-bucket
|
||||
|
||||
- name: Configure AWS credentials from Production account
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-west-2
|
||||
|
||||
- name: Copy files to the production website with the AWS CLI
|
||||
run: |
|
||||
aws s3 sync . s3://my-s3-prod-website-bucket
|
||||
```
|
||||
|
||||
See [action.yml](action.yml) for the full documentation for this action's inputs and outputs.
|
||||
|
||||
## Credentials
|
||||
@@ -26,10 +77,11 @@ We recommend following [Amazon IAM best practices](https://docs.aws.amazon.com/I
|
||||
* [Rotate the credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#rotate-credentials) used in GitHub Actions workflows regularly.
|
||||
* [Monitor the activity](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#keep-a-log) of the credentials used in GitHub Actions workflows.
|
||||
|
||||
## Assuming a role
|
||||
If you would like to use the credentials you provide to this action to assume a role, you can do so by specifying the role ARN in `role-to-assume`.
|
||||
The role credentials will then be output instead of the ones you have provided.
|
||||
## Assuming a Role
|
||||
If you would like to use the static credentials you provide to this action to assume a role, you can do so by specifying the role ARN in `role-to-assume`.
|
||||
The role credentials will then be configured in the Actions environment instead of the static credentials you have provided.
|
||||
The default session duration is 6 hours, but if you would like to adjust this you can pass a duration to `role-duration-seconds`.
|
||||
The default session name is GitHubActions, and you can modify it by specifying the desired name in `role-session-name`.
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
@@ -39,12 +91,58 @@ Example:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-2
|
||||
role-to-assume: arn:aws:iam::123456789100:role/role-to-assume
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
|
||||
role-external-id: ${{ secrets.AWS_ROLE_EXTERNAL_ID }}
|
||||
role-duration-seconds: 1200
|
||||
role-session-name: MySessionName
|
||||
```
|
||||
In this example, the secret `AWS_ROLE_TO_ASSUME` contains a string like `arn:aws:iam::123456789100:role/my-github-actions-role`. To assume a role in the same account as the static credentials, you can simply specify the role name, like `role-to-assume: my-github-actions-role`.
|
||||
|
||||
### Permissions for assuming a role
|
||||
|
||||
In order to assume a role, the IAM user for the static credentials must have the following permissions:
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"sts:AssumeRole",
|
||||
"sts:TagSession"
|
||||
],
|
||||
"Resource": "arn:aws:iam::123456789012:role/my-github-actions-role",
|
||||
"Effect": "Allow"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The role's trust policy must allow the IAM user to assume the role:
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowIamUserAssumeRole",
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {"AWS": "arn:aws:iam::123456789012:user/my-github-actions-user"},
|
||||
"Condition": {
|
||||
"StringEquals": {"sts:ExternalId": "Example987"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sid": "AllowPassSessionTags",
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:TagSession",
|
||||
"Principal": {"AWS": "arn:aws:iam::123456789012:user/my-github-actions-user"}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Session tagging
|
||||
The session will have the name "GitHubActions" and be tagged with the following tags:
|
||||
The session will have the name "GitHubActions" and be tagged with the following tags:
|
||||
(`GITHUB_` environment variable definitions can be [found here](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-environment-variables#default-environment-variables))
|
||||
|
||||
| Key | Value|
|
||||
@@ -59,6 +157,41 @@ The session will have the name "GitHubActions" and be tagged with the following
|
||||
|
||||
_Note: all tag values must conform to [the requirements](https://docs.aws.amazon.com/STS/latest/APIReference/API_Tag.html). Particularly, `GITHUB_WORKFLOW` will be truncated if it's too long. If `GITHUB_ACTOR` or `GITHUB_WORKFLOW` contain invalid charcters, the characters will be replaced with an '*'._
|
||||
|
||||
The action will use session tagging by default during role assumption. You can skip this session tagging by providing `role-skip-session-tagging` as true in the action's inputs:
|
||||
|
||||
```yaml
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
role-skip-session-tagging: true
|
||||
```
|
||||
|
||||
## Self-Hosted Runners
|
||||
|
||||
If you run your GitHub Actions in a [self-hosted runner](https://help.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners) that already has access to AWS credentials, such as an EC2 instance, then you do not need to provide IAM user access key credentials to this action.
|
||||
|
||||
If no access key credentials are given in the action inputs, this action will use credentials from the runner environment using the [default methods for the AWS SDK for Javascript](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html).
|
||||
|
||||
You can use this action to simply configure the region and account ID in the environment, and then use the runner's credentials for all AWS API calls made by your Actions workflow:
|
||||
```yaml
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-region: us-east-2
|
||||
```
|
||||
In this case, your runner's credentials must have permissions to call any AWS APIs called by your Actions workflow.
|
||||
|
||||
Or, you can use this action to assume a role, and then use the role credentials for all AWS API calls made by your Actions workflow:
|
||||
```yaml
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-region: us-east-2
|
||||
role-to-assume: my-github-actions-role
|
||||
```
|
||||
In this case, your runner's credentials must have permissions to assume the role.
|
||||
|
||||
## License Summary
|
||||
|
||||
This code is made available under the MIT license.
|
||||
|
||||
## Security Disclosures
|
||||
|
||||
If you would like to report a potential security issue in this project, please do not create a GitHub issue. Instead, please follow the instructions [here](https://aws.amazon.com/security/vulnerability-reporting/) or [email AWS security directly](mailto:aws-security@amazon.com).
|
||||
|
||||
+27
-6
@@ -5,11 +5,17 @@ branding:
|
||||
color: 'orange'
|
||||
inputs:
|
||||
aws-access-key-id:
|
||||
description: 'AWS Access Key ID'
|
||||
required: true
|
||||
description: >-
|
||||
AWS Access Key ID. This input is required if running in the GitHub hosted environment.
|
||||
It is optional if running in a self-hosted environment that already has AWS credentials,
|
||||
for example on an EC2 instance.
|
||||
required: false
|
||||
aws-secret-access-key:
|
||||
description: 'AWS Secret Access Key'
|
||||
required: true
|
||||
description: >-
|
||||
AWS Secret Access Key. This input is required if running in the GitHub hosted environment.
|
||||
It is optional if running in a self-hosted environment that already has AWS credentials,
|
||||
for example on an EC2 instance.
|
||||
required: false
|
||||
aws-session-token:
|
||||
description: 'AWS Session Token'
|
||||
required: false
|
||||
@@ -17,14 +23,29 @@ inputs:
|
||||
description: 'AWS Region, e.g. us-east-2'
|
||||
required: true
|
||||
mask-aws-account-id:
|
||||
description: "Whether to set the AWS account ID for these credentials as a secret value, so that it is masked in logs. Valid values are 'true' and 'false'. Defaults to true"
|
||||
description: >-
|
||||
Whether to set the AWS account ID for these credentials as a secret value,
|
||||
so that it is masked in logs. Valid values are 'true' and 'false'.
|
||||
Defaults to true
|
||||
required: false
|
||||
role-to-assume:
|
||||
description: "Use the provided credentials to assume a Role and output the assumed credentials for that Role rather than the provided credentials"
|
||||
description: >-
|
||||
Use the provided credentials to assume an IAM role and configure the Actions
|
||||
environment with the assumed role credentials rather than with the provided
|
||||
credentials
|
||||
required: false
|
||||
role-duration-seconds:
|
||||
description: "Role duration in seconds (default: 6 hours)"
|
||||
required: false
|
||||
role-session-name:
|
||||
description: 'Role session name (default: GitHubActions)'
|
||||
required: false
|
||||
role-external-id:
|
||||
description: 'The external ID of the role to assume'
|
||||
required: false
|
||||
role-skip-session-tagging:
|
||||
description: 'Skip session tagging during role assumption'
|
||||
required: false
|
||||
outputs:
|
||||
aws-account-id:
|
||||
description: 'The AWS account ID for the provided credentials'
|
||||
|
||||
Vendored
+701
-277
File diff suppressed because one or more lines are too long
@@ -1,22 +1,31 @@
|
||||
const core = require('@actions/core');
|
||||
const aws = require('aws-sdk');
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
|
||||
// The max time that a GitHub action is allowed to run is 6 hours.
|
||||
// That seems like a reasonable default to use if no role duration is defined.
|
||||
const MAX_ACTION_RUNTIME = 6 * 3600;
|
||||
const USER_AGENT = 'configure-aws-credentials-for-github-actions';
|
||||
const MAX_TAG_VALUE_LENGTH = 256;
|
||||
const SANITIZATION_CHARACTER = '_'
|
||||
const SANITIZATION_CHARACTER = '_';
|
||||
const ROLE_SESSION_NAME = 'GitHubActions';
|
||||
const REGION_REGEX = /^[a-z0-9-]+$/g;
|
||||
|
||||
async function assumeRole(params) {
|
||||
// Assume a role to get short-lived credentials using longer-lived credentials.
|
||||
const isDefined = i => !!i;
|
||||
|
||||
const {roleToAssume, roleDurationSeconds, accessKeyId, secretAccessKey, sessionToken, region} = params;
|
||||
const {
|
||||
sourceAccountId,
|
||||
roleToAssume,
|
||||
roleExternalId,
|
||||
roleDurationSeconds,
|
||||
roleSessionName,
|
||||
region,
|
||||
roleSkipSessionTagging
|
||||
} = params;
|
||||
assert(
|
||||
[roleToAssume, roleDurationSeconds, accessKeyId, secretAccessKey, region].every(isDefined),
|
||||
[sourceAccountId, roleToAssume, roleDurationSeconds, roleSessionName, region].every(isDefined),
|
||||
"Missing required input when assuming a Role."
|
||||
);
|
||||
|
||||
@@ -26,25 +35,37 @@ async function assumeRole(params) {
|
||||
'Missing required environment value. Are you running in GitHub Actions?'
|
||||
);
|
||||
|
||||
const endpoint = util.format('https://sts.%s.amazonaws.com', region);
|
||||
const sts = getStsClient(region);
|
||||
|
||||
const sts = new aws.STS({
|
||||
accessKeyId, secretAccessKey, sessionToken, region, endpoint, customUserAgent: USER_AGENT
|
||||
});
|
||||
return sts.assumeRole({
|
||||
RoleArn: roleToAssume,
|
||||
RoleSessionName: 'GitHubActions',
|
||||
let roleArn = roleToAssume;
|
||||
if (!roleArn.startsWith('arn:aws')) {
|
||||
// Supports only 'aws' partition. Customers in other partitions ('aws-cn') will need to provide full ARN
|
||||
roleArn = `arn:aws:iam::${sourceAccountId}:role/${roleArn}`;
|
||||
}
|
||||
const tagArray = [
|
||||
{Key: 'GitHub', Value: 'Actions'},
|
||||
{Key: 'Repository', Value: GITHUB_REPOSITORY},
|
||||
{Key: 'Workflow', Value: sanitizeGithubWorkflowName(GITHUB_WORKFLOW)},
|
||||
{Key: 'Action', Value: GITHUB_ACTION},
|
||||
{Key: 'Actor', Value: sanitizeGithubActor(GITHUB_ACTOR)},
|
||||
{Key: 'Branch', Value: GITHUB_REF},
|
||||
{Key: 'Commit', Value: GITHUB_SHA},
|
||||
];
|
||||
|
||||
const roleSessionTags = roleSkipSessionTagging ? undefined : tagArray;
|
||||
|
||||
const assumeRoleRequest = {
|
||||
RoleArn: roleArn,
|
||||
RoleSessionName: roleSessionName,
|
||||
DurationSeconds: roleDurationSeconds,
|
||||
Tags: [
|
||||
{Key: 'GitHub', Value: 'Actions'},
|
||||
{Key: 'Repository', Value: GITHUB_REPOSITORY},
|
||||
{Key: 'Workflow', Value: sanitizeGithubWorkflowName(GITHUB_WORKFLOW)},
|
||||
{Key: 'Action', Value: GITHUB_ACTION},
|
||||
{Key: 'Actor', Value: sanitizeGithubActor(GITHUB_ACTOR)},
|
||||
{Key: 'Branch', Value: GITHUB_REF},
|
||||
{Key: 'Commit', Value: GITHUB_SHA},
|
||||
]
|
||||
})
|
||||
Tags: roleSessionTags
|
||||
};
|
||||
|
||||
if (roleExternalId) {
|
||||
assumeRoleRequest.ExternalId = roleExternalId;
|
||||
}
|
||||
|
||||
return sts.assumeRole(assumeRoleRequest)
|
||||
.promise()
|
||||
.then(function (data) {
|
||||
return {
|
||||
@@ -65,7 +86,7 @@ function sanitizeGithubWorkflowName(name) {
|
||||
// Workflow names can be almost any valid UTF-8 string, but tags are more restrictive.
|
||||
// This replaces anything not conforming to the tag restrictions by inverting the regular expression.
|
||||
// See the AWS documentation for constraint specifics https://docs.aws.amazon.com/STS/latest/APIReference/API_Tag.html.
|
||||
const nameWithoutSpecialCharacters = name.replace(/[^\p{L}\p{Z}\p{N}_.:/=+-@]/gu, SANITIZATION_CHARACTER);
|
||||
const nameWithoutSpecialCharacters = name.replace(/[^\p{L}\p{Z}\p{N}_:/=+.-@-]/gu, SANITIZATION_CHARACTER);
|
||||
const nameTruncated = nameWithoutSpecialCharacters.slice(0, MAX_TAG_VALUE_LENGTH)
|
||||
return nameTruncated
|
||||
}
|
||||
@@ -90,6 +111,9 @@ function exportCredentials(params){
|
||||
if (sessionToken) {
|
||||
core.exportVariable('AWS_SESSION_TOKEN', sessionToken);
|
||||
core.setSecret(sessionToken);
|
||||
} else if (process.env.AWS_SESSION_TOKEN) {
|
||||
// clear session token from previous credentials action
|
||||
core.exportVariable('AWS_SESSION_TOKEN', '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,41 +124,127 @@ function exportRegion(region) {
|
||||
core.exportVariable('AWS_REGION', region);
|
||||
}
|
||||
|
||||
async function exportAccountId(maskAccountId) {
|
||||
async function exportAccountId(maskAccountId, region) {
|
||||
// Get the AWS account ID
|
||||
const sts = new aws.STS({customUserAgent: USER_AGENT});
|
||||
const sts = getStsClient(region);
|
||||
const identity = await sts.getCallerIdentity().promise();
|
||||
const accountId = identity.Account;
|
||||
core.setOutput('aws-account-id', accountId);
|
||||
if (!maskAccountId || maskAccountId.toLowerCase() == 'true') {
|
||||
core.setSecret(accountId);
|
||||
}
|
||||
return accountId;
|
||||
}
|
||||
|
||||
function loadCredentials() {
|
||||
// Force the SDK to re-resolve credentials with the default provider chain.
|
||||
//
|
||||
// This action typically sets credentials in the environment via environment variables.
|
||||
// The SDK never refreshes those env-var-based credentials after initial load.
|
||||
// In case there were already env-var creds set in the actions environment when this action
|
||||
// loaded, this action needs to refresh the SDK creds after overwriting those environment variables.
|
||||
//
|
||||
// The credentials object needs to be entirely recreated (instead of simply refreshed),
|
||||
// because the credential object type could change when this action writes env var creds.
|
||||
// For example, the first load could return EC2 instance metadata credentials
|
||||
// in a self-hosted runner, and the second load could return environment credentials
|
||||
// from an assume-role call in this action.
|
||||
aws.config.credentials = null;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
aws.config.getCredentials((err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(aws.config.credentials);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async function validateCredentials(expectedAccessKeyId) {
|
||||
let credentials;
|
||||
try {
|
||||
credentials = await loadCredentials();
|
||||
|
||||
if (!credentials.accessKeyId) {
|
||||
throw new Error('Access key ID empty after loading credentials');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Credentials could not be loaded, please check your action inputs: ${error.message}`);
|
||||
}
|
||||
|
||||
const actualAccessKeyId = credentials.accessKeyId;
|
||||
|
||||
if (expectedAccessKeyId && expectedAccessKeyId != actualAccessKeyId) {
|
||||
throw new Error('Unexpected failure: Credentials loaded by the SDK do not match the access key ID configured by the action');
|
||||
}
|
||||
}
|
||||
|
||||
function getStsClient(region) {
|
||||
return new aws.STS({
|
||||
region,
|
||||
stsRegionalEndpoints: 'regional',
|
||||
customUserAgent: USER_AGENT
|
||||
});
|
||||
}
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
// Get inputs
|
||||
const accessKeyId = core.getInput('aws-access-key-id', { required: true });
|
||||
const secretAccessKey = core.getInput('aws-secret-access-key', { required: true });
|
||||
const accessKeyId = core.getInput('aws-access-key-id', { required: false });
|
||||
const secretAccessKey = core.getInput('aws-secret-access-key', { required: false });
|
||||
const region = core.getInput('aws-region', { required: true });
|
||||
const sessionToken = core.getInput('aws-session-token', { required: false });
|
||||
const maskAccountId = core.getInput('mask-aws-account-id', { required: false });
|
||||
const roleToAssume = core.getInput('role-to-assume', {required: false});
|
||||
const roleExternalId = core.getInput('role-external-id', { required: false });
|
||||
const roleDurationSeconds = core.getInput('role-duration-seconds', {required: false}) || MAX_ACTION_RUNTIME;
|
||||
|
||||
// Get role credentials if configured to do so
|
||||
if (roleToAssume) {
|
||||
const roleCredentials = await assumeRole(
|
||||
{accessKeyId, secretAccessKey, sessionToken, region, roleToAssume, roleDurationSeconds}
|
||||
);
|
||||
exportCredentials(roleCredentials);
|
||||
} else {
|
||||
exportCredentials({accessKeyId, secretAccessKey, sessionToken});
|
||||
const roleSessionName = core.getInput('role-session-name', { required: false }) || ROLE_SESSION_NAME;
|
||||
const roleSkipSessionTagging = core.getInput('role-skip-session-tagging', { required: false });
|
||||
|
||||
if (!region.match(REGION_REGEX)) {
|
||||
throw new Error(`Region is not valid: ${region}`);
|
||||
}
|
||||
|
||||
exportRegion(region);
|
||||
|
||||
await exportAccountId(maskAccountId);
|
||||
// Always export the source credentials and account ID.
|
||||
// The STS client for calling AssumeRole pulls creds from the environment.
|
||||
// Plus, in the assume role case, if the AssumeRole call fails, we want
|
||||
// the source credentials and accound ID to already be masked as secrets
|
||||
// in any error messages.
|
||||
if (accessKeyId) {
|
||||
if (!secretAccessKey) {
|
||||
throw new Error("'aws-secret-access-key' must be provided if 'aws-access-key-id' is provided");
|
||||
}
|
||||
|
||||
exportCredentials({accessKeyId, secretAccessKey, sessionToken});
|
||||
}
|
||||
|
||||
// Regardless of whether any source credentials were provided as inputs,
|
||||
// validate that the SDK can actually pick up credentials. This validates
|
||||
// cases where this action is on a self-hosted runner that doesn't have credentials
|
||||
// configured correctly, and cases where the user intended to provide input
|
||||
// credentials but the secrets inputs resolved to empty strings.
|
||||
await validateCredentials(accessKeyId);
|
||||
|
||||
const sourceAccountId = await exportAccountId(maskAccountId, region);
|
||||
|
||||
// Get role credentials if configured to do so
|
||||
if (roleToAssume) {
|
||||
const roleCredentials = await assumeRole({
|
||||
sourceAccountId,
|
||||
region,
|
||||
roleToAssume,
|
||||
roleExternalId,
|
||||
roleDurationSeconds,
|
||||
roleSessionName,
|
||||
roleSkipSessionTagging
|
||||
});
|
||||
exportCredentials(roleCredentials);
|
||||
await validateCredentials(roleCredentials.accessKeyId);
|
||||
await exportAccountId(maskAccountId, region);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
core.setFailed(error.message);
|
||||
|
||||
+356
-26
@@ -1,6 +1,6 @@
|
||||
const core = require('@actions/core');
|
||||
const assert = require('assert');
|
||||
|
||||
const aws = require('aws-sdk');
|
||||
const run = require('.');
|
||||
|
||||
jest.mock('@actions/core');
|
||||
@@ -13,7 +13,9 @@ const FAKE_STS_SECRET_ACCESS_KEY = 'STS-AWS-SECRET-ACCESS-KEY';
|
||||
const FAKE_STS_SESSION_TOKEN = 'STS-AWS-SESSION-TOKEN';
|
||||
const FAKE_REGION = 'fake-region-1';
|
||||
const FAKE_ACCOUNT_ID = '123456789012';
|
||||
const FAKE_ROLE_ACCOUNT_ID = '111111111111';
|
||||
const ROLE_NAME = 'MY-ROLE';
|
||||
const ROLE_ARN = 'arn:aws:iam::111111111111:role/MY-ROLE';
|
||||
const ENVIRONMENT_VARIABLE_OVERRIDES = {
|
||||
SHOW_STACK_TRACE: 'true',
|
||||
GITHUB_REPOSITORY: 'MY-REPOSITORY-NAME',
|
||||
@@ -30,23 +32,26 @@ function mockGetInput(requestResponse) {
|
||||
return requestResponse[name]
|
||||
}
|
||||
}
|
||||
const REQUIRED_INPUTS = {
|
||||
const CREDS_INPUTS = {
|
||||
'aws-access-key-id': FAKE_ACCESS_KEY_ID,
|
||||
'aws-secret-access-key': FAKE_SECRET_ACCESS_KEY
|
||||
};
|
||||
const DEFAULT_INPUTS = {
|
||||
...REQUIRED_INPUTS,
|
||||
...CREDS_INPUTS,
|
||||
'aws-session-token': FAKE_SESSION_TOKEN,
|
||||
'aws-region': FAKE_REGION,
|
||||
'mask-aws-account-id': 'TRUE'
|
||||
};
|
||||
const ASSUME_ROLE_INPUTS = {...REQUIRED_INPUTS, 'role-to-assume': ROLE_NAME, 'aws-region': FAKE_REGION};
|
||||
const ASSUME_ROLE_INPUTS = {...CREDS_INPUTS, 'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION};
|
||||
|
||||
const mockStsCallerIdentity = jest.fn();
|
||||
const mockStsAssumeRole = jest.fn();
|
||||
|
||||
jest.mock('aws-sdk', () => {
|
||||
return {
|
||||
config: {
|
||||
getCredentials: jest.fn()
|
||||
},
|
||||
STS: jest.fn(() => ({
|
||||
getCallerIdentity: mockStsCallerIdentity,
|
||||
assumeRole: mockStsAssumeRole,
|
||||
@@ -67,13 +72,39 @@ describe('Configure AWS Credentials', () => {
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(DEFAULT_INPUTS));
|
||||
|
||||
mockStsCallerIdentity.mockImplementation(() => {
|
||||
return {
|
||||
mockStsCallerIdentity.mockReset();
|
||||
mockStsCallerIdentity
|
||||
.mockReturnValueOnce({
|
||||
promise() {
|
||||
return Promise.resolve({ Account: FAKE_ACCOUNT_ID });
|
||||
}
|
||||
};
|
||||
});
|
||||
})
|
||||
.mockReturnValueOnce({
|
||||
promise() {
|
||||
return Promise.resolve({ Account: FAKE_ROLE_ACCOUNT_ID });
|
||||
}
|
||||
});
|
||||
|
||||
aws.config.getCredentials.mockReset();
|
||||
aws.config.getCredentials
|
||||
.mockImplementationOnce(callback => {
|
||||
if (!aws.config.credentials) {
|
||||
aws.config.credentials = {
|
||||
accessKeyId: FAKE_ACCESS_KEY_ID,
|
||||
secretAccessKey: FAKE_SECRET_ACCESS_KEY
|
||||
}
|
||||
}
|
||||
callback(null);
|
||||
})
|
||||
.mockImplementationOnce(callback => {
|
||||
if (!aws.config.credentials) {
|
||||
aws.config.credentials = {
|
||||
accessKeyId: FAKE_STS_ACCESS_KEY_ID,
|
||||
secretAccessKey: FAKE_STS_SECRET_ACCESS_KEY
|
||||
}
|
||||
}
|
||||
callback(null);
|
||||
});
|
||||
|
||||
mockStsAssumeRole.mockImplementation(() => {
|
||||
return {
|
||||
@@ -111,8 +142,77 @@ describe('Configure AWS Credentials', () => {
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_ACCOUNT_ID);
|
||||
});
|
||||
|
||||
test('hosted runners can pull creds from a self-hosted environment', async () => {
|
||||
const mockInputs = {'aws-region': FAKE_REGION};
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(mockInputs));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledTimes(0);
|
||||
expect(core.exportVariable).toHaveBeenCalledTimes(2);
|
||||
expect(core.setSecret).toHaveBeenCalledTimes(1);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_DEFAULT_REGION', FAKE_REGION);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_REGION', FAKE_REGION);
|
||||
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', FAKE_ACCOUNT_ID);
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_ACCOUNT_ID);
|
||||
});
|
||||
|
||||
test('action with no accessible credentials fails', async () => {
|
||||
process.env.SHOW_STACK_TRACE = 'false';
|
||||
const mockInputs = {'aws-region': FAKE_REGION};
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(mockInputs));
|
||||
aws.config.getCredentials.mockReset();
|
||||
aws.config.getCredentials.mockImplementation(callback => {
|
||||
callback(new Error('No credentials to load'));
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith("Credentials could not be loaded, please check your action inputs: No credentials to load");
|
||||
});
|
||||
|
||||
test('action with empty credentials fails', async () => {
|
||||
process.env.SHOW_STACK_TRACE = 'false';
|
||||
const mockInputs = {'aws-region': FAKE_REGION};
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(mockInputs));
|
||||
aws.config.getCredentials.mockReset();
|
||||
aws.config.getCredentials.mockImplementation(callback => {
|
||||
aws.config.credentials = {
|
||||
accessKeyId: ''
|
||||
}
|
||||
callback(null);
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith("Credentials could not be loaded, please check your action inputs: Access key ID empty after loading credentials");
|
||||
});
|
||||
|
||||
test('action fails when credentials are not set in the SDK correctly', async () => {
|
||||
process.env.SHOW_STACK_TRACE = 'false';
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(ASSUME_ROLE_INPUTS));
|
||||
aws.config.getCredentials.mockReset();
|
||||
aws.config.getCredentials.mockImplementation(callback => {
|
||||
aws.config.credentials = {
|
||||
accessKeyId: FAKE_ACCESS_KEY_ID
|
||||
}
|
||||
callback(null);
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith("Unexpected failure: Credentials loaded by the SDK do not match the access key ID configured by the action");
|
||||
});
|
||||
|
||||
test('session token is optional', async () => {
|
||||
const mockInputs = {...REQUIRED_INPUTS, 'aws-region': 'eu-west-1'};
|
||||
const mockInputs = {...CREDS_INPUTS, 'aws-region': 'eu-west-1'};
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(mockInputs));
|
||||
@@ -131,8 +231,66 @@ describe('Configure AWS Credentials', () => {
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_ACCOUNT_ID);
|
||||
});
|
||||
|
||||
test('existing env var creds are cleared', async () => {
|
||||
const mockInputs = {...CREDS_INPUTS, 'aws-region': 'eu-west-1'};
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(mockInputs));
|
||||
process.env.AWS_ACCESS_KEY_ID = 'foo';
|
||||
process.env.AWS_SECRET_ACCESS_KEY = 'bar';
|
||||
process.env.AWS_SESSION_TOKEN = 'helloworld';
|
||||
aws.config.credentials = {
|
||||
accessKeyId: 'foo',
|
||||
secretAccessKey: 'bar',
|
||||
sessionToken: 'helloworld'
|
||||
};
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledTimes(0);
|
||||
expect(core.exportVariable).toHaveBeenCalledTimes(5);
|
||||
expect(core.setSecret).toHaveBeenCalledTimes(3);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_ACCESS_KEY_ID', FAKE_ACCESS_KEY_ID);
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_ACCESS_KEY_ID);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SECRET_ACCESS_KEY', FAKE_SECRET_ACCESS_KEY);
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_SECRET_ACCESS_KEY);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SESSION_TOKEN', '');
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_DEFAULT_REGION', 'eu-west-1');
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_REGION', 'eu-west-1');
|
||||
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', FAKE_ACCOUNT_ID);
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_ACCOUNT_ID);
|
||||
expect(aws.config.credentials.accessKeyId).toBe(FAKE_ACCESS_KEY_ID);
|
||||
expect(aws.config.credentials.secretAccessKey).toBe(FAKE_SECRET_ACCESS_KEY);
|
||||
expect(aws.config.credentials.sessionToken).toBeUndefined();
|
||||
});
|
||||
|
||||
test('validates region name', async () => {
|
||||
process.env.SHOW_STACK_TRACE = 'false';
|
||||
|
||||
const mockInputs = {...CREDS_INPUTS, 'aws-region': '$AWS_REGION'};
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(mockInputs));
|
||||
|
||||
await run();
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith('Region is not valid: $AWS_REGION');
|
||||
});
|
||||
|
||||
test('throws error if access key id exists but missing secret access key', async () => {
|
||||
process.env.SHOW_STACK_TRACE = 'false';
|
||||
const inputsWIthoutSecretKey = {...ASSUME_ROLE_INPUTS}
|
||||
inputsWIthoutSecretKey["aws-secret-access-key"] = undefined
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(inputsWIthoutSecretKey));
|
||||
|
||||
await run();
|
||||
expect(core.setFailed).toHaveBeenCalledWith("'aws-secret-access-key' must be provided if 'aws-access-key-id' is provided");
|
||||
|
||||
});
|
||||
|
||||
test('can opt out of masking account ID', async () => {
|
||||
const mockInputs = {...REQUIRED_INPUTS, 'aws-region': 'us-east-1', 'mask-aws-account-id': 'false'};
|
||||
const mockInputs = {...CREDS_INPUTS, 'aws-region': 'us-east-1', 'mask-aws-account-id': 'false'};
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(mockInputs));
|
||||
@@ -153,6 +311,7 @@ describe('Configure AWS Credentials', () => {
|
||||
test('error is caught by core.setFailed and caught', async () => {
|
||||
process.env.SHOW_STACK_TRACE = 'false';
|
||||
|
||||
mockStsCallerIdentity.mockReset();
|
||||
mockStsCallerIdentity.mockImplementation(() => {
|
||||
throw new Error();
|
||||
});
|
||||
@@ -164,6 +323,7 @@ describe('Configure AWS Credentials', () => {
|
||||
|
||||
test('error is caught by core.setFailed and passed', async () => {
|
||||
|
||||
mockStsCallerIdentity.mockReset();
|
||||
mockStsCallerIdentity.mockImplementation(() => {
|
||||
throw new Error();
|
||||
});
|
||||
@@ -178,20 +338,65 @@ describe('Configure AWS Credentials', () => {
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(ASSUME_ROLE_INPUTS));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledTimes(1);
|
||||
expect(core.exportVariable).toHaveBeenCalledTimes(7);
|
||||
expect(core.setSecret).toHaveBeenCalledTimes(7);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(2);
|
||||
|
||||
// first the source credentials are exported and masked
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(1, FAKE_ACCESS_KEY_ID);
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(2, FAKE_SECRET_ACCESS_KEY);
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(3, FAKE_ACCOUNT_ID);
|
||||
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(1, 'AWS_DEFAULT_REGION', FAKE_REGION);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(2, 'AWS_REGION', FAKE_REGION);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(3, 'AWS_ACCESS_KEY_ID', FAKE_ACCESS_KEY_ID);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(4, 'AWS_SECRET_ACCESS_KEY', FAKE_SECRET_ACCESS_KEY);
|
||||
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'aws-account-id', FAKE_ACCOUNT_ID);
|
||||
|
||||
// then the role credentials are exported and masked
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(4, FAKE_STS_ACCESS_KEY_ID);
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(5, FAKE_STS_SECRET_ACCESS_KEY);
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(6, FAKE_STS_SESSION_TOKEN);
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(7, FAKE_ROLE_ACCOUNT_ID);
|
||||
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(5, 'AWS_ACCESS_KEY_ID', FAKE_STS_ACCESS_KEY_ID);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(6, 'AWS_SECRET_ACCESS_KEY', FAKE_STS_SECRET_ACCESS_KEY);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(7, 'AWS_SESSION_TOKEN', FAKE_STS_SESSION_TOKEN);
|
||||
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(2, 'aws-account-id', FAKE_ROLE_ACCOUNT_ID);
|
||||
});
|
||||
|
||||
test('assume role can pull source credentials from self-hosted environment', async () => {
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput({'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION}));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledTimes(1);
|
||||
expect(core.exportVariable).toHaveBeenCalledTimes(5);
|
||||
expect(core.setSecret).toHaveBeenCalledTimes(4);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_ACCESS_KEY_ID', FAKE_STS_ACCESS_KEY_ID);
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_STS_ACCESS_KEY_ID);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SECRET_ACCESS_KEY', FAKE_STS_SECRET_ACCESS_KEY);
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_STS_SECRET_ACCESS_KEY);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SESSION_TOKEN', FAKE_STS_SESSION_TOKEN);
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_STS_SESSION_TOKEN);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_DEFAULT_REGION', FAKE_REGION);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_REGION', FAKE_REGION);
|
||||
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', FAKE_ACCOUNT_ID);
|
||||
expect(core.setSecret).toHaveBeenCalledWith(FAKE_ACCOUNT_ID);
|
||||
expect(core.setSecret).toHaveBeenCalledTimes(5);
|
||||
expect(core.setOutput).toHaveBeenCalledTimes(2);
|
||||
|
||||
// first the source account is exported and masked
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(1, FAKE_ACCOUNT_ID);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(1, 'AWS_DEFAULT_REGION', FAKE_REGION);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(2, 'AWS_REGION', FAKE_REGION);
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'aws-account-id', FAKE_ACCOUNT_ID);
|
||||
|
||||
// then the role credentials are exported and masked
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(2, FAKE_STS_ACCESS_KEY_ID);
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(3, FAKE_STS_SECRET_ACCESS_KEY);
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(4, FAKE_STS_SESSION_TOKEN);
|
||||
expect(core.setSecret).toHaveBeenNthCalledWith(5, FAKE_ROLE_ACCOUNT_ID);
|
||||
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(3, 'AWS_ACCESS_KEY_ID', FAKE_STS_ACCESS_KEY_ID);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(4, 'AWS_SECRET_ACCESS_KEY', FAKE_STS_SECRET_ACCESS_KEY);
|
||||
expect(core.exportVariable).toHaveBeenNthCalledWith(5, 'AWS_SESSION_TOKEN', FAKE_STS_SESSION_TOKEN);
|
||||
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(2, 'aws-account-id', FAKE_ROLE_ACCOUNT_ID);
|
||||
});
|
||||
|
||||
test('role assumption tags', async () => {
|
||||
@@ -201,7 +406,7 @@ describe('Configure AWS Credentials', () => {
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: ROLE_NAME,
|
||||
RoleArn: ROLE_ARN,
|
||||
RoleSessionName: 'GitHubActions',
|
||||
DurationSeconds: 6 * 3600,
|
||||
Tags: [
|
||||
@@ -223,7 +428,7 @@ describe('Configure AWS Credentials', () => {
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: ROLE_NAME,
|
||||
RoleArn: ROLE_ARN,
|
||||
RoleSessionName: 'GitHubActions',
|
||||
DurationSeconds: 5,
|
||||
Tags: [
|
||||
@@ -238,18 +443,85 @@ describe('Configure AWS Credentials', () => {
|
||||
})
|
||||
});
|
||||
|
||||
test('role assumption session name provided', async () => {
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput({...ASSUME_ROLE_INPUTS, 'role-session-name': 'MySessionName'}));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: ROLE_ARN,
|
||||
RoleSessionName: 'MySessionName',
|
||||
DurationSeconds: 6 * 3600,
|
||||
Tags: [
|
||||
{Key: 'GitHub', Value: 'Actions'},
|
||||
{Key: 'Repository', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REPOSITORY},
|
||||
{Key: 'Workflow', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_WORKFLOW},
|
||||
{Key: 'Action', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTION},
|
||||
{Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
|
||||
{Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
|
||||
{Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
test('role name provided instead of ARN', async () => {
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput({...CREDS_INPUTS, 'role-to-assume': ROLE_NAME, 'aws-region': FAKE_REGION}));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: 'arn:aws:iam::123456789012:role/MY-ROLE',
|
||||
RoleSessionName: 'GitHubActions',
|
||||
DurationSeconds: 6 * 3600,
|
||||
Tags: [
|
||||
{Key: 'GitHub', Value: 'Actions'},
|
||||
{Key: 'Repository', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REPOSITORY},
|
||||
{Key: 'Workflow', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_WORKFLOW},
|
||||
{Key: 'Action', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTION},
|
||||
{Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
|
||||
{Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
|
||||
{Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
test('role external ID provided', async () => {
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput({...ASSUME_ROLE_INPUTS, 'role-external-id': 'abcdef'}));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: ROLE_ARN,
|
||||
RoleSessionName: 'GitHubActions',
|
||||
DurationSeconds: 6 * 3600,
|
||||
Tags: [
|
||||
{Key: 'GitHub', Value: 'Actions'},
|
||||
{Key: 'Repository', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REPOSITORY},
|
||||
{Key: 'Workflow', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_WORKFLOW},
|
||||
{Key: 'Action', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTION},
|
||||
{Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
|
||||
{Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
|
||||
{Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
|
||||
],
|
||||
ExternalId: 'abcdef'
|
||||
})
|
||||
});
|
||||
|
||||
test('workflow name sanitized in role assumption tags', async () => {
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput(ASSUME_ROLE_INPUTS));
|
||||
|
||||
|
||||
process.env = {...process.env, GITHUB_WORKFLOW: 'Workflow!"#$%&\'()*+, -./:;<=>?@[]^_`{|}~🙂💥🍌1yFvMOeD3ZHYsHrGjCceOboMYzBPo0CRNFdcsVRG6UgR3A912a8KfcBtEVvkAS7kRBq80umGff8mux5IN1y55HQWPNBNyaruuVr4islFXte4FDQZexGJRUSMyHQpxJ8OmZnET84oDmbvmIjgxI6IBrdihX9PHMapT4gQvRYnLqNiKb18rEMWDNoZRy51UPX5sWK2GKPipgKSO9kqLckZai9D2AN2RlWCxtMqChNtxuxjqeqhoQZo0oaq39sjcRZgAAAAAAA'};
|
||||
|
||||
const sanitizedWorkflowName = 'Workflow__________+, -./:;<=>?@____________1yFvMOeD3ZHYsHrGjCceOboMYzBPo0CRNFdcsVRG6UgR3A912a8KfcBtEVvkAS7kRBq80umGff8mux5IN1y55HQWPNBNyaruuVr4islFXte4FDQZexGJRUSMyHQpxJ8OmZnET84oDmbvmIjgxI6IBrdihX9PHMapT4gQvRYnLqNiKb18rEMWDNoZRy51UPX5sWK2GKPipgKSO9kqLckZa'
|
||||
const sanitizedWorkflowName = 'Workflow__________+_ -./:;<=>?@____________1yFvMOeD3ZHYsHrGjCceOboMYzBPo0CRNFdcsVRG6UgR3A912a8KfcBtEVvkAS7kRBq80umGff8mux5IN1y55HQWPNBNyaruuVr4islFXte4FDQZexGJRUSMyHQpxJ8OmZnET84oDmbvmIjgxI6IBrdihX9PHMapT4gQvRYnLqNiKb18rEMWDNoZRy51UPX5sWK2GKPipgKSO9kqLckZa'
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: ROLE_NAME,
|
||||
RoleArn: ROLE_ARN,
|
||||
RoleSessionName: 'GitHubActions',
|
||||
DurationSeconds: 6 * 3600,
|
||||
Tags: [
|
||||
@@ -264,4 +536,62 @@ describe('Configure AWS Credentials', () => {
|
||||
})
|
||||
});
|
||||
|
||||
test('skip tagging provided as true', async () => {
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput({...ASSUME_ROLE_INPUTS, 'role-skip-session-tagging': true}));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: ROLE_ARN,
|
||||
RoleSessionName: 'GitHubActions',
|
||||
DurationSeconds: 21600,
|
||||
Tags: undefined
|
||||
})
|
||||
});
|
||||
|
||||
test('skip tagging provided as false', async () => {
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput({...ASSUME_ROLE_INPUTS, 'role-skip-session-tagging': false}));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: ROLE_ARN,
|
||||
RoleSessionName: 'GitHubActions',
|
||||
DurationSeconds: 21600,
|
||||
Tags: [
|
||||
{Key: 'GitHub', Value: 'Actions'},
|
||||
{Key: 'Repository', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REPOSITORY},
|
||||
{Key: 'Workflow', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_WORKFLOW},
|
||||
{Key: 'Action', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTION},
|
||||
{Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
|
||||
{Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
|
||||
{Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
test('skip tagging not provided', async () => {
|
||||
core.getInput = jest
|
||||
.fn()
|
||||
.mockImplementation(mockGetInput({...ASSUME_ROLE_INPUTS}));
|
||||
|
||||
await run();
|
||||
expect(mockStsAssumeRole).toHaveBeenCalledWith({
|
||||
RoleArn: ROLE_ARN,
|
||||
RoleSessionName: 'GitHubActions',
|
||||
DurationSeconds: 21600,
|
||||
Tags: [
|
||||
{Key: 'GitHub', Value: 'Actions'},
|
||||
{Key: 'Repository', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REPOSITORY},
|
||||
{Key: 'Workflow', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_WORKFLOW},
|
||||
{Key: 'Action', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTION},
|
||||
{Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
|
||||
{Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
|
||||
{Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Generated
+1474
-1422
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aws-actions-configure-aws-credentials",
|
||||
"version": "1.1.2",
|
||||
"version": "1.4.3",
|
||||
"description": "Configure AWS Credentials",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -25,12 +25,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/aws-actions/configure-aws-credentials#readme",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.2",
|
||||
"aws-sdk": "^2.613.0"
|
||||
"@actions/core": "^1.2.4",
|
||||
"aws-sdk": "^2.713.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@zeit/ncc": "^0.21.0",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.1.0"
|
||||
"@zeit/ncc": "^0.22.3",
|
||||
"eslint": "^7.4.0",
|
||||
"jest": "^26.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user