Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: sanitize AWS session tags #20

Merged
merged 8 commits into from
Jan 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,39 @@ 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.
The default session duration is 6 hours, but if you would like to adjust this you can pass a duration to `role-duration-seconds`.

Example:
```yaml
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
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-duration-seconds: 1200
```

### Session tagging
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|
| --- | --- |
| GitHub | "Actions" |
| Repository | GITHUB_REPOSITORY |
| Workflow | GITHUB_WORKFLOW |
| Action | GITHUB_ACTION |
| Actor | GITHUB_ACTOR |
| Branch | GITHUB_REF |
| Commit | GITHUB_SHA |

_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 '*'._

## License Summary

This code is made available under the MIT license.
21 changes: 19 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const util = require('util');
// 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 = '*'

async function assumeRole(params) {
// Assume a role to get short-lived credentials using longer-lived credentials.
Expand Down Expand Up @@ -36,9 +38,9 @@ async function assumeRole(params) {
Tags: [
{Key: 'GitHub', Value: 'Actions'},
{Key: 'Repository', Value: GITHUB_REPOSITORY},
{Key: 'Workflow', Value: GITHUB_WORKFLOW},
{Key: 'Workflow', Value: sanitizeGithubWorkflowName(GITHUB_WORKFLOW)},
{Key: 'Action', Value: GITHUB_ACTION},
{Key: 'Actor', Value: GITHUB_ACTOR},
{Key: 'Actor', Value: sanitizeGithubActor(GITHUB_ACTOR)},
{Key: 'Branch', Value: GITHUB_REF},
{Key: 'Commit', Value: GITHUB_SHA},
]
Expand All @@ -53,6 +55,21 @@ async function assumeRole(params) {
});
}

function sanitizeGithubActor(actor) {
// In some circumstances the actor may contain square brackets. For example, if they're a bot ('[bot]')
// Square brackets are not allowed in AWS session tags
return actor.replace(/\[|\]/g, SANITIZATION_CHARACTER)
}

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 nameTruncated = nameWithoutSpecialCharacters.slice(0, MAX_TAG_VALUE_LENGTH)
return nameTruncated
}

function exportCredentials(params){
// Configure the AWS CLI and AWS SDKs using environment variables and set them as secrets.
// Setting the credentials as secrets masks them in Github Actions logs
Expand Down
33 changes: 30 additions & 3 deletions index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ const ENVIRONMENT_VARIABLE_OVERRIDES = {
GITHUB_REPOSITORY: 'MY-REPOSITORY-NAME',
GITHUB_WORKFLOW: 'MY-WORKFLOW-ID',
GITHUB_ACTION: 'MY-ACTION-NAME',
GITHUB_ACTOR: 'MY-USERNAME',
GITHUB_ACTOR: 'MY-USERNAME[bot]',
GITHUB_REF: 'MY-BRANCH',
GITHUB_SHA: 'MY-COMMIT-ID',
};
const GITHUB_ACTOR_SANITIZED = 'MY-USERNAME*bot*'

function mockGetInput(requestResponse) {
return function (name, options) { // eslint-disable-line no-unused-vars
Expand Down Expand Up @@ -208,7 +209,7 @@ describe('Configure AWS Credentials', () => {
{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: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTOR},
{Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
{Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
{Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
]
Expand All @@ -230,7 +231,33 @@ describe('Configure AWS Credentials', () => {
{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: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTOR},
{Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
{Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
{Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
]
})
});

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'

await run();
expect(mockStsAssumeRole).toHaveBeenCalledWith({
RoleArn: ROLE_NAME,
RoleSessionName: 'GitHubActions',
DurationSeconds: 6 * 3600,
Tags: [
{Key: 'GitHub', Value: 'Actions'},
{Key: 'Repository', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REPOSITORY},
{Key: 'Workflow', Value: sanitizedWorkflowName},
{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},
]
Expand Down