Emit a better error when OIDC information is missing (#81)

This commit is contained in:
Seth Vargo 2021-12-09 12:52:57 -05:00 committed by GitHub
parent 1618f1c032
commit ccc7806970
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 25 deletions

2
dist/main/index.js vendored

File diff suppressed because one or more lines are too long

View File

@ -25,6 +25,9 @@ interface WorkloadIdentityClientOptions {
serviceAccount: string;
token: string;
audience: string;
oidcTokenRequestURL: string;
oidcTokenRequestToken: string;
}
/**
@ -38,12 +41,18 @@ export class WorkloadIdentityClient implements AuthClient {
readonly #token: string;
readonly #audience: string;
readonly #oidcTokenRequestURL: string;
readonly #oidcTokenRequestToken: string;
constructor(opts: WorkloadIdentityClientOptions) {
this.#providerID = opts.providerID;
this.#serviceAccount = opts.serviceAccount;
this.#token = opts.token;
this.#audience = opts.audience;
this.#oidcTokenRequestURL = opts.oidcTokenRequestURL;
this.#oidcTokenRequestToken = opts.oidcTokenRequestToken;
this.#projectID =
opts.projectID || this.extractProjectIDFromServiceAccountEmail(this.#serviceAccount);
}
@ -173,21 +182,7 @@ export class WorkloadIdentityClient implements AuthClient {
* set as GOOGLE_APPLICATION_CREDENTIALS for gcloud and client libraries.
*/
async createCredentialsFile(outputDir: string): Promise<string> {
// Extract the request token and request URL from the environment. These
// are only set when an id-token is requested and the submitter has
// collaborator permissions.
const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
const requestURLRaw = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
if (!requestToken || !requestURLRaw) {
throw new Error(
'GitHub Actions did not inject $ACTIONS_ID_TOKEN_REQUEST_TOKEN or ' +
'$ACTIONS_ID_TOKEN_REQUEST_URL into this job. This most likely ' +
'means the GitHub Actions workflow permissions are incorrect, or ' +
'this job is being run from a fork. For more information, please ' +
'see the GitHub documentation at https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token',
);
}
const requestURL = new URL(requestURLRaw);
const requestURL = new URL(this.#oidcTokenRequestURL);
// Append the audience value to the request.
const params = requestURL.searchParams;
@ -204,7 +199,7 @@ export class WorkloadIdentityClient implements AuthClient {
credential_source: {
url: requestURL,
headers: {
Authorization: `Bearer ${requestToken}`,
Authorization: `Bearer ${this.#oidcTokenRequestToken}`,
},
format: {
type: 'json',

View File

@ -16,9 +16,15 @@ import { BaseClient } from './base';
import { buildDomainWideDelegationJWT, explodeStrings, parseDuration } from './utils';
const secretsWarning =
'If you are specifying input values via GitHub secrets, ensure the secret ' +
'is being injected into the environment. By default, secrets are not passed ' +
'to workflows triggered from forks, including Dependabot.';
`If you are specifying input values via GitHub secrets, ensure the secret ` +
`is being injected into the environment. By default, secrets are not ` +
`passed to workflows triggered from forks, including Dependabot.`;
const oidcWarning =
`GitHub Actions did not inject $ACTIONS_ID_TOKEN_REQUEST_TOKEN or ` +
`$ACTIONS_ID_TOKEN_REQUEST_URL into this job. This most likely means the ` +
`GitHub Actions workflow permissions are incorrect, or this job is being ` +
`run from a fork. For more information, please see https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token`;
/**
* Executes the main action, documented inline.
@ -61,6 +67,15 @@ async function run(): Promise<void> {
// Instantiate the correct client based on the provided input parameters.
let client: AuthClient;
if (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.
const oidcTokenRequestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
const oidcTokenRequestURL = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
if (!oidcTokenRequestToken || !oidcTokenRequestURL) {
throw new Error(oidcWarning);
}
const token = await getIDToken(audience);
client = new WorkloadIdentityClient({
projectID: projectID,
@ -68,6 +83,8 @@ async function run(): Promise<void> {
serviceAccount: serviceAccount,
token: token,
audience: audience,
oidcTokenRequestToken: oidcTokenRequestToken,
oidcTokenRequestURL: oidcTokenRequestURL,
});
} else {
client = new CredentialsJSONClient({

View File

@ -15,6 +15,8 @@ describe('WorkloadIdentityClient', () => {
token: 'my-token',
serviceAccount: 'my-service@my-project.iam.gserviceaccount.com',
audience: 'my-aud',
oidcTokenRequestURL: 'https://example.com/',
oidcTokenRequestToken: 'token',
});
const result = await client.getProjectID();
@ -28,6 +30,8 @@ describe('WorkloadIdentityClient', () => {
token: 'my-token',
serviceAccount: 'my-service@my-project.iam.gserviceaccount.com',
audience: 'my-aud',
oidcTokenRequestURL: 'https://example.com/',
oidcTokenRequestToken: 'token',
});
const result = await client.getProjectID();
@ -41,6 +45,8 @@ describe('WorkloadIdentityClient', () => {
token: 'my-token',
serviceAccount: 'my-service@developers.google.com',
audience: 'my-aud',
oidcTokenRequestURL: 'https://example.com/',
oidcTokenRequestToken: 'token',
});
};
return expect(fn).to.throw(Error);
@ -55,6 +61,8 @@ describe('WorkloadIdentityClient', () => {
serviceAccount: 'my-service@my-project.iam.gserviceaccount.com',
token: 'my-token',
audience: 'my-aud',
oidcTokenRequestURL: 'https://example.com/',
oidcTokenRequestToken: 'token',
});
const result = await client.getServiceAccount();
expect(result).to.eq('my-service@my-project.iam.gserviceaccount.com');
@ -63,9 +71,6 @@ describe('WorkloadIdentityClient', () => {
describe('#createCredentialsFile', () => {
it('writes the file', async () => {
process.env.ACTIONS_ID_TOKEN_REQUEST_URL = 'https://actions-token.url';
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'github-token';
const tmp = tmpdir();
const client = new WorkloadIdentityClient({
projectID: 'my-project',
@ -73,6 +78,8 @@ describe('WorkloadIdentityClient', () => {
serviceAccount: 'my-service@my-project.iam.gserviceaccount.com',
token: 'my-token',
audience: 'my-aud',
oidcTokenRequestURL: 'https://example.com/',
oidcTokenRequestToken: 'token',
});
const exp = {
@ -83,9 +90,9 @@ describe('WorkloadIdentityClient', () => {
type: 'json',
},
headers: {
Authorization: 'Bearer github-token',
Authorization: 'Bearer token',
},
url: 'https://actions-token.url/?audience=my-aud',
url: 'https://example.com/?audience=my-aud',
},
service_account_impersonation_url:
'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/my-service@my-project.iam.gserviceaccount.com:generateAccessToken',