Skip to content

Commit

Permalink
feat(validation): add additional practices rules
Browse files Browse the repository at this point in the history
  • Loading branch information
WitoDelnat committed Jan 31, 2024
1 parent cc8dce5 commit 44c8fd0
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/khaki-meals-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@monokle/validation": minor
---

Improve compatibility with RHACS default security policy
14 changes: 14 additions & 0 deletions packages/validation/src/validators/practices/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ import {noRootGroup} from './rules/KBP109-no-root-group.js';
import {noPodExecute} from './rules/KBP108-noPodExecute.js';
import {noPodCreate} from './rules/KBP107-noPodCreate.js';
import {noAutomountServiceAccountToken} from './rules/KBP106-noAutomountServiceAccountToken.js';
import {noSshExposed} from './rules/KBP110-no-ssh-exposed.js';
import {noSecretMountedAsEnv} from './rules/KBP112-no-secret-mounted-as-env.js';
import {noSecretEnv} from './rules/KBP111-no-secret-env.js';
import {privilegedPorts} from './rules/KBP113-privileged-ports.js';
import {noExposedService} from './rules/KBP114-no-exposed-service.js';
import {mountPropagation} from './rules/KBP115-mount-propagation.js';
import {imageTagged} from './rules/KBP116-image-tagged.js';

