Update documentation and emit log messages (#85)

This commit is contained in:
Seth Vargo 2021-12-09 17:27:21 -05:00 committed by GitHub
parent d03480e8ad
commit ac09c292a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 6 deletions

View File

@ -174,6 +174,21 @@ regardless of the authentication mechanism.
generate a credentials file which can be used for authentication via gcloud
and Google Cloud SDKs in other steps in the workflow. The default is true.
The credentials file is exported into `$GITHUB_WORKSPACE`, which makes it
available to all future steps and filesystems (including Docker-based
GitHub Actions). The file is automatically removed at the end of the job
via a post action. In order to use exported credentials, you **must** add
the `actions/checkout` step before calling `auth`. This is due to how
GitHub Actions creates `$GITHUB_WORKSPACE`:
```yaml
jobs:
job_id:
steps:
- uses: 'actions/checkout@v2' # Must come first!
- uses: 'google-github-actions/auth@v0'
```
- `delegates`: (Optional) List of additional service account emails or unique
identities to use for impersonation in the chain. By default there are no
delegates.
@ -419,7 +434,7 @@ the [gcloud][gcloud] command-line tool.
--display-name="Demo pool"
```
1. Get the full ID of the Workload Identity Pool:
1. Get the full ID of the Workload Identity **Pool**:
```sh
gcloud iam workload-identity-pools describe "my-pool" \
@ -435,7 +450,7 @@ the [gcloud][gcloud] command-line tool.
```
1. Create a Workload Identity Provider in that pool:
1. Create a Workload Identity **Provider** in that pool:
```sh
gcloud iam workload-identity-pools providers create-oidc "my-provider" \
@ -470,10 +485,24 @@ the [gcloud][gcloud] command-line tool.
export REPO="username/name" # e.g. "google/chrome"
gcloud iam service-accounts add-iam-policy-binding "my-service-account@${PROJECT_ID}.iam.gserviceaccount.com" \
--project="${PROJECT_ID}" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"
```
1. Extract the Workload Identity **Provider** resource name:
```sh
gcloud iam workload-identity-pools providers describe "my-provider" \
--project="${PROJECT_ID}"
--location="global" \
--workload-identity-pool="my-pool" \
--format='value(name)'
```
Use this value as the `workload_identity_provider` value in your GitHub
Actions YAML.
1. Use this GitHub Action with the Workload Identity Provider ID and Service
Account email. The GitHub Action will mint a GitHub OIDC token and exchange
the GitHub token for a Google Cloud access token (assuming the authorization

2
dist/main/index.js vendored

File diff suppressed because one or more lines are too long

2
dist/post/index.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,12 @@
'use strict';
import {
debug as logDebug,
exportVariable,
getBooleanInput,
getIDToken,
getInput,
info as logInfo,
setFailed,
setOutput,
setSecret,
@ -13,7 +15,7 @@ import { WorkloadIdentityClient } from './client/workload_identity_client';
import { CredentialsJSONClient } from './client/credentials_json_client';
import { AuthClient } from './client/auth_client';
import { BaseClient } from './base';
import { buildDomainWideDelegationJWT, explodeStrings, parseDuration } from './utils';
import { buildDomainWideDelegationJWT, errorMessage, explodeStrings, parseDuration } from './utils';
const secretsWarning =
`If you are specifying input values via GitHub secrets, ensure the secret ` +
@ -67,6 +69,8 @@ async function run(): Promise<void> {
// Instantiate the correct client based on the provided input parameters.
let client: AuthClient;
if (workloadIdentityProvider) {
logDebug(`Using workload identity provider "${workloadIdentityProvider}"`);
// If we're going to do the OIDC dance, we need to make sure these values
// are set. If they aren't, core.getIDToken() will fail and so will
// generating the credentials file.
@ -87,6 +91,7 @@ async function run(): Promise<void> {
oidcTokenRequestURL: oidcTokenRequestURL,
});
} else {
logDebug(`Using credentials JSON`);
client = new CredentialsJSONClient({
projectID: projectID,
credentialsJSON: credentialsJSON,
@ -98,6 +103,8 @@ async function run(): Promise<void> {
// fails, which means continue-on-error actions will still have the file
// available.
if (createCredentialsFile) {
logDebug(`Creating credentials file`);
// Note: We explicitly and intentionally export to GITHUB_WORKSPACE
// instead of RUNNER_TEMP, because RUNNER_TEMP is not shared with
// Docker-based actions on the filesystem. Exporting to GITHUB_WORKSPACE
@ -113,14 +120,21 @@ async function run(): Promise<void> {
throw new Error('$GITHUB_WORKSPACE is not set');
}
// Create credentials file.
const credentialsPath = await client.createCredentialsFile(githubWorkspace);
logInfo(`Created credentials file at "${credentialsPath}"`);
// Output to be available to future steps.
setOutput('credentials_file_path', credentialsPath);
// CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE is picked up by gcloud to use
// a specific credential file (subject to change and equivalent to auth/credential_file_override)
exportVariable('CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE', credentialsPath);
// GOOGLE_APPLICATION_CREDENTIALS is used by Application Default Credentials
// in all GCP client libraries
exportVariable('GOOGLE_APPLICATION_CREDENTIALS', credentialsPath);
// GOOGLE_GHA_CREDS_PATH is used by other Google GitHub Actions
exportVariable('GOOGLE_GHA_CREDS_PATH', credentialsPath);
}
@ -142,6 +156,8 @@ async function run(): Promise<void> {
break;
}
case 'access_token': {
logDebug(`Creating access token`);
const accessTokenLifetime = parseDuration(getInput('access_token_lifetime'));
const accessTokenScopes = explodeStrings(getInput('access_token_scopes'));
const accessTokenSubject = getInput('access_token_subject');
@ -152,6 +168,12 @@ async function run(): Promise<void> {
// Credentials endpoints.
let accessToken, expiration;
if (accessTokenSubject) {
logInfo(
`An access token subject was specified, triggering Domain-Wide ` +
`Delegation flow. This flow does not support specifying an ` +
`access token lifetime of greater than 1 hour.`,
);
const unsignedJWT = buildDomainWideDelegationJWT(
serviceAccount,
accessTokenSubject,
@ -176,6 +198,8 @@ async function run(): Promise<void> {
break;
}
case 'id_token': {
logDebug(`Creating id token`);
const idTokenAudience = getInput('id_token_audience', { required: true });
const idTokenIncludeEmail = getBooleanInput('id_token_include_email');
const serviceAccount = await client.getServiceAccount();
@ -196,7 +220,8 @@ async function run(): Promise<void> {
}
}
} catch (err) {
setFailed(`google-github-actions/auth failed with: ${err}`);
const msg = errorMessage(err);
setFailed(`google-github-actions/auth failed with: ${msg}`);
}
}

View File

@ -229,3 +229,27 @@ export function buildDomainWideDelegationJWT(
return JSON.stringify(body);
}
/**
* errorMessage extracts the error message from the given error.
*
* TODO(sethvargo): Candidate for centralization.
*
*/
export function errorMessage(err: unknown): string {
if (!err) {
return '';
}
let msg = err instanceof Error ? err.message : `${err}`;
msg = msg.trim();
msg = msg.replace('Error: ', '');
msg = msg.trim();
if (!msg) {
return '';
}
msg = msg[0].toLowerCase() + msg.slice(1);
return msg;
}