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

Commit

Permalink
feat: restrict iam roles per namespace
Browse files Browse the repository at this point in the history
add option to restrict the range of assumed roles by
specifying an regular expression on a namespace annotation

Signed-off-by: Moritz Johner <[email protected]>
  • Loading branch information
moolen committed Aug 15, 2019
1 parent 4548af4 commit 747a457
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
40 changes: 39 additions & 1 deletion lib/poller.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* object, this is the property name of the value to use.
*/

const annotationPermittedKey = 'iam.amazonaws.com/permitted'

/** Poller class. */
class Poller {
/**
Expand Down Expand Up @@ -86,9 +88,15 @@ class Poller {
async _upsertKubernetesSecret () {
const secretDescriptor = this._secretDescriptor
const secretName = secretDescriptor.name
const secretManifest = await this._createSecretManifest()
const kubeNamespace = this._kubeClient.api.v1.namespaces(this._namespace)

// check if namespace is allowed to fetch this secret
const ns = await kubeNamespace.get()
const verdict = await this._isPermitted(ns.body, secretDescriptor)
if (!verdict.allowed) {
throw (new Error(`not allowed to fetch secret: ${secretDescriptor.name}: ${verdict.reason}`))
}
const secretManifest = await this._createSecretManifest()
this._logger.info(`upserting secret ${secretName} in ${this._namespace}`)
try {
return await kubeNamespace.secrets.post({ body: secretManifest })
Expand All @@ -98,6 +106,36 @@ class Poller {
}
}

/**
* checks if the supplied namespace is allowed to sync the given secret
*
* @param {Object} namespace namespace object
* @param {Object} descriptor secret descriptor
*/
async _isPermitted (namespace, descriptor) {
const role = descriptor.roleArn
let allowed = true
let reason = ''

if (!namespace.metadata.annotations) {
return {
allowed, reason
}
}
// an empty annotation value allows access to all roles
const re = new RegExp(namespace.metadata.annotations[annotationPermittedKey])

if (!re.test(role)) {
allowed = false
reason = `namspace does not allow to assume role ${role}`
}

return {
allowed,
reason
}
}

/**
* Checks if secret exists, if not trigger a poll
*/
Expand Down
31 changes: 31 additions & 0 deletions lib/poller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,24 @@ describe('Poller', () => {
describe('_upsertKubernetesSecret', () => {
let kubeNamespaceMock
let poller
let fakeNamespace

beforeEach(() => {
poller = pollerFactory({
backendType: 'fakeBackendType',
name: 'fakeSecretName',
properties: ['fakePropertyName']
})
fakeNamespace = {
body: {
metadata: {
annotations: {}
}
}
}
kubeNamespaceMock = sinon.mock()
kubeNamespaceMock.secrets = sinon.stub().returns(kubeNamespaceMock)
kubeNamespaceMock.get = sinon.stub().resolves(fakeNamespace)
kubeClientMock.api = sinon.mock()
kubeClientMock.api.v1 = sinon.mock()
kubeClientMock.api.v1.namespaces = sinon.stub().returns(kubeNamespaceMock)
Expand Down Expand Up @@ -228,6 +237,7 @@ describe('Poller', () => {
conflictError.statusCode = 409
kubeNamespaceMock.secrets.post = sinon.stub().throws(conflictError)
kubeNamespaceMock.put = sinon.stub().resolves()
kubeNamespaceMock.get = sinon.stub().resolves(fakeNamespace)

await poller._upsertKubernetesSecret()

Expand All @@ -248,6 +258,27 @@ describe('Poller', () => {
})).to.equal(true)
})

it('does not permit update of secret', async () => {
fakeNamespace.body.metadata.annotations['iam.amazonaws.com/permitted'] = '^$'
poller = pollerFactory({
backendType: 'fakeBackendType',
name: 'fakeSecretName',
roleArn: 'arn:aws:iam::123456789012:role/test-role',
properties: ['fakePropertyName']
})
kubeNamespaceMock.get = sinon.stub().resolves(fakeNamespace)

let error
try {
await poller._upsertKubernetesSecret()
} catch (err) {
error = err
}

expect(error).to.not.equal(undefined)
expect(error.message).equals('not allowed to fetch secret: fakeSecretName: namspace does not allow to assume role arn:aws:iam::123456789012:role/test-role')
})

it('fails storing secret', async () => {
const internalErrorServer = new Error('Internal Error Server')
internalErrorServer.statusCode = 500
Expand Down

0 comments on commit 747a457

Please sign in to comment.