mirror of
https://github.com/aws-actions/configure-aws-credentials.git
synced 2026-09-04 06:15:07 +09:00
implement last changes for vNext
This commit is contained in:
@@ -107,6 +107,7 @@ overrides:
|
|||||||
'@typescript-eslint/prefer-includes': [warn]
|
'@typescript-eslint/prefer-includes': [warn]
|
||||||
dot-notation: [off]
|
dot-notation: [off]
|
||||||
'@typescript-eslint/dot-notation': [error]
|
'@typescript-eslint/dot-notation': [error]
|
||||||
|
'@typescript-eslint/no-explicit-any': [off]
|
||||||
'@typescript-eslint/consistent-type-exports': [warn]
|
'@typescript-eslint/consistent-type-exports': [warn]
|
||||||
'@typescript-eslint/consistent-type-imports': [warn]
|
'@typescript-eslint/consistent-type-imports': [warn]
|
||||||
'@typescript-eslint/no-base-to-string': [error]
|
'@typescript-eslint/no-base-to-string': [error]
|
||||||
|
|||||||
+12
-3
@@ -34,9 +34,6 @@ inputs:
|
|||||||
description: The audience to use for the OIDC provider
|
description: The audience to use for the OIDC provider
|
||||||
required: false
|
required: false
|
||||||
default: sts.amazonaws.com
|
default: sts.amazonaws.com
|
||||||
disable-oidc:
|
|
||||||
description: Strictly disable action from attempting to fetch credentials with OIDC
|
|
||||||
required: false
|
|
||||||
http-proxy:
|
http-proxy:
|
||||||
description: 'Proxy to use for the AWS SDK agent'
|
description: 'Proxy to use for the AWS SDK agent'
|
||||||
required: false
|
required: false
|
||||||
@@ -61,6 +58,18 @@ inputs:
|
|||||||
managed-session-policies:
|
managed-session-policies:
|
||||||
description: 'List of managed session policies'
|
description: 'List of managed session policies'
|
||||||
required: false
|
required: false
|
||||||
|
output-credentials:
|
||||||
|
description: Whether to set credentials as step output
|
||||||
|
required: false
|
||||||
|
unset-current-credentials:
|
||||||
|
description: Whether to unset the existing credentials in your runner
|
||||||
|
required: false
|
||||||
|
disable-retry:
|
||||||
|
description: Whether to disable the retry and backoff mechanism when the assume role call fails
|
||||||
|
required: false
|
||||||
|
retry-max-attempts:
|
||||||
|
description: The maximum number of attempts it will attempt to retry the assume role call
|
||||||
|
required: false
|
||||||
outputs:
|
outputs:
|
||||||
aws-account-id:
|
aws-account-id:
|
||||||
description: The AWS account ID for the provided credentials
|
description: The AWS account ID for the provided credentials
|
||||||
|
|||||||
+23
-4
@@ -17583,14 +17583,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.isDefined = exports.errorMessage = exports.retryAndBackoff = exports.reset = exports.withsleep = exports.defaultSleep = exports.sanitizeGitHubVariables = exports.exportAccountId = exports.exportRegion = exports.exportCredentials = void 0;
|
exports.isDefined = exports.errorMessage = exports.retryAndBackoff = exports.reset = exports.withsleep = exports.defaultSleep = exports.sanitizeGitHubVariables = exports.exportAccountId = exports.exportRegion = exports.unsetCredentials = exports.exportCredentials = void 0;
|
||||||
const core = __importStar(__nccwpck_require__(2186));
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
const client_sts_1 = __nccwpck_require__(2209);
|
const client_sts_1 = __nccwpck_require__(2209);
|
||||||
const MAX_TAG_VALUE_LENGTH = 256;
|
const MAX_TAG_VALUE_LENGTH = 256;
|
||||||
const SANITIZATION_CHARACTER = '_';
|
const SANITIZATION_CHARACTER = '_';
|
||||||
// Configure the AWS CLI and AWS SDKs using environment variables and set them as secrets.
|
// 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
|
// Setting the credentials as secrets masks them in Github Actions logs
|
||||||
function exportCredentials(creds) {
|
function exportCredentials(creds, outputCredentials) {
|
||||||
if (creds?.AccessKeyId) {
|
if (creds?.AccessKeyId) {
|
||||||
core.setSecret(creds.AccessKeyId);
|
core.setSecret(creds.AccessKeyId);
|
||||||
core.exportVariable('AWS_ACCESS_KEY_ID', creds.AccessKeyId);
|
core.exportVariable('AWS_ACCESS_KEY_ID', creds.AccessKeyId);
|
||||||
@@ -17607,8 +17607,27 @@ function exportCredentials(creds) {
|
|||||||
// clear session token from previous credentials action
|
// clear session token from previous credentials action
|
||||||
core.exportVariable('AWS_SESSION_TOKEN', '');
|
core.exportVariable('AWS_SESSION_TOKEN', '');
|
||||||
}
|
}
|
||||||
|
if (outputCredentials) {
|
||||||
|
if (creds?.AccessKeyId) {
|
||||||
|
core.setOutput('aws-access-key-id', creds.AccessKeyId);
|
||||||
|
}
|
||||||
|
if (creds?.SecretAccessKey) {
|
||||||
|
core.setOutput('aws-secret-access-key', creds.SecretAccessKey);
|
||||||
|
}
|
||||||
|
if (creds?.SessionToken) {
|
||||||
|
core.setOutput('aws-session-token', creds.SessionToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.exportCredentials = exportCredentials;
|
exports.exportCredentials = exportCredentials;
|
||||||
|
function unsetCredentials() {
|
||||||
|
core.exportVariable('AWS_ACCESS_KEY_ID', '');
|
||||||
|
core.exportVariable('AWS_SECRET_ACCESS_KEY', '');
|
||||||
|
core.exportVariable('AWS_SESSION_TOKEN', '');
|
||||||
|
core.exportVariable('AWS_REGION', '');
|
||||||
|
core.exportVariable('AWS_DEFAULT_REGION', '');
|
||||||
|
}
|
||||||
|
exports.unsetCredentials = unsetCredentials;
|
||||||
function exportRegion(region) {
|
function exportRegion(region) {
|
||||||
core.exportVariable('AWS_DEFAULT_REGION', region);
|
core.exportVariable('AWS_DEFAULT_REGION', region);
|
||||||
core.exportVariable('AWS_REGION', region);
|
core.exportVariable('AWS_REGION', region);
|
||||||
@@ -17652,7 +17671,7 @@ function reset() {
|
|||||||
}
|
}
|
||||||
exports.reset = reset;
|
exports.reset = reset;
|
||||||
// Retries the promise with exponential backoff if the error isRetryable up to maxRetries time.
|
// Retries the promise with exponential backoff if the error isRetryable up to maxRetries time.
|
||||||
async function retryAndBackoff(fn, isRetryable, retries = 0, maxRetries = 12, base = 50) {
|
async function retryAndBackoff(fn, isRetryable, maxRetries = 12, retries = 0, base = 50) {
|
||||||
try {
|
try {
|
||||||
return await fn();
|
return await fn();
|
||||||
}
|
}
|
||||||
@@ -17666,7 +17685,7 @@ async function retryAndBackoff(fn, isRetryable, retries = 0, maxRetries = 12, ba
|
|||||||
if (retries === maxRetries) {
|
if (retries === maxRetries) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
return await retryAndBackoff(fn, isRetryable, retries, maxRetries, base);
|
return await retryAndBackoff(fn, isRetryable, maxRetries, retries, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.retryAndBackoff = retryAndBackoff;
|
exports.retryAndBackoff = retryAndBackoff;
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
import type { Credentials } from '@aws-sdk/client-sts';
|
import type { Credentials } from '@aws-sdk/client-sts';
|
||||||
import type { CredentialsClient } from './CredentialsClient';
|
import type { CredentialsClient } from './CredentialsClient';
|
||||||
export declare function exportCredentials(creds?: Partial<Credentials>): void;
|
export declare function exportCredentials(creds?: Partial<Credentials>, outputCredentials?: boolean): void;
|
||||||
|
export declare function unsetCredentials(): void;
|
||||||
export declare function exportRegion(region: string): void;
|
export declare function exportRegion(region: string): void;
|
||||||
export declare function exportAccountId(credentialsClient: CredentialsClient, maskAccountId?: boolean): Promise<string>;
|
export declare function exportAccountId(credentialsClient: CredentialsClient, maskAccountId?: boolean): Promise<string>;
|
||||||
export declare function sanitizeGitHubVariables(name: string): string;
|
export declare function sanitizeGitHubVariables(name: string): string;
|
||||||
@@ -8,7 +9,7 @@ export declare function defaultSleep(ms: number): Promise<unknown>;
|
|||||||
declare let sleep: typeof defaultSleep;
|
declare let sleep: typeof defaultSleep;
|
||||||
export declare function withsleep(s: typeof sleep): void;
|
export declare function withsleep(s: typeof sleep): void;
|
||||||
export declare function reset(): void;
|
export declare function reset(): void;
|
||||||
export declare function retryAndBackoff<T>(fn: () => Promise<T>, isRetryable: boolean, retries?: number, maxRetries?: number, base?: number): Promise<T>;
|
export declare function retryAndBackoff<T>(fn: () => Promise<T>, isRetryable: boolean, maxRetries?: number, retries?: number, base?: number): Promise<T>;
|
||||||
export declare function errorMessage(error: unknown): string;
|
export declare function errorMessage(error: unknown): string;
|
||||||
export declare function isDefined<T>(i: T | undefined | null): i is T;
|
export declare function isDefined<T>(i: T | undefined | null): i is T;
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
+36
-10
@@ -241,14 +241,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.isDefined = exports.errorMessage = exports.retryAndBackoff = exports.reset = exports.withsleep = exports.defaultSleep = exports.sanitizeGitHubVariables = exports.exportAccountId = exports.exportRegion = exports.exportCredentials = void 0;
|
exports.isDefined = exports.errorMessage = exports.retryAndBackoff = exports.reset = exports.withsleep = exports.defaultSleep = exports.sanitizeGitHubVariables = exports.exportAccountId = exports.exportRegion = exports.unsetCredentials = exports.exportCredentials = void 0;
|
||||||
const core = __importStar(__nccwpck_require__(2186));
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
const client_sts_1 = __nccwpck_require__(2209);
|
const client_sts_1 = __nccwpck_require__(2209);
|
||||||
const MAX_TAG_VALUE_LENGTH = 256;
|
const MAX_TAG_VALUE_LENGTH = 256;
|
||||||
const SANITIZATION_CHARACTER = '_';
|
const SANITIZATION_CHARACTER = '_';
|
||||||
// Configure the AWS CLI and AWS SDKs using environment variables and set them as secrets.
|
// 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
|
// Setting the credentials as secrets masks them in Github Actions logs
|
||||||
function exportCredentials(creds) {
|
function exportCredentials(creds, outputCredentials) {
|
||||||
if (creds?.AccessKeyId) {
|
if (creds?.AccessKeyId) {
|
||||||
core.setSecret(creds.AccessKeyId);
|
core.setSecret(creds.AccessKeyId);
|
||||||
core.exportVariable('AWS_ACCESS_KEY_ID', creds.AccessKeyId);
|
core.exportVariable('AWS_ACCESS_KEY_ID', creds.AccessKeyId);
|
||||||
@@ -265,8 +265,27 @@ function exportCredentials(creds) {
|
|||||||
// clear session token from previous credentials action
|
// clear session token from previous credentials action
|
||||||
core.exportVariable('AWS_SESSION_TOKEN', '');
|
core.exportVariable('AWS_SESSION_TOKEN', '');
|
||||||
}
|
}
|
||||||
|
if (outputCredentials) {
|
||||||
|
if (creds?.AccessKeyId) {
|
||||||
|
core.setOutput('aws-access-key-id', creds.AccessKeyId);
|
||||||
|
}
|
||||||
|
if (creds?.SecretAccessKey) {
|
||||||
|
core.setOutput('aws-secret-access-key', creds.SecretAccessKey);
|
||||||
|
}
|
||||||
|
if (creds?.SessionToken) {
|
||||||
|
core.setOutput('aws-session-token', creds.SessionToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.exportCredentials = exportCredentials;
|
exports.exportCredentials = exportCredentials;
|
||||||
|
function unsetCredentials() {
|
||||||
|
core.exportVariable('AWS_ACCESS_KEY_ID', '');
|
||||||
|
core.exportVariable('AWS_SECRET_ACCESS_KEY', '');
|
||||||
|
core.exportVariable('AWS_SESSION_TOKEN', '');
|
||||||
|
core.exportVariable('AWS_REGION', '');
|
||||||
|
core.exportVariable('AWS_DEFAULT_REGION', '');
|
||||||
|
}
|
||||||
|
exports.unsetCredentials = unsetCredentials;
|
||||||
function exportRegion(region) {
|
function exportRegion(region) {
|
||||||
core.exportVariable('AWS_DEFAULT_REGION', region);
|
core.exportVariable('AWS_DEFAULT_REGION', region);
|
||||||
core.exportVariable('AWS_REGION', region);
|
core.exportVariable('AWS_REGION', region);
|
||||||
@@ -310,7 +329,7 @@ function reset() {
|
|||||||
}
|
}
|
||||||
exports.reset = reset;
|
exports.reset = reset;
|
||||||
// Retries the promise with exponential backoff if the error isRetryable up to maxRetries time.
|
// Retries the promise with exponential backoff if the error isRetryable up to maxRetries time.
|
||||||
async function retryAndBackoff(fn, isRetryable, retries = 0, maxRetries = 12, base = 50) {
|
async function retryAndBackoff(fn, isRetryable, maxRetries = 12, retries = 0, base = 50) {
|
||||||
try {
|
try {
|
||||||
return await fn();
|
return await fn();
|
||||||
}
|
}
|
||||||
@@ -324,7 +343,7 @@ async function retryAndBackoff(fn, isRetryable, retries = 0, maxRetries = 12, ba
|
|||||||
if (retries === maxRetries) {
|
if (retries === maxRetries) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
return await retryAndBackoff(fn, isRetryable, retries, maxRetries, base);
|
return await retryAndBackoff(fn, isRetryable, maxRetries, retries, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.retryAndBackoff = retryAndBackoff;
|
exports.retryAndBackoff = retryAndBackoff;
|
||||||
@@ -398,12 +417,18 @@ async function run() {
|
|||||||
const roleSkipSessionTaggingInput = core.getInput('role-skip-session-tagging', { required: false }) || 'false';
|
const roleSkipSessionTaggingInput = core.getInput('role-skip-session-tagging', { required: false }) || 'false';
|
||||||
const roleSkipSessionTagging = roleSkipSessionTaggingInput.toLowerCase() === 'true';
|
const roleSkipSessionTagging = roleSkipSessionTaggingInput.toLowerCase() === 'true';
|
||||||
const proxyServer = core.getInput('http-proxy', { required: false });
|
const proxyServer = core.getInput('http-proxy', { required: false });
|
||||||
const disableOIDC = core.getInput('disable-oidc', { required: false });
|
|
||||||
const inlineSessionPolicy = core.getInput('inline-session-policy', { required: false });
|
const inlineSessionPolicy = core.getInput('inline-session-policy', { required: false });
|
||||||
const managedSessionPoliciesInput = core.getMultilineInput('managed-session-policies', { required: false });
|
const managedSessionPoliciesInput = core.getMultilineInput('managed-session-policies', { required: false });
|
||||||
const managedSessionPolicies = [];
|
const managedSessionPolicies = [];
|
||||||
const roleChainingInput = core.getInput('role-chaining', { required: false }) || 'false';
|
const roleChainingInput = core.getInput('role-chaining', { required: false }) || 'false';
|
||||||
const roleChaining = roleChainingInput.toLowerCase() === 'true';
|
const roleChaining = roleChainingInput.toLowerCase() === 'true';
|
||||||
|
const outputCredentialsInput = core.getInput('output-credentials', { required: false }) || 'false';
|
||||||
|
const outputCredentials = outputCredentialsInput.toLowerCase() === 'true';
|
||||||
|
const unsetCurrentCredentialsInput = core.getInput('unset-current-credentials', { required: false }) || 'false';
|
||||||
|
const unsetCurrentCredentials = unsetCurrentCredentialsInput.toLowerCase() === 'true';
|
||||||
|
const disableRetryInput = core.getInput('disable-retry', { required: false }) || 'false';
|
||||||
|
const disableRetry = disableRetryInput.toLowerCase() === 'true';
|
||||||
|
const maxRetries = parseInt(core.getInput('retry-max-attempts', { required: false })) || 12;
|
||||||
for (const managedSessionPolicy of managedSessionPoliciesInput) {
|
for (const managedSessionPolicy of managedSessionPoliciesInput) {
|
||||||
managedSessionPolicies.push({ arn: managedSessionPolicy });
|
managedSessionPolicies.push({ arn: managedSessionPolicy });
|
||||||
}
|
}
|
||||||
@@ -412,11 +437,10 @@ async function run() {
|
|||||||
// The `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variable is set when the `id-token` permission is granted.
|
// The `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variable is set when the `id-token` permission is granted.
|
||||||
// This is necessary to authenticate with OIDC, but not strictly set just for OIDC. If it is not set and all other
|
// This is necessary to authenticate with OIDC, but not strictly set just for OIDC. If it is not set and all other
|
||||||
// checks pass, it is likely but not guaranteed that the user needs but lacks this permission in their workflow.
|
// checks pass, it is likely but not guaranteed that the user needs but lacks this permission in their workflow.
|
||||||
// So, we will log a warning when it is the only piece absent, as well as add an opportunity to manually disable the entire check.
|
// So, we will log a warning when it is the only piece absent
|
||||||
if (!!roleToAssume &&
|
if (!!roleToAssume &&
|
||||||
!webIdentityTokenFile &&
|
!webIdentityTokenFile &&
|
||||||
!AccessKeyId &&
|
!AccessKeyId &&
|
||||||
!disableOIDC &&
|
|
||||||
!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] &&
|
!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] &&
|
||||||
!roleChaining) {
|
!roleChaining) {
|
||||||
core.info('It looks like you might be trying to authenticate with OIDC. Did you mean to set the `id-token` permission?');
|
core.info('It looks like you might be trying to authenticate with OIDC. Did you mean to set the `id-token` permission?');
|
||||||
@@ -425,9 +449,11 @@ async function run() {
|
|||||||
!!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] &&
|
!!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] &&
|
||||||
!AccessKeyId &&
|
!AccessKeyId &&
|
||||||
!webIdentityTokenFile &&
|
!webIdentityTokenFile &&
|
||||||
!disableOIDC &&
|
|
||||||
!roleChaining);
|
!roleChaining);
|
||||||
};
|
};
|
||||||
|
if (unsetCurrentCredentials) {
|
||||||
|
(0, helpers_1.unsetCredentials)();
|
||||||
|
}
|
||||||
if (!region.match(REGION_REGEX)) {
|
if (!region.match(REGION_REGEX)) {
|
||||||
throw new Error(`Region is not valid: ${region}`);
|
throw new Error(`Region is not valid: ${region}`);
|
||||||
}
|
}
|
||||||
@@ -477,9 +503,9 @@ async function run() {
|
|||||||
inlineSessionPolicy,
|
inlineSessionPolicy,
|
||||||
managedSessionPolicies,
|
managedSessionPolicies,
|
||||||
});
|
});
|
||||||
}, true);
|
}, !disableRetry, maxRetries);
|
||||||
core.info(`Authenticated as assumedRoleId ${roleCredentials.AssumedRoleUser.AssumedRoleId}`);
|
core.info(`Authenticated as assumedRoleId ${roleCredentials.AssumedRoleUser.AssumedRoleId}`);
|
||||||
(0, helpers_1.exportCredentials)(roleCredentials.Credentials);
|
(0, helpers_1.exportCredentials)(roleCredentials.Credentials, outputCredentials);
|
||||||
// We need to validate the credentials in 2 of our use-cases
|
// We need to validate the credentials in 2 of our use-cases
|
||||||
// First: self-hosted runners. If the GITHUB_ACTIONS environment variable
|
// First: self-hosted runners. If the GITHUB_ACTIONS environment variable
|
||||||
// is set to `true` then we are NOT in a self-hosted runner.
|
// is set to `true` then we are NOT in a self-hosted runner.
|
||||||
|
|||||||
+23
-3
@@ -8,7 +8,7 @@ const SANITIZATION_CHARACTER = '_';
|
|||||||
|
|
||||||
// Configure the AWS CLI and AWS SDKs using environment variables and set them as secrets.
|
// 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
|
// Setting the credentials as secrets masks them in Github Actions logs
|
||||||
export function exportCredentials(creds?: Partial<Credentials>) {
|
export function exportCredentials(creds?: Partial<Credentials>, outputCredentials?: boolean) {
|
||||||
if (creds?.AccessKeyId) {
|
if (creds?.AccessKeyId) {
|
||||||
core.setSecret(creds.AccessKeyId);
|
core.setSecret(creds.AccessKeyId);
|
||||||
core.exportVariable('AWS_ACCESS_KEY_ID', creds.AccessKeyId);
|
core.exportVariable('AWS_ACCESS_KEY_ID', creds.AccessKeyId);
|
||||||
@@ -26,6 +26,26 @@ export function exportCredentials(creds?: Partial<Credentials>) {
|
|||||||
// clear session token from previous credentials action
|
// clear session token from previous credentials action
|
||||||
core.exportVariable('AWS_SESSION_TOKEN', '');
|
core.exportVariable('AWS_SESSION_TOKEN', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outputCredentials) {
|
||||||
|
if (creds?.AccessKeyId) {
|
||||||
|
core.setOutput('aws-access-key-id', creds.AccessKeyId);
|
||||||
|
}
|
||||||
|
if (creds?.SecretAccessKey) {
|
||||||
|
core.setOutput('aws-secret-access-key', creds.SecretAccessKey);
|
||||||
|
}
|
||||||
|
if (creds?.SessionToken) {
|
||||||
|
core.setOutput('aws-session-token', creds.SessionToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unsetCredentials() {
|
||||||
|
core.exportVariable('AWS_ACCESS_KEY_ID', '');
|
||||||
|
core.exportVariable('AWS_SECRET_ACCESS_KEY', '');
|
||||||
|
core.exportVariable('AWS_SESSION_TOKEN', '');
|
||||||
|
core.exportVariable('AWS_REGION', '');
|
||||||
|
core.exportVariable('AWS_DEFAULT_REGION', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportRegion(region: string) {
|
export function exportRegion(region: string) {
|
||||||
@@ -74,8 +94,8 @@ export function reset() {
|
|||||||
export async function retryAndBackoff<T>(
|
export async function retryAndBackoff<T>(
|
||||||
fn: () => Promise<T>,
|
fn: () => Promise<T>,
|
||||||
isRetryable: boolean,
|
isRetryable: boolean,
|
||||||
retries = 0,
|
|
||||||
maxRetries = 12,
|
maxRetries = 12,
|
||||||
|
retries = 0,
|
||||||
base = 50
|
base = 50
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
try {
|
try {
|
||||||
@@ -90,7 +110,7 @@ export async function retryAndBackoff<T>(
|
|||||||
if (retries === maxRetries) {
|
if (retries === maxRetries) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
return await retryAndBackoff(fn, isRetryable, retries, maxRetries, base);
|
return await retryAndBackoff(fn, isRetryable, maxRetries, retries, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-8
@@ -1,7 +1,14 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import { assumeRole } from './assumeRole';
|
import { assumeRole } from './assumeRole';
|
||||||
import { CredentialsClient } from './CredentialsClient';
|
import { CredentialsClient } from './CredentialsClient';
|
||||||
import { errorMessage, retryAndBackoff, exportRegion, exportCredentials, exportAccountId } from './helpers';
|
import {
|
||||||
|
errorMessage,
|
||||||
|
retryAndBackoff,
|
||||||
|
exportRegion,
|
||||||
|
exportCredentials,
|
||||||
|
exportAccountId,
|
||||||
|
unsetCredentials,
|
||||||
|
} from './helpers';
|
||||||
|
|
||||||
const DEFAULT_ROLE_DURATION = 3600; // One hour (seconds)
|
const DEFAULT_ROLE_DURATION = 3600; // One hour (seconds)
|
||||||
const ROLE_SESSION_NAME = 'GitHubActions';
|
const ROLE_SESSION_NAME = 'GitHubActions';
|
||||||
@@ -26,12 +33,18 @@ export async function run() {
|
|||||||
const roleSkipSessionTaggingInput = core.getInput('role-skip-session-tagging', { required: false }) || 'false';
|
const roleSkipSessionTaggingInput = core.getInput('role-skip-session-tagging', { required: false }) || 'false';
|
||||||
const roleSkipSessionTagging = roleSkipSessionTaggingInput.toLowerCase() === 'true';
|
const roleSkipSessionTagging = roleSkipSessionTaggingInput.toLowerCase() === 'true';
|
||||||
const proxyServer = core.getInput('http-proxy', { required: false });
|
const proxyServer = core.getInput('http-proxy', { required: false });
|
||||||
const disableOIDC = core.getInput('disable-oidc', { required: false });
|
|
||||||
const inlineSessionPolicy = core.getInput('inline-session-policy', { required: false });
|
const inlineSessionPolicy = core.getInput('inline-session-policy', { required: false });
|
||||||
const managedSessionPoliciesInput = core.getMultilineInput('managed-session-policies', { required: false });
|
const managedSessionPoliciesInput = core.getMultilineInput('managed-session-policies', { required: false });
|
||||||
const managedSessionPolicies: any[] = [];
|
const managedSessionPolicies: any[] = [];
|
||||||
const roleChainingInput = core.getInput('role-chaining', { required: false }) || 'false';
|
const roleChainingInput = core.getInput('role-chaining', { required: false }) || 'false';
|
||||||
const roleChaining = roleChainingInput.toLowerCase() === 'true';
|
const roleChaining = roleChainingInput.toLowerCase() === 'true';
|
||||||
|
const outputCredentialsInput = core.getInput('output-credentials', { required: false }) || 'false';
|
||||||
|
const outputCredentials = outputCredentialsInput.toLowerCase() === 'true';
|
||||||
|
const unsetCurrentCredentialsInput = core.getInput('unset-current-credentials', { required: false }) || 'false';
|
||||||
|
const unsetCurrentCredentials = unsetCurrentCredentialsInput.toLowerCase() === 'true';
|
||||||
|
const disableRetryInput = core.getInput('disable-retry', { required: false }) || 'false';
|
||||||
|
const disableRetry = disableRetryInput.toLowerCase() === 'true';
|
||||||
|
const maxRetries = parseInt(core.getInput('retry-max-attempts', { required: false })) || 12;
|
||||||
for (const managedSessionPolicy of managedSessionPoliciesInput) {
|
for (const managedSessionPolicy of managedSessionPoliciesInput) {
|
||||||
managedSessionPolicies.push({ arn: managedSessionPolicy });
|
managedSessionPolicies.push({ arn: managedSessionPolicy });
|
||||||
}
|
}
|
||||||
@@ -41,12 +54,11 @@ export async function run() {
|
|||||||
// The `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variable is set when the `id-token` permission is granted.
|
// The `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variable is set when the `id-token` permission is granted.
|
||||||
// This is necessary to authenticate with OIDC, but not strictly set just for OIDC. If it is not set and all other
|
// This is necessary to authenticate with OIDC, but not strictly set just for OIDC. If it is not set and all other
|
||||||
// checks pass, it is likely but not guaranteed that the user needs but lacks this permission in their workflow.
|
// checks pass, it is likely but not guaranteed that the user needs but lacks this permission in their workflow.
|
||||||
// So, we will log a warning when it is the only piece absent, as well as add an opportunity to manually disable the entire check.
|
// So, we will log a warning when it is the only piece absent
|
||||||
if (
|
if (
|
||||||
!!roleToAssume &&
|
!!roleToAssume &&
|
||||||
!webIdentityTokenFile &&
|
!webIdentityTokenFile &&
|
||||||
!AccessKeyId &&
|
!AccessKeyId &&
|
||||||
!disableOIDC &&
|
|
||||||
!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] &&
|
!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] &&
|
||||||
!roleChaining
|
!roleChaining
|
||||||
) {
|
) {
|
||||||
@@ -59,11 +71,14 @@ export async function run() {
|
|||||||
!!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] &&
|
!!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] &&
|
||||||
!AccessKeyId &&
|
!AccessKeyId &&
|
||||||
!webIdentityTokenFile &&
|
!webIdentityTokenFile &&
|
||||||
!disableOIDC &&
|
|
||||||
!roleChaining
|
!roleChaining
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (unsetCurrentCredentials) {
|
||||||
|
unsetCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
if (!region.match(REGION_REGEX)) {
|
if (!region.match(REGION_REGEX)) {
|
||||||
throw new Error(`Region is not valid: ${region}`);
|
throw new Error(`Region is not valid: ${region}`);
|
||||||
}
|
}
|
||||||
@@ -101,7 +116,8 @@ export async function run() {
|
|||||||
|
|
||||||
// Get role credentials if configured to do so
|
// Get role credentials if configured to do so
|
||||||
if (roleToAssume) {
|
if (roleToAssume) {
|
||||||
const roleCredentials = await retryAndBackoff(async () => {
|
const roleCredentials = await retryAndBackoff(
|
||||||
|
async () => {
|
||||||
return assumeRole({
|
return assumeRole({
|
||||||
credentialsClient,
|
credentialsClient,
|
||||||
sourceAccountId,
|
sourceAccountId,
|
||||||
@@ -115,9 +131,12 @@ export async function run() {
|
|||||||
inlineSessionPolicy,
|
inlineSessionPolicy,
|
||||||
managedSessionPolicies,
|
managedSessionPolicies,
|
||||||
});
|
});
|
||||||
}, true);
|
},
|
||||||
|
!disableRetry,
|
||||||
|
maxRetries
|
||||||
|
);
|
||||||
core.info(`Authenticated as assumedRoleId ${roleCredentials.AssumedRoleUser!.AssumedRoleId!}`);
|
core.info(`Authenticated as assumedRoleId ${roleCredentials.AssumedRoleUser!.AssumedRoleId!}`);
|
||||||
exportCredentials(roleCredentials.Credentials);
|
exportCredentials(roleCredentials.Credentials, outputCredentials);
|
||||||
// We need to validate the credentials in 2 of our use-cases
|
// We need to validate the credentials in 2 of our use-cases
|
||||||
// First: self-hosted runners. If the GITHUB_ACTIONS environment variable
|
// First: self-hosted runners. If the GITHUB_ACTIONS environment variable
|
||||||
// is set to `true` then we are NOT in a self-hosted runner.
|
// is set to `true` then we are NOT in a self-hosted runner.
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ const FAKE_ACCOUNT_ID = '123456789012';
|
|||||||
const FAKE_ROLE_ACCOUNT_ID = '111111111111';
|
const FAKE_ROLE_ACCOUNT_ID = '111111111111';
|
||||||
const ROLE_NAME = 'MY-ROLE';
|
const ROLE_NAME = 'MY-ROLE';
|
||||||
const ROLE_ARN = 'arn:aws:iam::111111111111:role/MY-ROLE';
|
const ROLE_ARN = 'arn:aws:iam::111111111111:role/MY-ROLE';
|
||||||
|
const MANAGED_SESSION_POLICY_INPUT = [
|
||||||
|
'arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess',
|
||||||
|
'arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess',
|
||||||
|
];
|
||||||
const ENVIRONMENT_VARIABLE_OVERRIDES = {
|
const ENVIRONMENT_VARIABLE_OVERRIDES = {
|
||||||
SHOW_STACK_TRACE: 'false',
|
SHOW_STACK_TRACE: 'false',
|
||||||
GITHUB_REPOSITORY: 'MY-REPOSITORY-NAME',
|
GITHUB_REPOSITORY: 'MY-REPOSITORY-NAME',
|
||||||
@@ -54,6 +58,12 @@ function mockGetInput(requestResponse: Record<string, string>) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mockGetMultilineInput(requestResponse: Record<string, string[]>) {
|
||||||
|
return function (name: string, _options: unknown): string[] {
|
||||||
|
return requestResponse[name]!;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
jest.mock('fs', () => ({
|
jest.mock('fs', () => ({
|
||||||
...jest.requireActual('fs'),
|
...jest.requireActual('fs'),
|
||||||
@@ -481,6 +491,40 @@ describe('Configure AWS Credentials', () => {
|
|||||||
expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand).length).toEqual(12);
|
expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand).length).toEqual(12);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('role assumption fails after one trial when disabling retry', async () => {
|
||||||
|
process.env['GITHUB_ACTIONS'] = 'true';
|
||||||
|
process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] = 'test-token';
|
||||||
|
jest
|
||||||
|
.spyOn(core, 'getInput')
|
||||||
|
.mockImplementation(
|
||||||
|
mockGetInput({ 'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION, 'disable-retry': 'true' })
|
||||||
|
);
|
||||||
|
|
||||||
|
mockedSTS.reset();
|
||||||
|
mockedSTS.on(AssumeRoleWithWebIdentityCommand).rejects();
|
||||||
|
|
||||||
|
await run();
|
||||||
|
expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand).length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('max retries is configurable', async () => {
|
||||||
|
process.env['GITHUB_ACTIONS'] = 'true';
|
||||||
|
process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] = 'test-token';
|
||||||
|
jest.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mockGetInput({
|
||||||
|
'role-to-assume': ROLE_ARN,
|
||||||
|
'aws-region': FAKE_REGION,
|
||||||
|
'retry-max-attempts': '15',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
mockedSTS.reset();
|
||||||
|
mockedSTS.on(AssumeRoleWithWebIdentityCommand).rejects();
|
||||||
|
|
||||||
|
await run();
|
||||||
|
expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand).length).toEqual(15);
|
||||||
|
});
|
||||||
|
|
||||||
test('role external ID provided', async () => {
|
test('role external ID provided', async () => {
|
||||||
jest
|
jest
|
||||||
.spyOn(core, 'getInput')
|
.spyOn(core, 'getInput')
|
||||||
@@ -614,4 +658,46 @@ describe('Configure AWS Credentials', () => {
|
|||||||
|
|
||||||
expect(core.exportVariable).toReturn();
|
expect(core.exportVariable).toReturn();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('inline policy and managed session policies are provided in assume role calls', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(core, 'getInput')
|
||||||
|
.mockImplementation(mockGetInput({ ...ASSUME_ROLE_INPUTS, 'inline-session-policy': 'inline' }));
|
||||||
|
|
||||||
|
jest
|
||||||
|
.spyOn(core, 'getMultilineInput')
|
||||||
|
.mockImplementation(mockGetMultilineInput({ 'managed-session-policies': MANAGED_SESSION_POLICY_INPUT }));
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({
|
||||||
|
RoleArn: ROLE_ARN,
|
||||||
|
RoleSessionName: 'GitHubActions',
|
||||||
|
DurationSeconds: 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: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA },
|
||||||
|
{ Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF },
|
||||||
|
],
|
||||||
|
Policy: 'inline',
|
||||||
|
PolicyArns: [
|
||||||
|
{ arn: 'arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess' },
|
||||||
|
{ arn: 'arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unsets credentials if enabled', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(core, 'getInput')
|
||||||
|
.mockImplementation(mockGetInput({ ...ASSUME_ROLE_INPUTS, 'unset-current-credentials': 'true' }));
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(core.exportVariable).toHaveBeenCalledTimes(9);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user