5
0
Fork 0
mirror of https://github.com/hashicorp/vault-action.git synced 2025-11-07 15:16:56 +00:00

feat: add github auth and cleanup docs a little

Adds the GitHub Authentication method
This commit is contained in:
Richard Simpson 2020-03-31 11:54:47 -05:00
parent 9e8c0bad27
commit 795c9eddca
5 changed files with 137 additions and 38 deletions

View file

@ -46,14 +46,14 @@ jobs:
While most workflows will likely use a vault token, you can also use an `approle` to authenticate with vaule. You can configure which by using the `method` parameter: While most workflows will likely use a vault token, you can also use an `approle` to authenticate with vaule. You can configure which by using the `method` parameter:
- **token**: (by default) you must provide a token parameter - **token**: (by default) you must provide a `token` parameter
```yaml ```yaml
... ...
with: with:
url: https://vault.mycompany.com:8200 url: https://vault.mycompany.com:8200
token: ${{ secrets.VaultToken }} token: ${{ secrets.VaultToken }}
``` ```
- **approle**: you must provide a roleId & secretId parameter - **approle**: you must provide a `roleId` & `secretId` parameter
```yaml ```yaml
... ...
with: with:
@ -62,6 +62,14 @@ with:
roleId: ${{ secrets.roleId }} roleId: ${{ secrets.roleId }}
secretId: ${{ secrets.secretId }} secretId: ${{ secrets.secretId }}
``` ```
- **github**: you must provide the github token as `githubToken`
```yaml
...
with:
url: https://vault.mycompany.com:8200
method: github
githubToken: ${{ secrets.GITHUB_TOKEN }}
```
## Key Syntax ## Key Syntax

View file

