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:
parent
9e8c0bad27
commit
795c9eddca
5 changed files with 137 additions and 38 deletions
12
README.md
12
README.md
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
95
action.js
95
action.js
|
|
@ -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
|
||||||
|
|
|
||||||
29
action.yml
29
action.yml
|
|
@ -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
30
package-lock.json
generated
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue