feat: upgraded to new GH OIDC API (#284)

This commit is contained in:
Richard H Boyd
2021-10-18 14:33:08 -04:00
committed by GitHub
parent 9aaa1daa91
commit 036a4a1ddf
4 changed files with 35 additions and 4220 deletions
+14 -7
View File
@@ -37,7 +37,10 @@ jobs:
deploy: deploy:
name: Upload to Amazon S3 name: Upload to Amazon S3
runs-on: ubuntu-latest runs-on: ubuntu-latest
# These permissions are needed to interact with GitHub's OIDC Token endpoint.
permissions:
id-token: write
contents: read
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -136,24 +139,28 @@ Resources:
Role: Role:
Type: AWS::IAM::Role Type: AWS::IAM::Role
Properties: Properties:
RoleName: ExampleGithubRole
AssumeRolePolicyDocument: AssumeRolePolicyDocument:
Statement: Statement:
- Effect: Allow - Effect: Allow
Action: sts:AssumeRoleWithWebIdentity Action: sts:AssumeRoleWithWebIdentity
Principal: Principal:
Federated: !Ref GithubOidc Federated: !If
- CreateOIDCProvider
- !Ref GithubOidc
- !Ref OIDCProviderArn
Condition: Condition:
StringLike: StringLike:
vstoken.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/${RepositoryName}:* token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/${RepositoryName}:*
GithubOidc: GithubOidc:
Type: AWS::IAM::OIDCProvider Type: AWS::IAM::OIDCProvider
Condition: CreateOIDCProvider Condition: CreateOIDCProvider
Properties: Properties:
Url: https://vstoken.actions.githubusercontent.com Url: https://token.actions.githubusercontent.com
ClientIdList: [sigstore] ClientIdList:
ThumbprintList: [a031c46782e6e6c662c2c87c76da9aa62ccabd8e] - sts.amazonaws.com
ThumbprintList:
- a031c46782e6e6c662c2c87c76da9aa62ccabd8e
Outputs: Outputs:
Role: Role:
+6 -4183
View File
File diff suppressed because one or more lines are too long
+6 -24
View File
@@ -3,7 +3,6 @@ const aws = require('aws-sdk');
const assert = require('assert'); const assert = require('assert');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const axios = require('axios');
// The max time that a GitHub action is allowed to run is 6 hours. // 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. // That seems like a reasonable default to use if no role duration is defined.
@@ -185,21 +184,6 @@ async function exportAccountId(maskAccountId, region) {
return accountId; return accountId;
} }
async function getWebIdentityToken() {
const isDefined = i => !!i;
const {ACTIONS_ID_TOKEN_REQUEST_URL, ACTIONS_ID_TOKEN_REQUEST_TOKEN} = process.env;
assert(
[ACTIONS_ID_TOKEN_REQUEST_URL, ACTIONS_ID_TOKEN_REQUEST_TOKEN].every(isDefined),
'Missing required environment value. Are you running in GitHub Actions?'
);
const { data } = await axios.get(`${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=sigstore`, {
headers: {"Authorization": `bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}`}
}
);
return data.value;
}
function loadCredentials() { function loadCredentials() {
// Force the SDK to re-resolve credentials with the default provider chain. // Force the SDK to re-resolve credentials with the default provider chain.
// //
@@ -303,7 +287,7 @@ async function run() {
let sourceAccountId; let sourceAccountId;
let webIdentityToken; let webIdentityToken;
if(useGitHubOIDCProvider()) { if(useGitHubOIDCProvider()) {
webIdentityToken = await getWebIdentityToken(); webIdentityToken = await core.getIDToken('sts.amazonaws.com');
roleDurationSeconds = core.getInput('role-duration-seconds', {required: false}) || DEFAULT_ROLE_DURATION_FOR_OIDC_ROLES; roleDurationSeconds = core.getInput('role-duration-seconds', {required: false}) || DEFAULT_ROLE_DURATION_FOR_OIDC_ROLES;
// We don't validate the credentials here because we don't have them yet when using OIDC. // We don't validate the credentials here because we don't have them yet when using OIDC.
} else { } else {
@@ -331,13 +315,11 @@ async function run() {
webIdentityToken webIdentityToken
}); });
exportCredentials(roleCredentials); exportCredentials(roleCredentials);
// I don't know a good workaround for this. I'm not sure why we're validating the credentials // We need to validate the credentials in 2 of our use-cases
// so frequently inside the action. The approach I've taken here is that if the GH OIDC token // First: self-hosted runners. If the GITHUB_ACTIONS environment variable
// isn't set, then we're in a self-hosted runner and we need to validate the credentials for // is set to `true` then we are NOT in a self-hosted runner.
// some mysterious reason that wasn't explained by whoever wrote this aciton. // Second: Customer provided credentials manually (IAM User keys stored in GH Secrets)
// if (!process.env.GITHUB_ACTIONS || accessKeyId) {
// It's gross but it works so ... ¯\_(ツ)_/¯
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN) {
await validateCredentials(roleCredentials.accessKeyId); await validateCredentials(roleCredentials.accessKeyId);
} }
await exportAccountId(maskAccountId, region); await exportAccountId(maskAccountId, region);
+9 -6
View File
@@ -2,10 +2,8 @@ const core = require('@actions/core');
const assert = require('assert'); const assert = require('assert');
const aws = require('aws-sdk'); const aws = require('aws-sdk');
const run = require('./index.js'); const run = require('./index.js');
const axios = require('axios');
jest.mock('@actions/core'); jest.mock('@actions/core');
jest.mock("axios");
const FAKE_ACCESS_KEY_ID = 'MY-AWS-ACCESS-KEY-ID'; const FAKE_ACCESS_KEY_ID = 'MY-AWS-ACCESS-KEY-ID';
const FAKE_SECRET_ACCESS_KEY = 'MY-AWS-SECRET-ACCESS-KEY'; const FAKE_SECRET_ACCESS_KEY = 'MY-AWS-SECRET-ACCESS-KEY';
@@ -91,6 +89,12 @@ describe('Configure AWS Credentials', () => {
.fn() .fn()
.mockImplementation(mockGetInput(DEFAULT_INPUTS)); .mockImplementation(mockGetInput(DEFAULT_INPUTS));
core.getIDToken = jest
.fn()
.mockImplementation(() => {
return "testtoken"
});
mockStsCallerIdentity.mockReset(); mockStsCallerIdentity.mockReset();
mockStsCallerIdentity mockStsCallerIdentity
.mockReturnValueOnce({ .mockReturnValueOnce({
@@ -569,9 +573,9 @@ describe('Configure AWS Credentials', () => {
}); });
test('only role arn and region provided to use GH OIDC Token', async () => { test('only role arn and region provided to use GH OIDC Token', async () => {
process.env.GITHUB_ACTIONS = 'true';
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'test-token'; process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'test-token';
process.env.ACTIONS_ID_TOKEN_REQUEST_URL = 'https://www.example.com/token/endpoint';
axios.get.mockImplementation(() => Promise.resolve({ data: {value: "testtoken"} }));
core.getInput = jest core.getInput = jest
.fn() .fn()
.mockImplementation(mockGetInput({'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION})); .mockImplementation(mockGetInput({'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION}));
@@ -590,9 +594,8 @@ describe('Configure AWS Credentials', () => {
test('GH OIDC With custom role duration', async () => { test('GH OIDC With custom role duration', async () => {
const CUSTOM_ROLE_DURATION = 1234; const CUSTOM_ROLE_DURATION = 1234;
process.env.GITHUB_ACTIONS = 'true';
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'test-token'; process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'test-token';
process.env.ACTIONS_ID_TOKEN_REQUEST_URL = 'https://www.example.com/token/endpoint';
axios.get.mockImplementation(() => Promise.resolve({ data: {value: "testtoken"} }));
core.getInput = jest core.getInput = jest
.fn() .fn()
.mockImplementation(mockGetInput({'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION, 'role-duration-seconds': CUSTOM_ROLE_DURATION})); .mockImplementation(mockGetInput({'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION, 'role-duration-seconds': CUSTOM_ROLE_DURATION}));