Skip to content
This repository has been archived by the owner on Jul 26, 2022. It is now read-only.

Commit

Permalink
feat(secrets-manager): Added support for secrets versioning in Secret…
Browse files Browse the repository at this point in the history
…s Manager using version stage labels (#181)

* Added support for secrets versioning in Secrets Manager using version stage labels
  • Loading branch information
furikake authored and Flydiverny committed Nov 14, 2019
1 parent 0af60a2 commit 9d6c2f9
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 9 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ spec:
- key: hello-service/credentials
name: username
property: username
- key: hello-service/credentials
name: password
# Version Stage in Secrets Manager
versionStage: AWSPREVIOUS
property: password_previous
```

alternatively you can use `dataFrom` and get all the values from hello-service/credentials:
Expand Down
29 changes: 29 additions & 0 deletions lib/backends/kv-backend.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,35 @@ describe('kv-backend', () => {
})
expect(secretPropertyValues).deep.equals([{ fakePropertyName1: 'fakePropertyValue1' }, { fakePropertyName2: 'fakePropertyValue2' }])
})

it('fetches secret property with version', async () => {
kvBackend._get.onFirstCall().resolves('fakePropertyValue1')
kvBackend._get.onSecondCall().resolves('fakePropertyValue2')

const secretPropertyValues = await kvBackend._fetchDataValues({
data: [{
key: 'fakePropertyKey1',
name: 'fakePropertyName1'
}, {
key: 'fakePropertyKey2',
versionStage: 'AWSPREVIOUS',
name: 'fakePropertyName2'
}],
specOptions: {}
})

expect(kvBackend._get.getCall(0).args[0]).to.deep.equal({
key: 'fakePropertyKey1',
keyOptions: {},
specOptions: {}
})
expect(kvBackend._get.getCall(1).args[0]).to.deep.equal({
key: 'fakePropertyKey2',
keyOptions: { versionStage: 'AWSPREVIOUS' },
specOptions: {}
})
expect(secretPropertyValues).deep.equals([{ fakePropertyName1: 'fakePropertyValue1' }, { fakePropertyName2: 'fakePropertyValue2' }])
})
})

describe('_fetchDataFromValues', () => {
Expand Down
5 changes: 3 additions & 2 deletions lib/backends/secrets-manager-backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ class SecretsManagerBackend extends KVBackend {
* Get secret property value from Secrets Manager.
* @param {string} key - Key used to store secret property value in Secrets Manager.
* @param {object} keyOptions - Options for this specific key, eg version etc.
* @param {string} keyOptions.versionStage - Version stage
* @param {object} specOptions - Options for this external secret, eg role
* @param {string} specOptions.roleArn - IAM role arn to assume
* @returns {Promise} Promise object representing secret property value.
*/
async _get ({ key, specOptions: { roleArn } }) {
async _get ({ key, specOptions: { roleArn }, keyOptions: { versionStage = 'AWSCURRENT' } }) {
this._logger.info(`fetching secret property ${key} with role: ${roleArn || 'pods role'}`)

let client = this._client
Expand All @@ -41,7 +42,7 @@ class SecretsManagerBackend extends KVBackend {
}

const data = await client
.getSecretValue({ SecretId: key })
.getSecretValue({ SecretId: key, VersionStage: versionStage })
.promise()

if ('SecretBinary' in data) {
Expand Down
41 changes: 35 additions & 6 deletions lib/backends/secrets-manager-backend.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('SecretsManagerBackend', () => {
let assumeRoleMock
let secretsManagerBackend
const specOptions = {}
const keyOptions = {}

const assumeRoleCredentials = {
Credentials: {
Expand Down Expand Up @@ -51,11 +52,13 @@ describe('SecretsManagerBackend', () => {

const secretPropertyValue = await secretsManagerBackend._get({
key: 'fakeSecretKey',
specOptions
specOptions,
keyOptions
})

expect(clientMock.getSecretValue.calledWith({
SecretId: 'fakeSecretKey'
SecretId: 'fakeSecretKey',
VersionStage: 'AWSCURRENT'
})).to.equal(true)
expect(clientFactoryMock.getCall(0).args).deep.equals([])
expect(assumeRoleMock.callCount).equals(0)
Expand All @@ -69,11 +72,13 @@ describe('SecretsManagerBackend', () => {

const secretPropertyValue = await secretsManagerBackend._get({
key: 'fakeSecretKey',
specOptions
specOptions,
keyOptions
})

expect(clientMock.getSecretValue.calledWith({
SecretId: 'fakeSecretKey'
SecretId: 'fakeSecretKey',
VersionStage: 'AWSCURRENT'
})).to.equal(true)
expect(clientFactoryMock.getCall(0).args).deep.equals([])
expect(assumeRoleMock.callCount).equals(0)
Expand All @@ -87,7 +92,8 @@ describe('SecretsManagerBackend', () => {

const secretPropertyValue = await secretsManagerBackend._get({
key: 'fakeSecretKey',
specOptions: { roleArn: 'my-role' }
specOptions: { roleArn: 'my-role' },
keyOptions
})

expect(clientFactoryMock.lastArg).deep.equals({
Expand All @@ -96,7 +102,8 @@ describe('SecretsManagerBackend', () => {
sessionToken: assumeRoleCredentials.Credentials.SessionToken
})
expect(clientMock.getSecretValue.calledWith({
SecretId: 'fakeSecretKey'
SecretId: 'fakeSecretKey',
VersionStage: 'AWSCURRENT'
})).to.equal(true)
expect(clientFactoryMock.getCall(0).args).deep.equals([])
expect(clientFactoryMock.getCall(1).args).deep.equals([{
Expand All @@ -107,5 +114,27 @@ describe('SecretsManagerBackend', () => {
expect(assumeRoleMock.callCount).equals(1)
expect(secretPropertyValue).equals('fakeAssumeRoleSecretValue')
})

it('returns secret property value with versionStage', async () => {
getSecretValuePromise.promise.resolves({
SecretString: 'fakeSecretPropertyValuePreviousVersion'
})

const secretPropertyValue = await secretsManagerBackend._get({
key: 'fakeSecretKey',
specOptions,
keyOptions: {
versionStage: 'AWSPREVIOUS'
}
})

expect(clientMock.getSecretValue.calledWith({
SecretId: 'fakeSecretKey',
VersionStage: 'AWSPREVIOUS'
})).to.equal(true)
expect(clientFactoryMock.getCall(0).args).deep.equals([])
expect(assumeRoleMock.callCount).equals(0)
expect(secretPropertyValue).equals('fakeSecretPropertyValuePreviousVersion')
})
})
})
3 changes: 2 additions & 1 deletion lib/poller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const merge = require('lodash.merge')
* @param {string} properties[].name - Kubernetes Secret property name.
* @param {string} properties[].property - If the backend secret is an
* object, this is the property name of the value to use.
* @param {string} properties[].versionStage - If the backend supports versioning, eg secretsManager backend
*/

/** Poller class. */
Expand Down Expand Up @@ -132,7 +133,7 @@ class Poller {
}

/**
* Create or update Kubernets secret in the cluster.
* Create or update Kubernetes secret in the cluster.
* @returns {Promise} Promise object representing operation result.
*/
async _upsertKubernetesSecret () {
Expand Down

0 comments on commit 9d6c2f9

Please sign in to comment.