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

Commit 1517333

Browse files
omerlhFlydiverny
andauthored
feat: added the option to enforce namespace annotations (#448)
* added the option to enforce namespace annotations * Update lib/poller.js Co-authored-by: Markus Maga <[email protected]> * Update config/environment.js Co-authored-by: Markus Maga <[email protected]> Co-authored-by: Markus Maga <[email protected]>
1 parent 605a815 commit 1517333

File tree

5 files changed

+152
-2
lines changed

5 files changed

+152
-2
lines changed

bin/daemon.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ const {
2424
pollerIntervalMilliseconds,
2525
pollingDisabled,
2626
rolePermittedAnnotation,
27-
namingPermittedAnnotation
27+
namingPermittedAnnotation,
28+
enforceNamespaceAnnotation
2829
} = require('../config')
2930

3031
async function main () {
@@ -49,6 +50,7 @@ async function main () {
4950
pollerIntervalMilliseconds,
5051
rolePermittedAnnotation,
5152
namingPermittedAnnotation,
53+
enforceNamespaceAnnotation,
5254
customResourceManifest,
5355
pollingDisabled,
5456
logger

config/environment.js

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const pollingDisabled = 'DISABLE_POLLING' in process.env
3131

3232
const rolePermittedAnnotation = process.env.ROLE_PERMITTED_ANNOTATION || 'iam.amazonaws.com/permitted'
3333
const namingPermittedAnnotation = process.env.NAMING_PERMITTED_ANNOTATION || 'externalsecrets.kubernetes-client.io/permitted-key-name'
34+
const enforceNamespaceAnnotation = 'ENFORCE_NAMESPACE_ANNOTATIONS' in process.env || false
3435

3536
const metricsPort = process.env.METRICS_PORT || 3001
3637

@@ -44,6 +45,7 @@ module.exports = {
4445
metricsPort,
4546
rolePermittedAnnotation,
4647
namingPermittedAnnotation,
48+
enforceNamespaceAnnotation,
4749
pollingDisabled,
4850
logLevel,
4951
customResourceManagerDisabled,

lib/poller-factory.js

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class PollerFactory {
2121
rolePermittedAnnotation,
2222
namingPermittedAnnotation,
2323
customResourceManifest,
24+
enforceNamespaceAnnotation,
2425
pollingDisabled,
2526
logger
2627
}) {
@@ -32,6 +33,7 @@ class PollerFactory {
3233
this._customResourceManifest = customResourceManifest
3334
this._rolePermittedAnnotation = rolePermittedAnnotation
3435
this._namingPermittedAnnotation = namingPermittedAnnotation
36+
this._enforceNamespaceAnnotation = enforceNamespaceAnnotation
3537
this._pollingDisabled = pollingDisabled
3638
}
3739

@@ -49,6 +51,7 @@ class PollerFactory {
4951
customResourceManifest: this._customResourceManifest,
5052
rolePermittedAnnotation: this._rolePermittedAnnotation,
5153
namingPermittedAnnotation: this._namingPermittedAnnotation,
54+
enforceNamespaceAnnotation: this._enforceNamespaceAnnotation,
5255
pollingDisabled: this._pollingDisabled,
5356
externalSecret
5457
})

lib/poller.js

+22-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class Poller {
2929
* @param {Object} customResourceManifest - CRD manifest
3030
* @param {Object} externalSecret - ExternalSecret manifest.
3131
* @param {string} rolePermittedAnnotation - namespace annotation that defines which roles can be assumed within this namespace
32+
* @param {string} namingPermittedAnnotation - namespace annotation that defines which keys can be assumed within this namespace
33+
* @param {string} enforceNamespaceAnnotation - should enforce namespace annotations
3234
* @param {Object} metrics - Metrics client.
3335
*/
3436
constructor ({
@@ -40,6 +42,7 @@ class Poller {
4042
customResourceManifest,
4143
rolePermittedAnnotation,
4244
namingPermittedAnnotation,
45+
enforceNamespaceAnnotation,
4346
pollingDisabled,
4447
externalSecret
4548
}) {
@@ -53,6 +56,7 @@ class Poller {
5356
this._rolePermittedAnnotation = rolePermittedAnnotation
5457
this._namingPermittedAnnotation = namingPermittedAnnotation
5558
this._customResourceManifest = customResourceManifest
59+
this._enforceNamespaceAnnotation = enforceNamespaceAnnotation
5660

5761
this._externalSecret = externalSecret
5862
this._spec = externalSecret.spec || externalSecret.secretDescriptor
@@ -197,6 +201,8 @@ class Poller {
197201
let reason = ''
198202

199203
if (!namespace.metadata.annotations) {
204+
allowed = !this._enforceNamespaceAnnotation
205+
reason = this._enforceNamespaceAnnotation ? 'Namespace annotation is required' : ''
200206
return {
201207
allowed, reason
202208
}
@@ -213,6 +219,14 @@ class Poller {
213219
reNaming = new RegExp(namingConvention)
214220
}
215221

222+
if (!namingConvention && this._enforceNamespaceAnnotation) {
223+
allowed = false
224+
reason = `Missing required annotation ${this._namingPermittedAnnotation} on namespace ${namespace.metadata.name}`
225+
return {
226+
allowed, reason
227+
}
228+
}
229+
216230
// Testing data property
217231
if (namingConvention && externalData) {
218232
externalData.forEach((secretProperty, index) => {
@@ -242,14 +256,21 @@ class Poller {
242256

243257
// 2. testing assume role if configured
244258

245-
const role = descriptor.roleArn
259+
const role = descriptor.roleArn || descriptor.vaultRole
246260

247261
if (!role) {
248262
return {
249263
allowed, reason
250264
}
251265
}
252266

267+
if (!namespace.metadata.annotations[this._rolePermittedAnnotation] && this._enforceNamespaceAnnotation) {
268+
allowed = false
269+
reason = `Missing required annotation ${this._rolePermittedAnnotation} on namespace ${namespace.metadata.name}`
270+
return {
271+
allowed, reason
272+
}
273+
}
253274
// an empty annotation value allows access to all roles
254275
const re = new RegExp(namespace.metadata.annotations[this._rolePermittedAnnotation])
255276

lib/poller.test.js

+122
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,128 @@ describe('Poller', () => {
911911
]
912912
},
913913
permitted: true
914+
},
915+
{
916+
// test regex
917+
ns: { metadata: { annotations: { [namingPermittedAnnotation]: '.*', [rolePermittedAnnotation]: 'a' } } },
918+
descriptor: {
919+
data: [
920+
{ key: 'whatever', name: 'somethingelse' }
921+
],
922+
roleArn: 'b'
923+
},
924+
permitted: false
925+
},
926+
{
927+
// test regex
928+
ns: { metadata: { annotations: { [namingPermittedAnnotation]: '.*', [rolePermittedAnnotation]: 'a' } } },
929+
descriptor: {
930+
data: [
931+
{ key: 'whatever', name: 'somethingelse' }
932+
],
933+
vaultRole: 'b'
934+
},
935+
permitted: false
936+
},
937+
{
938+
// test regex
939+
ns: { metadata: { annotations: { [namingPermittedAnnotation]: '.*', [rolePermittedAnnotation]: 'a' } } },
940+
descriptor: {
941+
data: [
942+
{ key: 'whatever', name: 'somethingelse' }
943+
],
944+
roleArn: 'a'
945+
},
946+
permitted: true
947+
},
948+
{
949+
// test regex
950+
ns: { metadata: { annotations: { [namingPermittedAnnotation]: '.*', [rolePermittedAnnotation]: 'a' } } },
951+
descriptor: {
952+
data: [
953+
{ key: 'whatever', name: 'somethingelse' }
954+
],
955+
vaultRole: 'a'
956+
},
957+
permitted: true
958+
}
959+
]
960+
961+
for (let i = 0; i < testcases.length; i++) {
962+
const testcase = testcases[i]
963+
const verdict = poller._isPermitted(testcase.ns, testcase.descriptor)
964+
expect(verdict.allowed, `test case ${i + 1}`).to.equal(testcase.permitted)
965+
}
966+
})
967+
})
968+
describe('namespace annotation enforcement', () => {
969+
let poller
970+
beforeEach(() => {
971+
poller = new Poller({
972+
backends: {
973+
fakeBackendType: backendMock
974+
},
975+
metrics: metricsMock,
976+
intervalMilliseconds: 5000,
977+
kubeClient: kubeClientMock,
978+
logger: loggerMock,
979+
externalSecret: fakeExternalSecret,
980+
rolePermittedAnnotation,
981+
namingPermittedAnnotation,
982+
enforceNamespaceAnnotation: true,
983+
customResourceManifest: fakeCustomResourceManifest
984+
})
985+
})
986+
987+
it('should enforce namespace annotations`', () => {
988+
const testcases = [
989+
{
990+
// no annotations at all
991+
ns: { metadata: {} },
992+
descriptor: {},
993+
permitted: false
994+
},
995+
{
996+
// empty name annotation
997+
ns: { metadata: { annotations: { [namingPermittedAnnotation]: '' } } },
998+
descriptor: {
999+
dataFrom: ['test']
1000+
},
1001+
permitted: false
1002+
},
1003+
{
1004+
// empty role annotation
1005+
ns: { metadata: { annotations: { [rolePermittedAnnotation]: '' } } },
1006+
descriptor: {
1007+
dataFrom: ['test']
1008+
},
1009+
permitted: false
1010+
},
1011+
{
1012+
// missing role annotation
1013+
ns: { metadata: { annotations: { [namingPermittedAnnotation]: 'a' } } },
1014+
descriptor: {
1015+
dataFrom: ['a'],
1016+
roleArn: 'a'
1017+
},
1018+
permitted: false
1019+
},
1020+
{
1021+
// empty role annotation
1022+
ns: { metadata: { annotations: { [namingPermittedAnnotation]: 'a', [rolePermittedAnnotation]: '' } } },
1023+
descriptor: {
1024+
dataFrom: ['a'],
1025+
roleArn: 'a'
1026+
},
1027+
permitted: false
1028+
},
1029+
{
1030+
// all required annotations
1031+
ns: { metadata: { annotations: { [namingPermittedAnnotation]: 'a', [rolePermittedAnnotation]: 'b' } } },
1032+
descriptor: {
1033+
dataFrom: ['a']
1034+
},
1035+
permitted: true
9141036
}
9151037
]
9161038

0 commit comments

Comments
 (0)