Skip to content

Commit 71f6050

Browse files
committed
feat: drop all capabilities
Following kubesec's best practices we are now explicitly dropping all capabilities in the snyk-monitor. This should positively affect the secure config displayed in Snyk UI.
1 parent 0b05aee commit 71f6050

File tree

5 files changed

+86
-2
lines changed

5 files changed

+86
-2
lines changed

snyk-monitor-deployment.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ spec:
5757
limits:
5858
cpu: '1'
5959
memory: '2Gi'
60-
securityContext: {}
60+
securityContext:
61+
capabilities:
62+
drop:
63+
- ALL
6164
volumes:
6265
- name: docker-config
6366
secret:

snyk-monitor/templates/deployment.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ spec:
5151
limits:
5252
cpu: '1'
5353
memory: '2Gi'
54+
securityContext:
55+
capabilities:
56+
drop:
57+
- ALL
5458
volumes:
5559
- name: docker-config
5660
secret:

test/helpers/deployment.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as tap from 'tap';
2+
import { V1Deployment } from '@kubernetes/client-node';
3+
4+
export function validateSecureConfiguration(test: tap, deployment: V1Deployment) {
5+
if (
6+
!deployment.spec ||
7+
!deployment.spec.template.spec ||
8+
!deployment.spec.template.spec.containers ||
9+
deployment.spec.template.spec.containers.length === 0 ||
10+
!deployment.spec.template.spec.containers[0].securityContext
11+
) {
12+
test.fail('bad container spec or missing securityContext');
13+
return;
14+
}
15+
16+
const securityContext =
17+
deployment.spec.template.spec.containers[0].securityContext;
18+
19+
if (!securityContext.capabilities) {
20+
test.fail('missing capabilities section in pod securityContext');
21+
return;
22+
}
23+
24+
test.same(
25+
securityContext.capabilities.drop,
26+
['ALL'],
27+
'all capabilities are dropped',
28+
);
29+
30+
if (securityContext.capabilities.add) {
31+
test.false(
32+
securityContext.capabilities.add.includes('SYS_ADMIN'),
33+
'CAP_SYS_ADMIN not added',
34+
);
35+
}
36+
}
37+

test/integration/kubernetes.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CoreV1Api, KubeConfig } from '@kubernetes/client-node';
1+
import { CoreV1Api, KubeConfig, AppsV1Api } from '@kubernetes/client-node';
22
import setup = require('../setup');
33
import * as tap from 'tap';
44
import { getKindConfigPath } from '../helpers/kind';
@@ -9,6 +9,7 @@ import {
99
validateHomebaseStoredMetadata,
1010
getHomebaseResponseBody,
1111
} from '../helpers/homebase';
12+
import { validateSecureConfiguration } from '../helpers/deployment';
1213

1314
let integrationId: string;
1415

@@ -162,3 +163,19 @@ tap.test(`snyk-monitor has resource limits`, async (t) => {
162163
t.ok(monitorResources.requests.cpu !== undefined, 'snyk-monitor has cpu resource request');
163164
t.ok(monitorResources.requests.memory !== undefined, 'snyk-monitor has memory resource request');
164165
});
166+
167+
tap.test('snyk-monitor secure configuration is as expected', async (t) => {
168+
const kindConfigPath = await getKindConfigPath();
169+
const kubeConfig = new KubeConfig();
170+
kubeConfig.loadFromFile(kindConfigPath);
171+
172+
const k8sApi = kubeConfig.makeApiClient(AppsV1Api);
173+
174+
const response = await k8sApi.readNamespacedDeployment(
175+
'snyk-monitor',
176+
'snyk-monitor',
177+
);
178+
const deployment = response.body;
179+
180+
validateSecureConfiguration(t, deployment);
181+
});

test/unit/deployment-files.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as tap from 'tap';
2+
import { parse } from 'yaml';
3+
import { readFileSync } from 'fs';
4+
import { V1Deployment } from '@kubernetes/client-node';
5+
import * as snykConfig from '../../src/common/config';
6+
import { validateSecureConfiguration } from '../helpers/deployment';
7+
8+
/**
9+
* Note that these checks are also performed at runtime on the deployed snyk-monitor, see the integration tests.
10+
*/
11+
12+
tap.test('ensure the security properties of the deployment files are unchanged', async (t) => {
13+
t.same(snykConfig.IMAGE_STORAGE_ROOT, '/var/tmp', 'the snyk-monitor points to the correct mounted path');
14+
15+
const deploymentFiles = ['./snyk-monitor/templates/deployment.yaml', './snyk-monitor-deployment.yaml'];
16+
17+
for (const filePath of deploymentFiles) {
18+
const fileContent = readFileSync(filePath, 'utf8');
19+
const deployment: V1Deployment = parse(fileContent);
20+
21+
validateSecureConfiguration(t, deployment);
22+
}
23+
});

0 commit comments

Comments
 (0)