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:
parent
b8c90c7243
commit
29c85b7b54
4 changed files with 106 additions and 6 deletions
12
README.md
12
README.md
|
|
@ -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
49
dist/index.js
vendored
|
|
@ -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
2
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
49
src/auth.js
49
src/auth.js
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue