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

Simplify secret request UX (#102)

* Simplify secret UX

* Update doc

* Fix tests
This commit is contained in:
Jason O'Donnell 2020-08-11 10:06:28 -04:00 committed by GitHub
parent 633de935e7
commit f7f0d5a289
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 220 deletions

View file

@ -141,21 +141,19 @@ jobs:
url: http://localhost:8200 url: http://localhost:8200
token: testtoken token: testtoken
secrets: | secrets: |
test secret ; secret/data/test secret ;
test secret | NAMED_SECRET ; secret/data/test secret | NAMED_SECRET ;
nested/test otherSecret ; secret/data/nested/test otherSecret ;
- name: Test Vault Action (default KV V1) - name: Test Vault Action (default KV V1)
uses: ./ uses: ./
with: with:
url: http://localhost:8200 url: http://localhost:8200
token: testtoken token: testtoken
path: my-secret
kv-version: 1
secrets: | secrets: |
test altSecret ; my-secret/test altSecret ;
test altSecret | NAMED_ALTSECRET ; my-secret/test altSecret | NAMED_ALTSECRET ;
nested/test otherAltSecret ; my-secret/nested/test otherAltSecret ;
- name: Test Vault Action (cubbyhole) - name: Test Vault Action (cubbyhole)
uses: ./ uses: ./
@ -217,9 +215,9 @@ jobs:
clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }}
clientKey: ${{ secrets.VAULT_CLIENT_KEY }} clientKey: ${{ secrets.VAULT_CLIENT_KEY }}
secrets: | secrets: |
test secret ; secret/data/test secret ;
test secret | NAMED_SECRET ; secret/data/test secret | NAMED_SECRET ;
nested/test otherSecret ; secret/data/nested/test otherSecret ;
- name: Test Vault Action (tlsSkipVerify) - name: Test Vault Action (tlsSkipVerify)
uses: ./ uses: ./
@ -230,22 +228,20 @@ jobs:
clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }}
clientKey: ${{ secrets.VAULT_CLIENT_KEY }} clientKey: ${{ secrets.VAULT_CLIENT_KEY }}
secrets: | secrets: |
tlsSkipVerify skip ; secret/data/tlsSkipVerify skip ;
- name: Test Vault Action (default KV V1) - name: Test Vault Action (default KV V1)
uses: ./ uses: ./
with: with:
url: https://localhost:8200 url: https://localhost:8200
token: ${{ env.VAULT_TOKEN }} token: ${{ env.VAULT_TOKEN }}
path: my-secret
kv-version: 1
caCertificate: ${{ secrets.VAULTCA }} caCertificate: ${{ secrets.VAULTCA }}
clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }}
clientKey: ${{ secrets.VAULT_CLIENT_KEY }} clientKey: ${{ secrets.VAULT_CLIENT_KEY }}
secrets: | secrets: |
test altSecret ; my-secret/test altSecret ;
test altSecret | NAMED_ALTSECRET ; my-secret/test altSecret | NAMED_ALTSECRET ;
nested/test otherAltSecret ; my-secret/nested/test otherAltSecret ;
- name: Test Vault Action (cubbyhole) - name: Test Vault Action (cubbyhole)
uses: ./ uses: ./

110
README.md
View file

