Skip to content

Commit

Permalink
feat(namespace): handle request on vault namespace (#5)
Browse files Browse the repository at this point in the history
* feat(namespace): handle request on vault namespace
  • Loading branch information
Ameausoone authored and RichiCoder1 committed Nov 24, 2019
1 parent 0104a02 commit 3747195
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 6 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,39 @@ jobs:
VAULT_PORT: ${{ job.services.vault.ports[8200] }}
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:
runs-on: ubuntu-latest

Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,25 @@ with:
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

This action uses Github Action's built in masking, so all variables will automatically be masked if printed to the console or to logs.
14 changes: 10 additions & 4 deletions action.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ const got = require('got');
async function exportSecrets() {
const vaultUrl = core.getInput('url', { required: true });
const vaultToken = core.getInput('token', { required: true });
const vaultNamespace = core.getInput('namespace', { required: false });

const secretsInput = core.getInput('secrets', { required: true });
const secrets = parseSecretsInput(secretsInput);

for (const secret of secrets) {
const { secretPath, outputName, secretKey } = secret;
const result = await got(`${vaultUrl}/v1/secret/data/${secretPath}`, {
const requestOptions = {
headers: {
'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 vaultKeyData = parsedResponse.data;
Expand Down Expand Up @@ -91,4 +97,4 @@ module.exports = {
exportSecrets,
parseSecretsInput,
normalizeOutputKey
};
};
5 changes: 4 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ inputs:
secrets:
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
namespace:
description: 'The Vault namespace from which to query secrets. Vault Enterprise only, unset by default'
required: false
runs:
using: 'node12'
main: 'dist/index.js'
branding:
icon: 'unlock'
color: 'gray-dark'
color: 'gray-dark'
12 changes: 12 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 131 additions & 0 deletions integration-ent/integration.test.js
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions integration-ent/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
verbose: true
};
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/integration/', '<rootDir>/e2e/'],
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/integration/', '<rootDir>/e2e/','<rootDir>/integration-ent'],
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"build": "ncc build index.js -o dist",
"test": "jest",
"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"
},
"release": {
Expand Down

0 comments on commit 3747195

Please sign in to comment.