export default definePlugin({
id: 'KBP',
Expand All @@ -36,5 +43,12 @@ export default definePlugin({
noPodExecute,
noPodCreate,
noAutomountServiceAccountToken,
noSshExposed,
noSecretEnv,
noSecretMountedAsEnv,
privilegedPorts,
noExposedService,
mountPropagation,
imageTagged,
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {defineRule} from '../../custom/config.js';
import {validatePodSpec} from '../../custom/utils.js';

export const noSshExposed = defineRule({
id: 110,
description: 'Disallow exposing ports associated with SSH',
fullDescription:
'Expose port 22 is prohibited, as its commonly reserved for SSH. This could give malicious actors access to your containers.',
help: "Do not set 'containers[].ports[].containerPort to 22'.",
advanced: {
enabled: false,
severity: 7,
},
validate({resources}, {report}) {
validatePodSpec(resources, (resource, pod, prefix) => {
pod.containers.forEach((container, i) => {
container.ports?.forEach((port, j) => {
const valid = port.containerPort !== 22;
if (valid) return;
report(resource, {
path: `${prefix}.containers.${i}.ports.${j}.containerPort`,
});
});
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {defineRule} from '../../custom/config.js';
import {validatePodSpec} from '../../custom/utils.js';

const SECRET_LIKE_NAMES = ['SECRET'];

export const noSecretEnv = defineRule({
id: 111,
description: 'Disallow secrets in environment variables',
fullDescription:
'Checks whether your environment variables contains "SECRET". This practice often hints that instead a secret vault should be used.',
help: 'Remove the variable and rely on a vault for secret management.',
advanced: {
enabled: false,
severity: 3,
},
validate({resources}, {report}) {
validatePodSpec(resources, (resource, pod, prefix) => {
pod.containers.forEach((container, i) => {
container.env?.forEach((env, j) => {
const invalid = SECRET_LIKE_NAMES.some(s => env.name.includes(s));
if (!invalid) return;
report(resource, {
path: `${prefix}.containers.${i}.env.${j}.name`,
});
});
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {defineRule} from '../../custom/config.js';
import {validatePodSpec} from '../../custom/utils.js';

export const noSecretMountedAsEnv = defineRule({
id: 112,
description: 'Disallow mounting secrets in environment variables',
fullDescription:
'Checks whether your environment variables mounts secrets. This practice often hints that instead a secret vault should be used.',
help: 'Remove the variable and rely on a vault for secret management.',
advanced: {
enabled: false,
severity: 3,
},
validate({resources}, {report}) {
validatePodSpec(resources, (resource, pod, prefix) => {
pod.containers.forEach((container, i) => {
container.env?.forEach((env, j) => {
const invalid = env.valueFrom?.secretKeyRef !== undefined;
if (!invalid) return;
report(resource, {
path: `${prefix}.containers.${i}.env.${j}.valueFrom.secretKeyRef`,
});
});
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {defineRule} from '../../custom/config.js';
import {validatePodSpec} from '../../custom/utils.js';

export const privilegedPorts = defineRule({
id: 113,
description: 'Restrict usage of privileged ports.',
fullDescription:
'Privileged ports are TCP/IP port numbers lower than 1024. Normal users and processes can not use them for security reasons, but containers might map their ports to privileged ports.',
help: 'Remove the variable and rely on a vault for secret management.',
advanced: {
enabled: false,
severity: 7,
},
validate({resources}, {report}) {
validatePodSpec(resources, (resource, pod, prefix) => {
pod.containers.forEach((container, i) => {
container.ports?.forEach((p, j) => {
const valid = p.containerPort >= 1024;
if (valid) return;
report(resource, {
path: `${prefix}.containers.${i}.ports.${j}.containerPort`,
});
});
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {defineRule} from '../../custom/config.js';
import {isService} from '../../custom/schemas/service.v1.js';

export const noExposedService = defineRule({
id: 114,
description: 'Disallow exposing services publicly.',
fullDescription:
'Exposed services are those who are not of the "ClusterIP" type. This practice hints that an ingress should be used instead.',
help: 'Use ClusterIP services and expose it through an ingress.',
advanced: {
enabled: false,
severity: 5,
},
validate({resources}, {report}) {
resources.filter(isService).forEach(service => {
const valid = service.spec?.type === undefined || service.spec.type === 'ClusterIP';
if (valid) return;
report(service, {
path: 'spec.type',
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {defineRule} from '../../custom/config.js';
import {validatePodSpec} from '../../custom/utils.js';

export const mountPropagation = defineRule({
id: 115,
description: 'Prohibit bidirectional mount propagation',
fullDescription:
'Bidirectional mount propagation can be dangerous. It can damage the host operating system and therefore it is allowed only in privileged containers. Familiarity with Linux kernel behavior is strongly recommended. In addition, any volume mounts created by containers in pods must be destroyed (unmounted) by the containers on termination.',
help: 'Do not set Bidirectional mount propagations.',
advanced: {
enabled: false,
severity: 7,
},
validate({resources}, {report}) {
validatePodSpec(resources, (resource, pod, prefix) => {
pod.initContainers?.forEach((container, i) => {
container.volumeMounts?.forEach((volume, j) => {
const invalid = volume.mountPropagation === 'Bidirectional';
if (!invalid) return;
report(resource, {
path: `${prefix}.initContainers.${i}.volumeMounts.${j}.mountPropagation`,
});
});
});
pod.containers.forEach((container, i) => {
container.volumeMounts?.forEach((volume, j) => {
const invalid = volume.mountPropagation === 'Bidirectional';
if (!invalid) return;
report(resource, {
path: `${prefix}.containers.${i}.volumeMounts.${j}.mountPropagation`,
});
});
});
});
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {defineRule} from '../../custom/config.js';
import {validatePodSpec} from '../../custom/utils.js';

export const imageTagged = defineRule({
id: 116,
description: 'Require images to be tagged.',
fullDescription:
'Untagged images will pull undetermined image versions. Adding explicit tags makes it easy to understand what is deployed and helps with rollbacks in case of problems.',
help: 'Add a tag to the image.',
advanced: {
enabled: false,
severity: 5,
},
validate({resources}, {report}) {
validatePodSpec(resources, (resource, pod, prefix) => {
pod.initContainers?.forEach((container, i) => {
const parts = container.image?.split(':');
const valid = parts?.at(1) !== undefined;
if (valid) return;
report(resource, {
path: `${prefix}.initContainers.${i}.image`,
});
});
pod.containers.forEach((container, i) => {
const parts = container.image?.split(':');
const valid = parts?.at(1) !== undefined;
if (valid) return;
report(resource, {
path: `${prefix}.containers.${i}.image`,
});
});
});
},
});

0 comments on commit 44c8fd0

Please sign in to comment.