feat(authenticate): add approle auth method (#10)

* feat(authenticate): add approle auth method

* docs(readme): update readme

* fix: update index.js

* fix: update got to 10.2.2 to fix ncc

* chore: clean up code slightly

* chore: update tests to use got correctly

* chore(test): fix integration tests

* chore: streamline method logic

* chore: make role and secret required in approle

Co-authored-by: Sébastien FAUVART <sebastien.fauvart@gmail.com>
Co-authored-by: Richard Simpson <richardsimpson@outlook.com>
This commit is contained in:
Antoine Méausoone 2020-01-29 02:10:19 +01:00 committed by GitHub
parent 7a9634236c
commit 3b9239de79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 2981 additions and 2159 deletions

View file

@ -23,12 +23,11 @@ describe('integration', () => {
headers: {
'X-Vault-Token': 'testtoken',
},
body: {
json: {
data: {
secret: 'SUPERSECRET',
},
},
json: true,
});
await got(`${vaultUrl}/v1/secret/data/nested/test`, {
@ -36,12 +35,11 @@ describe('integration', () => {
headers: {
'X-Vault-Token': 'testtoken',
},
body: {
json: {
data: {
otherSecret: 'OTHERSUPERSECRET',
},
},
json: true,
});
});

View file

@ -14,12 +14,11 @@ const got = require('got');
headers: {
'X-Vault-Token': 'testtoken',
},
body: {
json: {
data: {
secret: 'SUPERSECRET',
},
},
json: true,
});
await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/secret/data/nested/test`, {
@ -27,12 +26,11 @@ const got = require('got');
headers: {
'X-Vault-Token': 'testtoken',
},
body: {
json: {
data: {
otherSecret: 'OTHERSUPERSECRET',
},
},
json: true,
});
} catch (error) {
console.log(error);

View file

@ -11,60 +11,61 @@ const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.
describe('integration', () => {
beforeAll(async () => {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': 'testtoken',
},
});
// Create namespace
await got(`${vaultUrl}/v1/sys/namespaces/ns1`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
},
json: true,
});
// Enable secret engine
await got(`${vaultUrl}/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(`${vaultUrl}/v1/secret/data/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns1',
},
body: {
data: {
secret: 'SUPERSECRET_IN_NAMESPACE',
try {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': 'testtoken',
},
},
json: true,
});
});
await got(`${vaultUrl}/v1/secret/data/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns1',
},
body: {
data: {
otherSecret: 'OTHERSUPERSECRET_IN_NAMESPACE',
// Create namespace
await got(`${vaultUrl}/v1/sys/namespaces/ns1`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
}
});
// Enable secret engine
await got(`${vaultUrl}/v1/sys/mounts/secret`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns1',
},
},
json: true,
});
json: { path: 'secret', type: 'kv', config: {}, options: { version: 2 }, generate_signing_key: true },
});
await got(`${vaultUrl}/v1/secret/data/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns1',
},
json: {
data: {
secret: 'SUPERSECRET_IN_NAMESPACE',
},
},
});
await got(`${vaultUrl}/v1/secret/data/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns1',
},
json: {
data: {
otherSecret: 'OTHERSUPERSECRET_IN_NAMESPACE',
},
},
});
} catch (e) {
console.error('Failed to setup test', e);
throw e;
}
});
beforeEach(() => {
@ -128,3 +129,145 @@ describe('integration', () => {
expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET_IN_NAMESPACE');
});
});
describe('authenticate with approle', () => {
let roleId;
let secretId;
beforeAll(async () => {
try {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': 'testtoken',
},
});
// Create namespace
await got(`${vaultUrl}/v1/sys/namespaces/ns2`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
},
});
// Enable secret engine
await got(`${vaultUrl}/v1/sys/mounts/secret`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns2',
},
json: { path: 'secret', type: 'kv', config: {}, options: { version: 2 }, generate_signing_key: true },
});
// Add secret
await got(`${vaultUrl}/v1/secret/data/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns2',
},
json: {
data: {
secret: 'SUPERSECRET_WITH_APPROLE',
},
},
});
// Enable approle
await got(`${vaultUrl}/v1/sys/auth/approle`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns2',
},
json: {
type: 'approle'
},
});
// Create policies
await got(`${vaultUrl}/v1/sys/policies/acl/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns2',
},
json: {
"name":"test",
"policy":"path \"auth/approle/*\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"auth/approle/role/my-role/role-id\"\n{\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\npath \"auth/approle/role/my-role/secret-id\"\n{\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\n\npath \"secret/data/*\" {\n capabilities = [\"list\"]\n}\npath \"secret/metadata/*\" {\n capabilities = [\"list\"]\n}\n\npath \"secret/data/test\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"secret/metadata/test\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"secret/data/test/*\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"secret/metadata/test/*\" {\n capabilities = [\"read\", \"list\"]\n}\n"
},
});
// Create approle
await got(`${vaultUrl}/v1/auth/approle/role/my-role`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns2',
},
json: {
policies: 'test'
},
});
// Get role-id
const roldIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/role-id`, {
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns2',
},
responseType: 'json',
});
roleId = roldIdResponse.body.data.role_id;
// Get secret-id
const secretIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/secret-id`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Namespace': 'ns2',
},
responseType: 'json',
});
secretId = secretIdResponse.body.data.secret_id;
} catch(err) {
console.warn('Create approle', err);
throw err;
}
});
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('method')
.mockReturnValue('approle');
when(core.getInput)
.calledWith('roleId')
.mockReturnValue(roleId);
when(core.getInput)
.calledWith('secretId')
.mockReturnValue(secretId);
when(core.getInput)
.calledWith('url')
.mockReturnValue(`${vaultUrl}`);
when(core.getInput)
.calledWith('namespace')
.mockReturnValue('ns2');
});
function mockInput(secrets) {
when(core.getInput)
.calledWith('secrets')
.mockReturnValue(secrets);
}
it('authenticate with approle', async()=> {
mockInput('test secret');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_WITH_APPROLE');
})
});