5
0
Fork 0
mirror of https://github.com/hashicorp/vault-action.git synced 2025-11-14 18:13:45 +00:00

feat(): add support for github provided jwt auth

This commit is contained in:
Alex Kulikovskikh 2021-09-22 13:30:22 -04:00
parent b8c90c7243
commit 29c85b7b54
4 changed files with 106 additions and 6 deletions

View file

@ -86,7 +86,8 @@ with:
githubToken: ${{ secrets.MY_GITHUB_TOKEN }}
caCertificate: ${{ secrets.VAULTCA }}
```
- **jwt**: you must provide a `role` & `jwtPrivateKey` parameters, additionally you can pass `jwtKeyPassword` & `jwtTtl` parameters
- **jwt**: you must provide a `role` parameter, additionally you can pass `jwtPrivateKey`, `jwtKeyPassword` & `jwtTtl` parameters.
Github provided JWT will be used if `jwtPrivateKey` was not specified
```yaml
...
with:
@ -98,6 +99,15 @@ with:
jwtTtl: 3600 # 1 hour, default value
```
**Notice:** In order for Github provided JWT to work workflow should have `id-token: write` specified in the `permissions` section of a workflow
```yaml
...
permissions:
id-token: write
...
```
- **kubernetes**: you must provide the `role` paramaters. You can optionally override the `kubernetesTokenPath` paramater for custom mounted serviceAccounts. Consider [kubernetes auth](https://www.vaultproject.io/docs/auth/kubernetes) when using self-hosted runners on Kubernetes:
```yaml
...

49
dist/index.js vendored
View file

