Compare commits

..

4 Commits

Author SHA1 Message Date
Antoine Méausoone 3747195c5f feat(namespace): handle request on vault namespace (#5)
* feat(namespace): handle request on vault namespace
2019-11-24 15:21:11 -06:00
Richard Simpson 0104a02854 doc: fix old docs 2019-10-16 10:23:36 -05:00
Richard Simpson c81d6f1774 doc: add small clarification comment 2019-10-16 10:17:12 -05:00
Richard Simpson da9a93f3f5 fix: package action (#3)
Package action output per github docs.
2019-10-15 14:00:01 -05:00
11 changed files with 4974 additions and 10 deletions
+37
View File
@@ -21,6 +21,8 @@ jobs:
node-version: 10.x node-version: 10.x
- name: npm install - name: npm install
run: npm ci run: npm ci
- name: npm build
run: npm run build
- name: npm run test - name: npm run test
run: npm run test run: npm run test
env: env:
@@ -32,6 +34,39 @@ jobs:
VAULT_PORT: ${{ job.services.vault.ports[8200] }} VAULT_PORT: ${{ job.services.vault.ports[8200] }}
CI: true CI: true
test-ent:
runs-on: ubuntu-latest
services:
vault:
image: hashicorp/vault-enterprise:1.3.0_ent
ports:
- 8200/tcp
env:
VAULT_DEV_ROOT_TOKEN_ID: testtoken
options: --cap-add=IPC_LOCK
steps:
- uses: actions/checkout@v1
- name: Use Node.js 10.x
uses: actions/setup-node@v1
with:
node-version: 10.x
- name: npm install
run: npm ci
- name: npm build
run: npm run build
- name: npm run test
run: npm run test
env:
CI: true
- name: npm run test:integration-ent
run: npm run test:integration-ent
env:
VAULT_HOST: localhost
VAULT_PORT: ${{ job.services.vault.ports[8200] }}
CI: true
e2e: e2e:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -52,6 +87,8 @@ jobs:
node-version: 10.x node-version: 10.x
- name: npm install - name: npm install
run: npm ci run: npm ci
- name: npm build
run: npm run build
- name: setup vault - name: setup vault
run: node ./e2e/setup.js run: node ./e2e/setup.js
env: env:
+21 -2
View File
@@ -1,6 +1,6 @@
# vault-action # vault-action
A helper action for easily pulling secrets from the v2 K/V backend of vault. A helper action for easily pulling secrets from the default v2 K/V backend of vault.
## Example Usage ## Example Usage
@@ -68,11 +68,30 @@ This action can take multi-line input, so say you had your AWS keys stored in a
```yaml ```yaml
with: with:
keys: | secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ; ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY ci/aws secretKey | AWS_SECRET_ACCESS_KEY
``` ```
### Namespace
This action could be use with namespace Vault Enterprise feature. You can specify namespace in request :
```yaml
steps:
# ...
- name: Import Secrets
uses: RichiCoder1/vault-action
with:
url: https://vault-enterprise.mycompany.com:8200
token: ${{ secrets.VaultToken }}
namespace: ns1
secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY ;
ci npm_token
```
## Masking ## Masking
This action uses Github Action's built in masking, so all variables will automatically be masked if printed to the console or to logs. This action uses Github Action's built in masking, so all variables will automatically be masked if printed to the console or to logs.
+10 -4
View File
@@ -5,17 +5,23 @@ const got = require('got');
async function exportSecrets() { async function exportSecrets() {
const vaultUrl = core.getInput('url', { required: true }); const vaultUrl = core.getInput('url', { required: true });
const vaultToken = core.getInput('token', { required: true }); const vaultToken = core.getInput('token', { required: true });
const vaultNamespace = core.getInput('namespace', { required: false });
const secretsInput = core.getInput('secrets', { required: true }); const secretsInput = core.getInput('secrets', { required: true });
const secrets = parseSecretsInput(secretsInput); const secrets = parseSecretsInput(secretsInput);
for (const secret of secrets) { for (const secret of secrets) {
const { secretPath, outputName, secretKey } = secret; const { secretPath, outputName, secretKey } = secret;
const result = await got(`${vaultUrl}/v1/secret/data/${secretPath}`, { const requestOptions = {
headers: { headers: {
'X-Vault-Token': vaultToken 'X-Vault-Token': vaultToken
} }};
});
if (vaultNamespace != null){
requestOptions.headers["X-Vault-Namespace"] = vaultNamespace
}
const result = await got(`${vaultUrl}/v1/secret/data/${secretPath}`, requestOptions);
const parsedResponse = JSON.parse(result.body); const parsedResponse = JSON.parse(result.body);
const vaultKeyData = parsedResponse.data; const vaultKeyData = parsedResponse.data;
@@ -91,4 +97,4 @@ module.exports = {
exportSecrets, exportSecrets,
parseSecretsInput, parseSecretsInput,
normalizeOutputKey normalizeOutputKey
}; };
+5 -2
View File
@@ -10,9 +10,12 @@ inputs:
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:
description: 'The Vault namespace from which to query secrets. Vault Enterprise only, unset by default'
required: false
runs: runs:
using: 'node12' using: 'node12'
main: 'index.js' main: 'dist/index.js'
branding: branding:
icon: 'unlock' icon: 'unlock'
color: 'gray-dark' color: 'gray-dark'
+4744
View File
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
# Start vault server locally
# You can run integration tests against server by running
# `VAULT_HOST=localhost VAULT_PORT=8200 CI=true npm run test:integration-ent`
version: "3.0"
services:
vault:
image: hashicorp/vault-enterprise:1.3.0_ent
environment:
VAULT_DEV_ROOT_TOKEN_ID: testtoken
ports:
- 8200:8200
privileged: true
+131
View File
@@ -0,0 +1,131 @@
jest.mock('@actions/core');
jest.mock('@actions/core/lib/command');
const core = require('@actions/core');
const got = require('got');
const { when } = require('jest-when');
const { exportSecrets } = require('../action');
describe('integration', () => {
beforeAll(async () => {
// Verify Connection
await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/secret/config`, {
headers: {
'X-Vault-Token': 'testtoken',
},
});
// Create namespace
await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/sys/namespaces/ns1`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
},
json: true,
});
// Enable secret engine
await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/sys/mounts/secret`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns1',
},
body: {"path":"secret","type":"kv","config":{},"options":{"version":2},"generate_signing_key":true},
json: true,
});
await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/secret/data/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns1',
},
body: {
data: {
secret: "SUPERSECRET_IN_NAMESPACE",
},
},
json: true,
});
await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/secret/data/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns1',
},
body: {
data: {
otherSecret: "OTHERSUPERSECRET_IN_NAMESPACE",
},
},
json: true,
});
})
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('url')
.mockReturnValue(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`);
when(core.getInput)
.calledWith('token')
.mockReturnValue('testtoken');
when(core.getInput)
.calledWith('namespace')
.mockReturnValue('ns1');
});
function mockInput(secrets) {
when(core.getInput)
.calledWith('secrets')
.mockReturnValue(secrets);
}
it('get simple secret', async () => {
mockInput('test secret')
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_IN_NAMESPACE');
});
it('re-map secret', async () => {
mockInput('test secret | TEST_KEY')
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('TEST_KEY', 'SUPERSECRET_IN_NAMESPACE');
});
it('get nested secret', async () => {
mockInput('nested/test otherSecret')
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET_IN_NAMESPACE');
});
it('get multiple secrets', async () => {
mockInput(`
test secret ;
test secret | NAMED_SECRET ;
nested/test otherSecret ;`);
await exportSecrets();
expect(core.exportVariable).toBeCalledTimes(3);
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_IN_NAMESPACE');
expect(core.exportVariable).toBeCalledWith('NAMED_SECRET', 'SUPERSECRET_IN_NAMESPACE');
expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET_IN_NAMESPACE');
});
});
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
verbose: true
};
+1 -1
View File
@@ -1,3 +1,3 @@
module.exports = { module.exports = {
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/integration/', '<rootDir>/e2e/'], testPathIgnorePatterns: ['/node_modules/', '<rootDir>/integration/', '<rootDir>/e2e/','<rootDir>/integration-ent'],
}; };
+6
View File
@@ -965,6 +965,12 @@
"integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==",
"dev": true "dev": true
}, },
"@zeit/ncc": {
"version": "0.20.5",
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.20.5.tgz",
"integrity": "sha512-XU6uzwvv95DqxciQx+aOLhbyBx/13ky+RK1y88Age9Du3BlA4mMPCy13BGjayOrrumOzlq1XV3SD/BWiZENXlw==",
"dev": true
},
"JSONStream": { "JSONStream": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+4 -1
View File
@@ -2,10 +2,12 @@
"name": "vault-action", "name": "vault-action",
"version": "0.1.0", "version": "0.1.0",
"description": "A Github Action that allows you to consume vault secrets as secure environment variables.", "description": "A Github Action that allows you to consume vault secrets as secure environment variables.",
"main": "index.js", "main": "dist/index.js",
"scripts": { "scripts": {
"build": "ncc build index.js -o dist",
"test": "jest", "test": "jest",
"test:integration": "jest -c integration/jest.config.js", "test:integration": "jest -c integration/jest.config.js",
"test:integration-ent": "jest -c integration-ent/jest.config.js",
"test:e2e": "jest -c e2e/jest.config.js" "test:e2e": "jest -c e2e/jest.config.js"
}, },
"release": { "release": {
@@ -41,6 +43,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^24.0.18", "@types/jest": "^24.0.18",
"@zeit/ncc": "^0.20.5",
"jest": "^24.9.0", "jest": "^24.9.0",
"jest-when": "^2.7.0", "jest-when": "^2.7.0",
"semantic-release": "^15.13.24" "semantic-release": "^15.13.24"