@ -6,13 +6,14 @@ const core = require('@actions/core');
const command = require('@actions/core/lib/command'); const command = require('@actions/core/lib/command');
const got = require('got'); const got = require('got');
const AUTH_METHODS = ['approle', 'token']; const AUTH_METHODS = ['approle', 'token', 'github'];
const VALID_KV_VERSION = [-1, 1, 2]; const VALID_KV_VERSION = [-1, 1, 2];
async function exportSecrets() { async function exportSecrets() {
const vaultUrl = core.getInput('url', { required: true }); const vaultUrl = core.getInput('url', { required: true });
const vaultNamespace = core.getInput('namespace', { required: false }); const vaultNamespace = core.getInput('namespace', { required: false });
const extraHeaders = parseHeadersInput('extraHeaders', { required: false }); const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
const exportEnv = core.getInput('exportEnv', { required: false }) != 'false';
let enginePath = core.getInput('path', { required: false }); let enginePath = core.getInput('path', { required: false });
let kvVersion = core.getInput('kv-version', { required: false }); let kvVersion = core.getInput('kv-version', { required: false });
@ -20,40 +21,27 @@ async function exportSecrets() {
const secretsInput = core.getInput('secrets', { required: true }); const secretsInput = core.getInput('secrets', { required: true });
const secretRequests = parseSecretsInput(secretsInput); const secretRequests = parseSecretsInput(secretsInput);
const vaultMethod = core.getInput('method', { required: false }) || 'token'; const vaultMethod = (core.getInput('method', { required: false }) || 'token').toLowerCase();
if (!AUTH_METHODS.includes(vaultMethod)) { if (!AUTH_METHODS.includes(vaultMethod)) {
throw Error(`Sorry, the authentication method ${vaultMethod} is not currently supported.`); throw Error(`Sorry, the authentication method ${vaultMethod} is not currently supported.`);
} }
let vaultToken = null; const defaultOptions = {
switch (vaultMethod) { baseUrl: vaultUrl,
case 'approle': throwHttpErrors: true,
const vaultRoleId = core.getInput('roleId', { required: true }); headers: {}
const vaultSecretId = core.getInput('secretId', { required: true }); }
core.debug('Try to retrieve Vault Token from approle');
var options = { for (const [headerName, headerValue] of extraHeaders) {
headers: {}, defaultOptions.headers[headerName] = headerValue;
json: { role_id: vaultRoleId, secret_id: vaultSecretId }, }
responseType: 'json'
};
if (vaultNamespace != null) { if (vaultNamespace != null) {
options.headers["X-Vault-Namespace"] = vaultNamespace; defaultOptions.headers["X-Vault-Namespace"] = vaultNamespace;
} }
/** @type {any} */ const client = got.extend(defaultOptions);
const result = await got.post(`${vaultUrl}/v1/auth/approle/login`, options); const vaultToken = await retrieveToken(vaultMethod, client);
if (result && result.body && result.body.auth && result.body.auth.client_token) {
vaultToken = result.body.auth.client_token;
core.debug('✔ Vault Token has retrieved from approle');
} else {
throw Error(`No token was retrieved with the role_id and secret_id provided.`);
}
break;
default:
vaultToken = core.getInput('token', { required: true });
break;
}
if (!enginePath) { if (!enginePath) {
enginePath = 'secret'; enginePath = 'secret';
@ -110,9 +98,11 @@ async function exportSecrets() {
const secretData = getResponseData(body, dataDepth); const secretData = getResponseData(body, dataDepth);
const value = selectData(secretData, secretSelector, isJSONPath); const value = selectData(secretData, secretSelector, isJSONPath);
command.issue('add-mask', value); command.issue('add-mask', value);
if (exportEnv) {
core.exportVariable(envVarName, `${value}`); core.exportVariable(envVarName, `${value}`);
}
core.setOutput(outputVarName, `${value}`); core.setOutput(outputVarName, `${value}`);
core.debug(`${secretPath} => outputs.${outputVarName} | env.${envVarName}`); core.debug(`${secretPath} => outputs.${outputVarName}${exportEnv ? ` | env.${envVarName}` : ''}`);
} }
}; };
@ -185,6 +175,55 @@ function parseSecretsInput(secretsInput) {
return output; return output;
} }
/***
* Authentication with Vault and retrieve a vault token
* @param {string} method
* @param {import('got')} client
*/
async function retrieveToken(method, client) {
switch (method) {
case 'approle': {
const vaultRoleId = core.getInput('roleId', { required: true });
const vaultSecretId = core.getInput('secretId', { required: true });
core.debug('Try to retrieve Vault Token from approle');
/** @type {any} */
var options = {
json: { role_id: vaultRoleId, secret_id: vaultSecretId },
responseType: 'json'
};
const result = await client.post(`/v1/auth/approle/login`, options);
if (result && result.body && result.body.auth && result.body.auth.client_token) {
core.debug('✔ Vault Token has retrieved from approle');
return result.body.auth.client_token;
} else {
throw Error(`No token was retrieved with the role_id and secret_id provided.`);
}
}
case 'github': {
const githubToken = core.getInput('githubToken', { required: true });
core.debug('Try to retrieve Vault Token from approle');
/** @type {any} */
var options = {
json: { token: githubToken },
responseType: 'json'
};
const result = await client.post(`/v1/auth/github/login`, options);
if (result && result.body && result.body.auth && result.body.auth.client_token) {
core.debug('✔ Vault Token has retrieved from approle');
return result.body.auth.client_token;
} else {
throw Error(`No token was retrieved with the role_id and secret_id provided.`);
}
}
default:
return core.getInput('token', { required: true });
}
}
/** /**
* Parses a JSON response and returns the secret data * Parses a JSON response and returns the secret data
* @param {string} responseBody * @param {string} responseBody

View file

@ -4,17 +4,38 @@ inputs:
url: url:
description: 'The URL for the vault endpoint' description: 'The URL for the vault endpoint'
required: true required: true
token:
description: 'The Vault Token to be used to authenticate with Vault'
required: true
secrets: secrets:
description: 'A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details' description: 'A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details'
required: true required: true
namespace: namespace:
description: 'The Vault namespace from which to query secrets. Vault Enterprise only, unset by default' description: 'The Vault namespace from which to query secrets. Vault Enterprise only, unset by default'
required: false required: false
path:
description: 'The path of a non-default K/V engine'
required: false
kv-version:
description: 'The version of the K/V engine to use. Default: 2'
required: false
method:
description: 'The method to use to authenticate with Vault. Default: token'
required: false
token:
description: 'The Vault Token to be used to authenticate with Vault'
required: false
roleId:
description: 'The Role Id for App Role authentication'
required: false
secretId:
description: 'The Secret Id for App Role authentication'
required: false
githubToken:
description: 'The Github Token to be used to authenticate with Vault'
required: false
extraHeaders: extraHeaders:
description: 'A string of newline seperated extra headers to include on every request.' description: 'A string of newline separated extra headers to include on every request.'
required: false
exportEnv:
description: 'Whether or not export secrets as environment variables. Default: true'
required: false required: false
runs: runs:
using: 'node12' using: 'node12'

30
package-lock.json generated
View file

@ -1929,6 +1929,30 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/got": {
"version": "9.6.9",
"resolved": "https://registry.npmjs.org/@types/got/-/got-9.6.9.tgz",
"integrity": "sha512-w+ZE+Ovp6fM+1sHwJB7RN3f3pTJHZkyABuULqbtknqezQyWadFEp5BzOXaZzRqAw2md6/d3ybxQJt+BNgpvzOg==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/tough-cookie": "*",
"form-data": "^2.5.0"
},
"dependencies": {
"form-data": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
}
}
},
"@types/http-cache-semantics": { "@types/http-cache-semantics": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
@ -2141,6 +2165,12 @@
"integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==",
"dev": true "dev": true
}, },
"@types/tough-cookie": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.6.tgz",
"integrity": "sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ==",
"dev": true
},
"@types/yargs": { "@types/yargs": {
"version": "13.0.2", "version": "13.0.2",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz",

View file

@ -42,6 +42,7 @@
"got": "^10.2.2" "got": "^10.2.2"
}, },
"devDependencies": { "devDependencies": {
"@types/got": "^9.6.9",
"@types/jest": "^25.1.3", "@types/jest": "^25.1.3",
"@zeit/ncc": "^0.22.0", "@zeit/ncc": "^0.22.0",
"jest": "^25.1.0", "jest": "^25.1.0",