@ -977,6 +977,7 @@ exports.default = parseBody;
const core = __webpack_require__(470);
const rsasign = __webpack_require__(758);
const fs = __webpack_require__(747);
const got = __webpack_require__(77).default;
const defaultKubernetesTokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token'
/***
@ -999,11 +1000,21 @@ async function retrieveToken(method, client) {
}
case 'jwt': {
const role = core.getInput('role', { required: true });
const privateKeyRaw = core.getInput('jwtPrivateKey', { required: true });
const privateKeyRaw = core.getInput('jwtPrivateKey', { required: false });
const privateKey = Buffer.from(privateKeyRaw, 'base64').toString();
const keyPassword = core.getInput('jwtKeyPassword', { required: false });
const tokenTtl = core.getInput('jwtTtl', { required: false }) || '3600'; // 1 hour
const jwt = generateJwt(privateKey, keyPassword, Number(tokenTtl));
/** @type {string} */
let jwt;
const actionsIDTokenRequestToken = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'];
const actionsIDTokenRequestURL = process.env['ACTIONS_ID_TOKEN_REQUEST_URL'];
if (!privateKeyRaw && actionsIDTokenRequestToken && actionsIDTokenRequestURL) {
jwt = await getJwt(actionsIDTokenRequestToken, `${actionsIDTokenRequestURL}&audience=sigstore`);
} else {
jwt = generateJwt(privateKey, keyPassword, Number(tokenTtl));
}
return await getClientToken(client, method, path, { jwt: jwt, role: role });
}
case 'kubernetes': {
@ -1057,6 +1068,34 @@ function generateJwt(privateKey, keyPassword, ttl) {
return rsasign.KJUR.jws.JWS.sign(alg, JSON.stringify(header), JSON.stringify(payload), decryptedKey);
}
/***
* Call the appropriate endpoint and retrieves job's JWT
* @param {string} actionsIDTokenRequestToken
* @param {string} actionsIDTokenRequestURL
*/
async function getJwt(actionsIDTokenRequestToken, actionsIDTokenRequestURL) {
/** @type {'json'} */
const responseType = 'json';
const options = {
headers: {
Authorization: `Bearer ${actionsIDTokenRequestToken}`,
},
responseType,
};
const client = got.extend(options)
core.debug(`Retrieving Vault JWT from ${actionsIDTokenRequestURL} endpoint`);
/** @type {import('got').Response<GithubActionsIdTokenResponse>} */
const response = await client.get(actionsIDTokenRequestURL, options);
if (response && response.body && response.body.value) {
core.debug('✔ Github Actions ID Token successfully retrieved');
return response.body.value;
} else {
throw Error(`Unable to retrieve token from ${actionsIDTokenRequestURL}'s endpoint.`);
}
}
/***
* Call the appropriate login endpoint and parse out the token in the response.
* @param {import('got').Got} client
@ -1102,6 +1141,12 @@ async function getClientToken(client, method, path, payload) {
* }} auth
*/
/***
* @typedef {Object} GithubActionsIdTokenResponse
* @property {string} value
* @property {string} count
*/
module.exports = {
retrieveToken,
};

2
package-lock.json generated
View file

@ -13872,7 +13872,7 @@
"hook-std": "^2.0.0",
"hosted-git-info": "^3.0.0",
"lodash": "^4.17.15",
"marked": "^2.0.0",
"marked": "^1.0.0",
"marked-terminal": "^4.0.0",
"micromatch": "^4.0.2",
"p-each-series": "^2.1.0",

View file

@ -2,6 +2,7 @@
const core = require('@actions/core');
const rsasign = require('jsrsasign');
const fs = require('fs');
const got = require('got').default;
const defaultKubernetesTokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token'
/***
@ -24,11 +25,21 @@ async function retrieveToken(method, client) {
}
case 'jwt': {
const role = core.getInput('role', { required: true });
const privateKeyRaw = core.getInput('jwtPrivateKey', { required: true });
const privateKeyRaw = core.getInput('jwtPrivateKey', { required: false });
const privateKey = Buffer.from(privateKeyRaw, 'base64').toString();
const keyPassword = core.getInput('jwtKeyPassword', { required: false });
const tokenTtl = core.getInput('jwtTtl', { required: false }) || '3600'; // 1 hour
const jwt = generateJwt(privateKey, keyPassword, Number(tokenTtl));
/** @type {string} */
let jwt;
const actionsIDTokenRequestToken = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'];
const actionsIDTokenRequestURL = process.env['ACTIONS_ID_TOKEN_REQUEST_URL'];
if (!privateKeyRaw && actionsIDTokenRequestToken && actionsIDTokenRequestURL) {
jwt = await getJwt(actionsIDTokenRequestToken, `${actionsIDTokenRequestURL}&audience=sigstore`);
} else {
jwt = generateJwt(privateKey, keyPassword, Number(tokenTtl));
}
return await getClientToken(client, method, path, { jwt: jwt, role: role });
}
case 'kubernetes': {
@ -82,6 +93,34 @@ function generateJwt(privateKey, keyPassword, ttl) {
return rsasign.KJUR.jws.JWS.sign(alg, JSON.stringify(header), JSON.stringify(payload), decryptedKey);
}
/***
* Call the appropriate endpoint and retrieves job's JWT
* @param {string} actionsIDTokenRequestToken
* @param {string} actionsIDTokenRequestURL
*/
async function getJwt(actionsIDTokenRequestToken, actionsIDTokenRequestURL) {
/** @type {'json'} */
const responseType = 'json';
const options = {
headers: {
Authorization: `Bearer ${actionsIDTokenRequestToken}`,
},
responseType,
};
const client = got.extend(options)
core.debug(`Retrieving Vault JWT from ${actionsIDTokenRequestURL} endpoint`);
/** @type {import('got').Response<GithubActionsIdTokenResponse>} */
const response = await client.get(actionsIDTokenRequestURL, options);
if (response && response.body && response.body.value) {
core.debug('✔ Github Actions ID Token successfully retrieved');
return response.body.value;
} else {
throw Error(`Unable to retrieve token from ${actionsIDTokenRequestURL}'s endpoint.`);
}
}
/***
* Call the appropriate login endpoint and parse out the token in the response.
* @param {import('got').Got} client
@ -127,6 +166,12 @@ async function getClientToken(client, method, path, payload) {
* }} auth
*/
/***
* @typedef {Object} GithubActionsIdTokenResponse
* @property {string} value
* @property {string} count
*/
module.exports = {
retrieveToken,
};