@ -4,18 +4,10 @@
**Please note**: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault or this Vault Action, _please responsibly disclose_ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). **Please note**: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault or this Vault Action, _please responsibly disclose_ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com).
This repository was recently adopted by HashiCorp. We're actively working on adding
additional functionality to this action soon:
- [ ] TLS
- [ ] mTLS
- [ ] Simplify secret request UX
--- ---
A helper action for easily pulling secrets from HashiCorp Vault™. A helper action for easily pulling secrets from HashiCorp Vault™.
By default, this action pulls from [Version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2/) of the K/V Engine. See examples below for how to [use v1](#using-kv-version-1) as well as [other non-K/V engines](#other-secret-engines).
<!-- TOC --> <!-- TOC -->
- [Example Usage](#example-usage) - [Example Usage](#example-usage)
@ -25,8 +17,6 @@ By default, this action pulls from [Version 2](https://www.vaultproject.io/docs
- [Set Output Variable Name](#set-output-variable-name) - [Set Output Variable Name](#set-output-variable-name)
- [Multiple Secrets](#multiple-secrets) - [Multiple Secrets](#multiple-secrets)
- [Nested Secrets](#nested-secrets) - [Nested Secrets](#nested-secrets)
- [Using K/V version 1](#using-kv-version-1)
- [Custom K/V Engine Path](#custom-kv-engine-path)
- [Other Secret Engines](#other-secret-engines) - [Other Secret Engines](#other-secret-engines)
- [Adding Extra Headers](#adding-extra-headers) - [Adding Extra Headers](#adding-extra-headers)
- [Vault Enterprise Features](#vault-enterprise-features) - [Vault Enterprise Features](#vault-enterprise-features)
@ -51,9 +41,9 @@ jobs:
url: https://vault.mycompany.com:8200 url: https://vault.mycompany.com:8200
token: ${{ secrets.VaultToken }} token: ${{ secrets.VaultToken }}
secrets: | secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ; secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY ; secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ;
ci npm_token secret/data/ci npm_token
# ... # ...
``` ```
@ -100,11 +90,11 @@ Each secret request consists of the `path` and the `key` of the desired secret,
### Simple Key ### Simple Key
To retrieve a key `npmToken` from path `ci` that has value `somelongtoken` from vault you could do: To retrieve a key `npmToken` from path `secret/data/ci` that has value `somelongtoken` from vault you could do:
```yaml ```yaml
with: with:
secrets: ci npmToken secrets: secret/data/ci npmToken
``` ```
`vault-action` will automatically normalize the given secret selector key, and set the follow as environment variables for the following steps in the current job: `vault-action` will automatically normalize the given secret selector key, and set the follow as environment variables for the following steps in the current job:
@ -134,7 +124,7 @@ However, if you want to set it to a specific name, say `NPM_TOKEN`, you could do
```yaml ```yaml
with: with:
secrets: ci npmToken | NPM_TOKEN secrets: secret/data/ci npmToken | NPM_TOKEN
``` ```
With that, `vault-action` will now use your requested name and output: With that, `vault-action` will now use your requested name and output:
@ -161,65 +151,18 @@ This action can take multi-line input, so say you had your AWS keys stored in a
```yaml ```yaml
with: with:
secrets: | secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ; secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY
``` ```
### Nested Secrets
By default, `vault-action` will read the key from `data.data` in the response for the K/V v2 engine (default), or `data` for K/V v1 and other Secret Engines (see below for more info).
If you need to retrieve a sub-key from a JSON document, or are interested in some other component of the Vault response, you can specify a different key as the path to the desired out.
_**Important**_: You must specify an [Output Variable Name](#set-output-variable-name) when using this method.
```yaml
with:
secrets: |
my/path pair.key | NESTED_SECRET ;
```
Under the covers, we're using [JSONata](https://jsonata.org/) to provide the querying functionality. Any valid JSONata syntax is valid here, and will be outputted as a `JSON.stringify`-ied result.
### Using K/V version 1
By default, `vault-action` expects a K/V engine using [version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2.html).
In order to work with a [v1 engine](https://www.vaultproject.io/docs/secrets/kv/kv-v1/), the `kv-version` parameter may be passed:
```yaml
with:
kv-version: 1
```
## Custom K/V Engine Path
When you enable the K/V Engine, by default it's placed at the path `secret`, so a secret named `ci` will be accessed from `secret/ci`. However, [if you enabled the secrets engine using a custom `path`](https://www.vaultproject.io/docs/commands/secrets/enable/#inlinecode--path-4), you
can pass it as follows:
```yaml
with:
path: my-secrets
secrets: ci npmToken
```
This way, the `ci` secret in the example above will be retrieved from `my-secrets/ci`.
## Other Secret Engines ## Other Secret Engines
While this action primarily supports the K/V engine, it is possible to request secrets from other engines in Vault. Vault Action currently supports retrieving secrets from any engine where secrets
are retrieved via `GET` requests. This means secret engines such as PKI are currently
not supported due to their requirement of sending parameters along with the request
(such as `common_name`).
To do so when specifying the `Secret Path`, just append a leading forward slash (`/`) and specify the path as described in the Vault API documentation. For example, to request a secret from the `cubbyhole` secret engine:
For example, to retrieve a stored secret from the [`cubbyhole` engine](https://www.vaultproject.io/api-docs/secret/cubbyhole/), assuming you have a stored secret at the path `foo` with the contents:
```json
{
"foo": "bar",
"zip": "zap"
}
```
You could request the contents like so:
```yaml ```yaml
with: with:
@ -247,21 +190,6 @@ steps:
run: "my-cli --token '${{ steps.secrets.outputs.MY_KEY }}'" run: "my-cli --token '${{ steps.secrets.outputs.MY_KEY }}'"
``` ```
Secrets pulled from the same `Secret Path` are cached by default. So if you, for example, are using the `aws` engine and retrieve a key, only a single key for a given path is returned.
e.g.:
```yaml
with:
secrets: |
/aws/creds/ci access_key | AWS_ACCESS_KEY_ID ;
/aws/creds/ci secret_key | AWS_SECRET_ACCESS_KEY
```
would work fine.
*NOTE: Per [Nested Secrets](#nested-secrets), the `Key` is pulled from the `data` property of the response.*
## Adding Extra Headers ## Adding Extra Headers
If you ever need to add extra headers to the vault request, say if you need to authenticate with a firewall, all you need to do is set `extraHeaders`: If you ever need to add extra headers to the vault request, say if you need to authenticate with a firewall, all you need to do is set `extraHeaders`:
@ -269,8 +197,8 @@ If you ever need to add extra headers to the vault request, say if you need to a
```yaml ```yaml
with: with:
secrets: | secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ; secret/ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY secret/ci/aws secretKey | AWS_SECRET_ACCESS_KEY
extraHeaders: | extraHeaders: |
X-Secure-Id: ${{ secrets.SECURE_ID }} X-Secure-Id: ${{ secrets.SECURE_ID }}
X-Secure-Secret: ${{ secrets.SECURE_SECRET }} X-Secure-Secret: ${{ secrets.SECURE_SECRET }}
@ -295,9 +223,9 @@ steps:
token: ${{ secrets.VaultToken }} token: ${{ secrets.VaultToken }}
namespace: ns1 namespace: ns1
secrets: | secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ; secret/ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY ; secret/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ;
ci npm_token secret/ci npm_token
``` ```
## Reference ## Reference
@ -309,8 +237,6 @@ Here are all the inputs available through `with`:
| `url` | The URL for the vault endpoint | | ✔ | | `url` | The URL for the vault endpoint | | ✔ |
| `secrets` | A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details | | ✔ | | `secrets` | A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details | | ✔ |
| `namespace` | The Vault namespace from which to query secrets. Vault Enterprise only, unset by default | | | | `namespace` | The Vault namespace from which to query secrets. Vault Enterprise only, unset by default | | |
| `path` | The path of a non-default K/V engine | | |
| `kv-version` | The version of the K/V engine to use. | `2` | |
| `method` | The method to use to authenticate with Vault. | `token` | | | `method` | The method to use to authenticate with Vault. | `token` | |
| `token` | The Vault Token to be used to authenticate with Vault | | | | `token` | The Vault Token to be used to authenticate with Vault | | |
| `roleId` | The Role Id for App Role authentication | | | | `roleId` | The Role Id for App Role authentication | | |

View file

@ -10,13 +10,6 @@ inputs:
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: method:
description: 'The method to use to authenticate with Vault.' description: 'The method to use to authenticate with Vault.'
default: 'token' default: 'token'

40
dist/index.js vendored
View file

@ -10552,9 +10552,9 @@ async function getSecrets(secretRequests, client) {
const responseCache = new Map(); const responseCache = new Map();
const results = []; const results = [];
for (const secretRequest of secretRequests) { for (const secretRequest of secretRequests) {
const { path, selector } = secretRequest; let { path, selector } = secretRequest;
const requestPath = `v1${path}`; const requestPath = `v1/${path}`;
let body; let body;
let cachedResponse = false; let cachedResponse = false;
if (responseCache.has(requestPath)) { if (responseCache.has(requestPath)) {
@ -10566,7 +10566,13 @@ async function getSecrets(secretRequests, client) {
responseCache.set(requestPath, body); responseCache.set(requestPath, body);
} }
const value = selectData(JSON.parse(body), selector); selector = "data." + selector
body = JSON.parse(body)
if (body.data["data"] != undefined) {
selector = "data." + selector
}
const value = selectData(body, selector);
results.push({ results.push({
request: secretRequest, request: secretRequest,
value, value,
@ -14099,7 +14105,6 @@ const jsonata = __webpack_require__(350);
const { auth: { retrieveToken }, secrets: { getSecrets } } = __webpack_require__(676); const { auth: { retrieveToken }, secrets: { getSecrets } } = __webpack_require__(676);
const AUTH_METHODS = ['approle', 'token', 'github']; const AUTH_METHODS = ['approle', 'token', 'github'];
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 });
@ -14107,10 +14112,6 @@ async function exportSecrets() {
const extraHeaders = parseHeadersInput('extraHeaders', { required: false }); const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
const exportEnv = core.getInput('exportEnv', { required: false }) != 'false'; const exportEnv = core.getInput('exportEnv', { required: false }) != 'false';
let enginePath = core.getInput('path', { required: false });
/** @type {number | string} */
let kvVersion = core.getInput('kv-version', { required: false });
const secretsInput = core.getInput('secrets', { required: true }); const secretsInput = core.getInput('secrets', { required: true });
const secretRequests = parseSecretsInput(secretsInput); const secretRequests = parseSecretsInput(secretsInput);
@ -14158,32 +14159,9 @@ async function exportSecrets() {
defaultOptions.headers['X-Vault-Token'] = vaultToken; defaultOptions.headers['X-Vault-Token'] = vaultToken;
const client = got.extend(defaultOptions); const client = got.extend(defaultOptions);
if (!enginePath) {
enginePath = 'secret';
}
if (!kvVersion) {
kvVersion = 2;
}
kvVersion = +kvVersion;
if (Number.isNaN(kvVersion) || !VALID_KV_VERSION.includes(kvVersion)) {
throw Error(`You must provide a valid K/V version (${VALID_KV_VERSION.slice(1).join(', ')}). Input: "${kvVersion}"`);
}
const requests = secretRequests.map(request => { const requests = secretRequests.map(request => {
const { path, selector } = request; const { path, selector } = request;
if (path.startsWith('/')) {
return request; return request;
}
const kvPath = (kvVersion === 2)
? `/${enginePath}/data/${path}`
: `/${enginePath}/${path}`;
const kvSelector = (kvVersion === 2)
? `data.data.${selector}`
: `data.${selector}`;
return { ...request, path: kvPath, selector: kvSelector };
}); });
const results = await getSecrets(requests, client); const results = await getSecrets(requests, client);

View file

@ -42,9 +42,21 @@ describe('integration', () => {
} }
}); });
await got(`${vaultUrl}/v1/secret/data/foobar`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
},
json: {
data: {
fookv2: 'bar',
},
}
});
// Enable custom secret engine // Enable custom secret engine
try { try {
await got(`${vaultUrl}/v1/sys/mounts/my-secret`, { await got(`${vaultUrl}/v1/sys/mounts/secret-kv1`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': 'testtoken',
@ -62,7 +74,7 @@ describe('integration', () => {
} }
} }
await got(`${vaultUrl}/v1/my-secret/test`, { await got(`${vaultUrl}/v1/secret-kv1/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': 'testtoken',
@ -72,7 +84,17 @@ describe('integration', () => {
} }
}); });
await got(`${vaultUrl}/v1/my-secret/nested/test`, { await got(`${vaultUrl}/v1/secret-kv1/foobar`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
},
json: {
fookv1: 'bar',
}
});
await got(`${vaultUrl}/v1/secret-kv1/nested/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': 'testtoken',
@ -101,20 +123,8 @@ describe('integration', () => {
.mockReturnValueOnce(secrets); .mockReturnValueOnce(secrets);
} }
function mockEngineName(name) {
when(core.getInput)
.calledWith('path')
.mockReturnValueOnce(name);
}
function mockVersion(version) {
when(core.getInput)
.calledWith('kv-version')
.mockReturnValueOnce(version);
}
it('get simple secret', async () => { it('get simple secret', async () => {
mockInput('test secret'); mockInput('secret/data/test secret');
await exportSecrets(); await exportSecrets();
@ -122,7 +132,7 @@ describe('integration', () => {
}); });
it('re-map secret', async () => { it('re-map secret', async () => {
mockInput('test secret | TEST_KEY'); mockInput('secret/data/test secret | TEST_KEY');
await exportSecrets(); await exportSecrets();
@ -130,7 +140,7 @@ describe('integration', () => {
}); });
it('get nested secret', async () => { it('get nested secret', async () => {
mockInput('nested/test otherSecret'); mockInput('secret/data/nested/test otherSecret');
await exportSecrets(); await exportSecrets();
@ -139,9 +149,9 @@ describe('integration', () => {
it('get multiple secrets', async () => { it('get multiple secrets', async () => {
mockInput(` mockInput(`
test secret ; secret/data/test secret ;
test secret | NAMED_SECRET ; secret/data/test secret | NAMED_SECRET ;
nested/test otherSecret ;`); secret/data/nested/test otherSecret ;`);
await exportSecrets(); await exportSecrets();
@ -152,10 +162,16 @@ describe('integration', () => {
expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET'); expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET');
}); });
it('leading slash kvv2', async () => {
mockInput('/secret/data/foobar fookv2');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('FOOKV2', 'bar');
});
it('get secret from K/V v1', async () => { it('get secret from K/V v1', async () => {
mockInput('test secret'); mockInput('secret-kv1/test secret');
mockEngineName('my-secret');
mockVersion('1');
await exportSecrets(); await exportSecrets();
@ -163,15 +179,21 @@ describe('integration', () => {
}); });
it('get nested secret from K/V v1', async () => { it('get nested secret from K/V v1', async () => {
mockInput('nested/test otherSecret'); mockInput('secret-kv1/nested/test otherSecret');
mockEngineName('my-secret');
mockVersion('1');
await exportSecrets(); await exportSecrets();
expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERCUSTOMSECRET'); expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERCUSTOMSECRET');
}); });
it('leading slash kvv1', async () => {
mockInput('/secret-kv1/foobar fookv1');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('FOOKV1', 'bar');
});
describe('generic engines', () => { describe('generic engines', () => {
beforeAll(async () => { beforeAll(async () => {
await got(`${vaultUrl}/v1/cubbyhole/test`, { await got(`${vaultUrl}/v1/cubbyhole/test`, {

View file

@ -56,7 +56,7 @@ describe('integration', () => {
}); });
it('get simple secret', async () => { it('get simple secret', async () => {
mockInput('test secret'); mockInput('secret/data/test secret');
await exportSecrets(); await exportSecrets();
@ -64,7 +64,7 @@ describe('integration', () => {
}); });
it('re-map secret', async () => { it('re-map secret', async () => {
mockInput('test secret | TEST_KEY'); mockInput('secret/data/test secret | TEST_KEY');
await exportSecrets(); await exportSecrets();
@ -72,7 +72,7 @@ describe('integration', () => {
}); });
it('get nested secret', async () => { it('get nested secret', async () => {
mockInput('nested/test otherSecret'); mockInput('secret/data/nested/test otherSecret');
await exportSecrets(); await exportSecrets();
@ -81,9 +81,9 @@ describe('integration', () => {
it('get multiple secrets', async () => { it('get multiple secrets', async () => {
mockInput(` mockInput(`
test secret ; secret/data/test secret ;
test secret | NAMED_SECRET ; secret/data/test secret | NAMED_SECRET ;
nested/test otherSecret ;`); secret/data/nested/test otherSecret ;`);
await exportSecrets(); await exportSecrets();
@ -95,9 +95,7 @@ describe('integration', () => {
}); });
it('get secret from K/V v1', async () => { it('get secret from K/V v1', async () => {
mockInput('test secret'); mockInput('my-secret/test secret');
mockEngineName('my-secret');
mockVersion('1');
await exportSecrets(); await exportSecrets();
@ -105,9 +103,7 @@ describe('integration', () => {
}); });
it('get nested secret from K/V v1', async () => { it('get nested secret from K/V v1', async () => {
mockInput('nested/test otherSecret'); mockInput('my-secret/nested/test otherSecret');
mockEngineName('my-secret');
mockVersion('1');
await exportSecrets(); await exportSecrets();
@ -229,7 +225,7 @@ describe('authenticate with approle', () => {
}); });
it('authenticate with approle', async() => { it('authenticate with approle', async() => {
mockInput('test secret'); mockInput('secret/data/test secret');
await exportSecrets(); await exportSecrets();

View file

@ -6,7 +6,6 @@ const jsonata = require('jsonata');
const { auth: { retrieveToken }, secrets: { getSecrets } } = require('./index'); const { auth: { retrieveToken }, secrets: { getSecrets } } = require('./index');
const AUTH_METHODS = ['approle', 'token', 'github']; const AUTH_METHODS = ['approle', 'token', 'github'];
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 });
@ -14,10 +13,6 @@ async function exportSecrets() {
const extraHeaders = parseHeadersInput('extraHeaders', { required: false }); const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
const exportEnv = core.getInput('exportEnv', { required: false }) != 'false'; const exportEnv = core.getInput('exportEnv', { required: false }) != 'false';
let enginePath = core.getInput('path', { required: false });
/** @type {number | string} */
let kvVersion = core.getInput('kv-version', { required: false });
const secretsInput = core.getInput('secrets', { required: true }); const secretsInput = core.getInput('secrets', { required: true });
const secretRequests = parseSecretsInput(secretsInput); const secretRequests = parseSecretsInput(secretsInput);
@ -65,32 +60,9 @@ async function exportSecrets() {
defaultOptions.headers['X-Vault-Token'] = vaultToken; defaultOptions.headers['X-Vault-Token'] = vaultToken;
const client = got.extend(defaultOptions); const client = got.extend(defaultOptions);
if (!enginePath) {
enginePath = 'secret';
}
if (!kvVersion) {
kvVersion = 2;
}
kvVersion = +kvVersion;
if (Number.isNaN(kvVersion) || !VALID_KV_VERSION.includes(kvVersion)) {
throw Error(`You must provide a valid K/V version (${VALID_KV_VERSION.slice(1).join(', ')}). Input: "${kvVersion}"`);
}
const requests = secretRequests.map(request => { const requests = secretRequests.map(request => {
const { path, selector } = request; const { path, selector } = request;
if (path.startsWith('/')) {
return request; return request;
}
const kvPath = (kvVersion === 2)
? `/${enginePath}/data/${path}`
: `/${enginePath}/${path}`;
const kvSelector = (kvVersion === 2)
? `data.data.${selector}`
: `data.${selector}`;
return { ...request, path: kvPath, selector: kvSelector };
}); });
const results = await getSecrets(requests, client); const results = await getSecrets(requests, client);

View file

@ -25,9 +25,9 @@ async function getSecrets(secretRequests, client) {
const responseCache = new Map(); const responseCache = new Map();
const results = []; const results = [];
for (const secretRequest of secretRequests) { for (const secretRequest of secretRequests) {
const { path, selector } = secretRequest; let { path, selector } = secretRequest;
const requestPath = `v1${path}`; const requestPath = `v1/${path}`;
let body; let body;
let cachedResponse = false; let cachedResponse = false;
if (responseCache.has(requestPath)) { if (responseCache.has(requestPath)) {
@ -39,7 +39,13 @@ async function getSecrets(secretRequests, client) {
responseCache.set(requestPath, body); responseCache.set(requestPath, body);
} }
const value = selectData(JSON.parse(body), selector); selector = "data." + selector
body = JSON.parse(body)
if (body.data["data"] != undefined) {
selector = "data." + selector
}
const value = selectData(body, selector);
results.push({ results.push({
request: secretRequest, request: secretRequest,
value, value,