From baa0216960e85bd60240edb69b3bf8eab9a8a73f Mon Sep 17 00:00:00 2001 From: gwg313 Date: Wed, 27 May 2026 19:23:54 -0400 Subject: [PATCH] add kyverno policies Signed-off-by: gwg313 --- apps/audiobookshelf/deployment.yaml | 8 +++ apps/navidrome/deployment.yaml | 9 ++++ apps/stirling-pdf/deployment.yaml | 26 ++++++++- apps/yopass/deployment.yaml | 9 ++++ .../policies/00-meta/cleanup-rbac.yaml | 49 +++++++++++++++++ .../policies/00-meta/kustomization.yaml | 13 +++++ .../{ => 00-meta}/kyverno-cilium-rbac.yaml | 1 + .../default-container-resources.yaml | 42 +++++++++++++++ .../10-defaults/default-cronjob-history.yaml | 18 +++++++ .../default-deployment-revision-history.yaml | 17 ++++++ .../default-drop-all-capabilities.yaml | 36 +++++++++++++ .../policies/10-defaults/default-job-ttl.yaml | 17 ++++++ .../10-defaults/default-run-as-non-root.yaml | 46 ++++++++++++++++ .../default-seccomp-runtime-default.yaml | 49 +++++++++++++++++ .../policies/10-defaults/kustomization.yaml | 18 +++++++ .../policies/20-require/kustomization.yaml | 17 ++++++ .../require-drop-all-capabilities.yaml | 44 +++++++++++++++ .../require-no-privilege-escalation.yaml | 43 +++++++++++++++ .../policies/20-require/require-non-root.yaml | 52 ++++++++++++++++++ .../require-readonly-root-filesystem.yaml | 38 +++++++++++++ .../require-requests-limits.yaml | 0 .../require-seccomp-runtime-default.yaml | 52 ++++++++++++++++++ .../30-disallow/disallow-host-namespace.yaml | 35 ++++++++++++ .../disallow-hostpath-volumes.yaml | 20 +++++++ .../disallow-latest-tag.yaml | 0 .../disallow-privileged-containers.yaml | 48 +++++++++++++++++ .../policies/30-disallow/kustomization.yaml | 15 ++++++ .../generate-namespace-network-baseline.yaml} | 53 +++++++++++++------ .../policies/40-generate/kustomization.yaml | 12 +++++ .../50-cleanup/cleanup-evicted-pods.yaml | 22 ++++++++ .../50-cleanup/cleanup-old-replicasets.yaml | 22 ++++++++ .../cleanup-terminal-pods.yaml} | 13 +++-- .../policies/50-cleanup/kustomization.yaml | 14 +++++ platform/kyverno/policies/cleanup-rbac.yaml | 12 ----- platform/kyverno/policies/kustomization.yaml | 12 ++--- 35 files changed, 843 insertions(+), 39 deletions(-) create mode 100644 platform/kyverno/policies/00-meta/cleanup-rbac.yaml create mode 100644 platform/kyverno/policies/00-meta/kustomization.yaml rename platform/kyverno/policies/{ => 00-meta}/kyverno-cilium-rbac.yaml (96%) create mode 100644 platform/kyverno/policies/10-defaults/default-container-resources.yaml create mode 100644 platform/kyverno/policies/10-defaults/default-cronjob-history.yaml create mode 100644 platform/kyverno/policies/10-defaults/default-deployment-revision-history.yaml create mode 100644 platform/kyverno/policies/10-defaults/default-drop-all-capabilities.yaml create mode 100644 platform/kyverno/policies/10-defaults/default-job-ttl.yaml create mode 100644 platform/kyverno/policies/10-defaults/default-run-as-non-root.yaml create mode 100644 platform/kyverno/policies/10-defaults/default-seccomp-runtime-default.yaml create mode 100644 platform/kyverno/policies/10-defaults/kustomization.yaml create mode 100644 platform/kyverno/policies/20-require/kustomization.yaml create mode 100644 platform/kyverno/policies/20-require/require-drop-all-capabilities.yaml create mode 100644 platform/kyverno/policies/20-require/require-no-privilege-escalation.yaml create mode 100644 platform/kyverno/policies/20-require/require-non-root.yaml create mode 100644 platform/kyverno/policies/20-require/require-readonly-root-filesystem.yaml rename platform/kyverno/policies/{ => 20-require}/require-requests-limits.yaml (100%) create mode 100644 platform/kyverno/policies/20-require/require-seccomp-runtime-default.yaml create mode 100644 platform/kyverno/policies/30-disallow/disallow-host-namespace.yaml create mode 100644 platform/kyverno/policies/30-disallow/disallow-hostpath-volumes.yaml rename platform/kyverno/policies/{ => 30-disallow}/disallow-latest-tag.yaml (100%) create mode 100644 platform/kyverno/policies/30-disallow/disallow-privileged-containers.yaml create mode 100644 platform/kyverno/policies/30-disallow/kustomization.yaml rename platform/kyverno/policies/{generate-ns-network-baseline.yaml => 40-generate/generate-namespace-network-baseline.yaml} (64%) create mode 100644 platform/kyverno/policies/40-generate/kustomization.yaml create mode 100644 platform/kyverno/policies/50-cleanup/cleanup-evicted-pods.yaml create mode 100644 platform/kyverno/policies/50-cleanup/cleanup-old-replicasets.yaml rename platform/kyverno/policies/{purge-terminal-pods.yaml => 50-cleanup/cleanup-terminal-pods.yaml} (59%) create mode 100644 platform/kyverno/policies/50-cleanup/kustomization.yaml delete mode 100644 platform/kyverno/policies/cleanup-rbac.yaml diff --git a/apps/audiobookshelf/deployment.yaml b/apps/audiobookshelf/deployment.yaml index 32e61c2..7a8336e 100644 --- a/apps/audiobookshelf/deployment.yaml +++ b/apps/audiobookshelf/deployment.yaml @@ -15,8 +15,16 @@ spec: labels: app: audiobookshelf spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 containers: - name: audiobookshelf + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 image: ghcr.io/advplyr/audiobookshelf:2.35.0 imagePullPolicy: IfNotPresent ports: diff --git a/apps/navidrome/deployment.yaml b/apps/navidrome/deployment.yaml index 37af140..4e6bf54 100644 --- a/apps/navidrome/deployment.yaml +++ b/apps/navidrome/deployment.yaml @@ -15,8 +15,17 @@ spec: labels: app: navidrome spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 containers: - name: navidrome + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + readOnlyRootFilesystem: false image: deluan/navidrome:pr-5495 ports: - containerPort: 4533 diff --git a/apps/stirling-pdf/deployment.yaml b/apps/stirling-pdf/deployment.yaml index 930f763..6f0607e 100644 --- a/apps/stirling-pdf/deployment.yaml +++ b/apps/stirling-pdf/deployment.yaml @@ -14,9 +14,15 @@ spec: app: stirling-pdf spec: securityContext: - fsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 containers: - name: stirling-pdf + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:2.11.0-fat resources: requests: @@ -43,7 +49,25 @@ spec: - name: stirling-data mountPath: /pipeline subPath: pipeline + + - name: stirling-user + mountPath: /home + + - name: tmp + mountPath: /tmp + - name: stirling + mountPath: /tmp/stirling-pdf + - name: app-data + mountPath: /usr/local/bin volumes: - name: stirling-data persistentVolumeClaim: claimName: stirling-data + - name: tmp + emptyDir: {} + - name: stirling + emptyDir: {} + - name: app-data + emptyDir: {} + - name: stirling-user + emptyDir: {} diff --git a/apps/yopass/deployment.yaml b/apps/yopass/deployment.yaml index 67d8dfe..2b28a18 100644 --- a/apps/yopass/deployment.yaml +++ b/apps/yopass/deployment.yaml @@ -12,8 +12,17 @@ spec: labels: app.kubernetes.io/name: yopass spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 containers: - name: yopass + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + readOnlyRootFilesystem: false image: jhaals/yopass:13.1.0 args: - "--memcached=localhost:11211" diff --git a/platform/kyverno/policies/00-meta/cleanup-rbac.yaml b/platform/kyverno/policies/00-meta/cleanup-rbac.yaml new file mode 100644 index 0000000..038db7d --- /dev/null +++ b/platform/kyverno/policies/00-meta/cleanup-rbac.yaml @@ -0,0 +1,49 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kyverno:cleanup-extra-resources + labels: + rbac.kyverno.io/aggregate-to-cleanup-controller: "true" + annotations: + argocd.argoproj.io/sync-wave: "-1" + +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - delete + + - apiGroups: + - batch + resources: + - jobs + - cronjobs + verbs: + - get + - list + - watch + - delete + + - apiGroups: + - apps + resources: + - replicasets + verbs: + - get + - list + - watch + - delete + + - apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch diff --git a/platform/kyverno/policies/00-meta/kustomization.yaml b/platform/kyverno/policies/00-meta/kustomization.yaml new file mode 100644 index 0000000..e82ef17 --- /dev/null +++ b/platform/kyverno/policies/00-meta/kustomization.yaml @@ -0,0 +1,13 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - cleanup-rbac.yaml + - kyverno-cilium-rbac.yaml + +commonLabels: + app.kubernetes.io/part-of: kyverno-policies + policy-tier: meta + +commonAnnotations: + argocd.argoproj.io/sync-wave: "-1" diff --git a/platform/kyverno/policies/kyverno-cilium-rbac.yaml b/platform/kyverno/policies/00-meta/kyverno-cilium-rbac.yaml similarity index 96% rename from platform/kyverno/policies/kyverno-cilium-rbac.yaml rename to platform/kyverno/policies/00-meta/kyverno-cilium-rbac.yaml index b62c1b7..6e29b12 100644 --- a/platform/kyverno/policies/kyverno-cilium-rbac.yaml +++ b/platform/kyverno/policies/00-meta/kyverno-cilium-rbac.yaml @@ -5,6 +5,7 @@ metadata: labels: kyverno.io/aggregate-to-background: "true" kyverno.io/aggregate-to-reports: "true" + app.kubernetes.io/component: kyverno annotations: argocd.argoproj.io/sync-wave: "-1" rules: diff --git a/platform/kyverno/policies/10-defaults/default-container-resources.yaml b/platform/kyverno/policies/10-defaults/default-container-resources.yaml new file mode 100644 index 0000000..e6161c6 --- /dev/null +++ b/platform/kyverno/policies/10-defaults/default-container-resources.yaml @@ -0,0 +1,42 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: default-container-resources +spec: + background: true + + rules: + - name: default-container-resources + match: + any: + - resources: + kinds: + - Pod + + mutate: + foreach: + - list: "request.object.spec.containers" + patchStrategicMerge: + spec: + containers: + - (name): "{{ element.name }}" + resources: + requests: + +(cpu): "100m" + +(memory): "128Mi" + limits: + +(cpu): "200m" + +(memory): "512Mi" + + - list: "request.object.spec.initContainers || []" + patchStrategicMerge: + spec: + initContainers: + - (name): "{{ element.name }}" + resources: + requests: + +(cpu): "100m" + +(memory): "128Mi" + limits: + +(cpu): "200m" + +(memory): "512Mi" diff --git a/platform/kyverno/policies/10-defaults/default-cronjob-history.yaml b/platform/kyverno/policies/10-defaults/default-cronjob-history.yaml new file mode 100644 index 0000000..2df3a99 --- /dev/null +++ b/platform/kyverno/policies/10-defaults/default-cronjob-history.yaml @@ -0,0 +1,18 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: default-cronjob-history +spec: + rules: + - name: set-cronjob-history + match: + any: + - resources: + kinds: + - CronJob + + mutate: + patchStrategicMerge: + spec: + +(successfulJobsHistoryLimit): 1 + +(failedJobsHistoryLimit): 2 diff --git a/platform/kyverno/policies/10-defaults/default-deployment-revision-history.yaml b/platform/kyverno/policies/10-defaults/default-deployment-revision-history.yaml new file mode 100644 index 0000000..d6d2e8a --- /dev/null +++ b/platform/kyverno/policies/10-defaults/default-deployment-revision-history.yaml @@ -0,0 +1,17 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: default-deployment-revision-history +spec: + rules: + - name: set-revision-history-limit + match: + any: + - resources: + kinds: + - Deployment + + mutate: + patchStrategicMerge: + spec: + +(revisionHistoryLimit): 3 diff --git a/platform/kyverno/policies/10-defaults/default-drop-all-capabilities.yaml b/platform/kyverno/policies/10-defaults/default-drop-all-capabilities.yaml new file mode 100644 index 0000000..56ab6a7 --- /dev/null +++ b/platform/kyverno/policies/10-defaults/default-drop-all-capabilities.yaml @@ -0,0 +1,36 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: default-drop-all-capabilities +spec: + background: true + + rules: + - name: default-drop-all-capabilities + match: + any: + - resources: + kinds: + - Pod + + mutate: + foreach: + - list: "request.object.spec.containers" + patchStrategicMerge: + spec: + containers: + - (name): "{{ element.name }}" + securityContext: + +(capabilities): + drop: + - ALL + + - list: "request.object.spec.initContainers || []" + patchStrategicMerge: + spec: + initContainers: + - (name): "{{ element.name }}" + securityContext: + +(capabilities): + drop: + - ALL diff --git a/platform/kyverno/policies/10-defaults/default-job-ttl.yaml b/platform/kyverno/policies/10-defaults/default-job-ttl.yaml new file mode 100644 index 0000000..9749b27 --- /dev/null +++ b/platform/kyverno/policies/10-defaults/default-job-ttl.yaml @@ -0,0 +1,17 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: default-job-ttl +spec: + rules: + - name: set-job-ttl + match: + any: + - resources: + kinds: + - Job + + mutate: + patchStrategicMerge: + spec: + +(ttlSecondsAfterFinished): 86400 diff --git a/platform/kyverno/policies/10-defaults/default-run-as-non-root.yaml b/platform/kyverno/policies/10-defaults/default-run-as-non-root.yaml new file mode 100644 index 0000000..5900ac2 --- /dev/null +++ b/platform/kyverno/policies/10-defaults/default-run-as-non-root.yaml @@ -0,0 +1,46 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: default-run-as-non-root +spec: + background: true + + rules: + + - name: default-pod-run-as-non-root + match: + any: + - resources: + kinds: + - Pod + + mutate: + patchStrategicMerge: + spec: + securityContext: + +(runAsNonRoot): true + + - name: default-container-run-as-non-root + match: + any: + - resources: + kinds: + - Pod + + mutate: + foreach: + - list: "request.object.spec.containers" + patchStrategicMerge: + spec: + containers: + - (name): "{{ element.name }}" + securityContext: + +(runAsNonRoot): true + + - list: "request.object.spec.initContainers || []" + patchStrategicMerge: + spec: + initContainers: + - (name): "{{ element.name }}" + securityContext: + +(runAsNonRoot): true diff --git a/platform/kyverno/policies/10-defaults/default-seccomp-runtime-default.yaml b/platform/kyverno/policies/10-defaults/default-seccomp-runtime-default.yaml new file mode 100644 index 0000000..02841df --- /dev/null +++ b/platform/kyverno/policies/10-defaults/default-seccomp-runtime-default.yaml @@ -0,0 +1,49 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: default-seccomp-runtime-default + +spec: + background: true + + rules: + - name: default-pod-seccomp-runtime-default + match: + any: + - resources: + kinds: + - Pod + + mutate: + patchStrategicMerge: + spec: + securityContext: + seccompProfile: + type: RuntimeDefault + + - name: default-container-seccomp-runtime-default + match: + any: + - resources: + kinds: + - Pod + + mutate: + foreach: + - list: "request.object.spec.containers" + patchStrategicMerge: + spec: + containers: + - (name): "{{ element.name }}" + securityContext: + seccompProfile: + type: RuntimeDefault + + - list: "request.object.spec.initContainers || []" + patchStrategicMerge: + spec: + initContainers: + - (name): "{{ element.name }}" + securityContext: + seccompProfile: + type: RuntimeDefault diff --git a/platform/kyverno/policies/10-defaults/kustomization.yaml b/platform/kyverno/policies/10-defaults/kustomization.yaml new file mode 100644 index 0000000..fd0af43 --- /dev/null +++ b/platform/kyverno/policies/10-defaults/kustomization.yaml @@ -0,0 +1,18 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - default-container-resources.yaml + - default-cronjob-history.yaml + - default-deployment-revision-history.yaml + - default-drop-all-capabilities.yaml + - default-job-ttl.yaml + - default-run-as-non-root.yaml + - default-seccomp-runtime-default.yaml + +commonLabels: + app.kubernetes.io/part-of: kyverno-policies + policy-tier: defaults + +commonAnnotations: + argocd.argoproj.io/sync-wave: "0" diff --git a/platform/kyverno/policies/20-require/kustomization.yaml b/platform/kyverno/policies/20-require/kustomization.yaml new file mode 100644 index 0000000..22b1481 --- /dev/null +++ b/platform/kyverno/policies/20-require/kustomization.yaml @@ -0,0 +1,17 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - require-drop-all-capabilities.yaml + - require-no-privilege-escalation.yaml + - require-non-root.yaml + - require-readonly-root-filesystem.yaml + - require-requests-limits.yaml + - require-seccomp-runtime-default.yaml + +commonLabels: + app.kubernetes.io/part-of: kyverno-policies + policy-tier: require + +commonAnnotations: + argocd.argoproj.io/sync-wave: "1" diff --git a/platform/kyverno/policies/20-require/require-drop-all-capabilities.yaml b/platform/kyverno/policies/20-require/require-drop-all-capabilities.yaml new file mode 100644 index 0000000..bdb2d53 --- /dev/null +++ b/platform/kyverno/policies/20-require/require-drop-all-capabilities.yaml @@ -0,0 +1,44 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-drop-all-capabilities + annotations: + policies.kyverno.io/title: Require Dropping Linux Capabilities + policies.kyverno.io/category: Pod Security + policies.kyverno.io/severity: high +spec: + validationFailureAction: Enforce + background: true + + rules: + - name: require-drop-all-capabilities + match: + any: + - resources: + kinds: + - Pod + + validate: + message: "All containers must drop ALL Linux capabilities." + + foreach: + - list: "request.object.spec.containers" + pattern: + securityContext: + capabilities: + drop: + - ALL + + - list: "request.object.spec.initContainers" + pattern: + securityContext: + capabilities: + drop: + - ALL + + - list: "request.object.spec.ephemeralContainers" + pattern: + securityContext: + capabilities: + drop: + - ALL diff --git a/platform/kyverno/policies/20-require/require-no-privilege-escalation.yaml b/platform/kyverno/policies/20-require/require-no-privilege-escalation.yaml new file mode 100644 index 0000000..69a64ad --- /dev/null +++ b/platform/kyverno/policies/20-require/require-no-privilege-escalation.yaml @@ -0,0 +1,43 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-no-privilege-escalation +spec: + validationFailureAction: Enforce + background: true + + rules: + - name: require-no-privilege-escalation + match: + any: + - resources: + kinds: + - Pod + + validate: + message: "allowPrivilegeEscalation must be false." + + foreach: + - list: "request.object.spec.containers" + deny: + conditions: + any: + - key: "{{ element.securityContext.allowPrivilegeEscalation || `false` }}" + operator: Equals + value: true + + - list: "request.object.spec.initContainers || []" + deny: + conditions: + any: + - key: "{{ element.securityContext.allowPrivilegeEscalation || `false` }}" + operator: Equals + value: true + + - list: "request.object.spec.ephemeralContainers || []" + deny: + conditions: + any: + - key: "{{ element.securityContext.allowPrivilegeEscalation || `false` }}" + operator: Equals + value: true diff --git a/platform/kyverno/policies/20-require/require-non-root.yaml b/platform/kyverno/policies/20-require/require-non-root.yaml new file mode 100644 index 0000000..c902def --- /dev/null +++ b/platform/kyverno/policies/20-require/require-non-root.yaml @@ -0,0 +1,52 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-non-root + annotations: + policies.kyverno.io/title: Require Non-Root Containers + policies.kyverno.io/category: Pod Security + policies.kyverno.io/severity: high +spec: + validationFailureAction: Enforce + background: true + + rules: + - name: require-pod-run-as-non-root + match: + any: + - resources: + kinds: + - Pod + + validate: + message: "Pods must set runAsNonRoot=true." + pattern: + spec: + securityContext: + runAsNonRoot: true + + - name: require-container-run-as-non-root + match: + any: + - resources: + kinds: + - Pod + + validate: + message: "All containers must set runAsNonRoot=true." + + foreach: + - list: "request.object.spec.containers" + pattern: + securityContext: + runAsNonRoot: true + + - list: "request.object.spec.initContainers" + pattern: + securityContext: + runAsNonRoot: true + + - list: "request.object.spec.ephemeralContainers" + pattern: + securityContext: + runAsNonRoot: true diff --git a/platform/kyverno/policies/20-require/require-readonly-root-filesystem.yaml b/platform/kyverno/policies/20-require/require-readonly-root-filesystem.yaml new file mode 100644 index 0000000..ef1016e --- /dev/null +++ b/platform/kyverno/policies/20-require/require-readonly-root-filesystem.yaml @@ -0,0 +1,38 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-readonly-root-filesystem + annotations: + policies.kyverno.io/title: Require Read-Only Root Filesystem + policies.kyverno.io/category: Pod Security + policies.kyverno.io/severity: high +spec: + validationFailureAction: Audit + background: true + + rules: + - name: require-readonly-root-filesystem + match: + any: + - resources: + kinds: + - Pod + + validate: + message: "Containers must use readOnlyRootFilesystem=true." + + foreach: + - list: "request.object.spec.containers" + pattern: + securityContext: + readOnlyRootFilesystem: true + + - list: "request.object.spec.initContainers" + pattern: + securityContext: + readOnlyRootFilesystem: true + + - list: "request.object.spec.ephemeralContainers" + pattern: + securityContext: + readOnlyRootFilesystem: true diff --git a/platform/kyverno/policies/require-requests-limits.yaml b/platform/kyverno/policies/20-require/require-requests-limits.yaml similarity index 100% rename from platform/kyverno/policies/require-requests-limits.yaml rename to platform/kyverno/policies/20-require/require-requests-limits.yaml diff --git a/platform/kyverno/policies/20-require/require-seccomp-runtime-default.yaml b/platform/kyverno/policies/20-require/require-seccomp-runtime-default.yaml new file mode 100644 index 0000000..0ffbf4e --- /dev/null +++ b/platform/kyverno/policies/20-require/require-seccomp-runtime-default.yaml @@ -0,0 +1,52 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-seccomp-runtime-default +spec: + validationFailureAction: Enforce + background: true + + rules: + - name: require-pod-seccomp-runtime-default + match: + any: + - resources: + kinds: + - Pod + + validate: + message: "Pod seccompProfile.type must be RuntimeDefault." + pattern: + spec: + securityContext: + seccompProfile: + type: RuntimeDefault + + - name: require-container-seccomp-runtime-default + match: + any: + - resources: + kinds: + - Pod + + validate: + message: "All containers must use RuntimeDefault seccomp." + + foreach: + - list: "request.object.spec.containers" + pattern: + securityContext: + seccompProfile: + type: RuntimeDefault + + - list: "request.object.spec.initContainers" + pattern: + securityContext: + seccompProfile: + type: RuntimeDefault + + - list: "request.object.spec.ephemeralContainers" + pattern: + securityContext: + seccompProfile: + type: RuntimeDefault diff --git a/platform/kyverno/policies/30-disallow/disallow-host-namespace.yaml b/platform/kyverno/policies/30-disallow/disallow-host-namespace.yaml new file mode 100644 index 0000000..4507e1b --- /dev/null +++ b/platform/kyverno/policies/30-disallow/disallow-host-namespace.yaml @@ -0,0 +1,35 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-host-namespace +spec: + validationFailureAction: Enforce + background: true + + rules: + - name: no-host-namespaces + match: + any: + - resources: + kinds: + - Pod + - Deployment + - StatefulSet + - DaemonSet + + validate: + message: "Host networking, PID, and IPC are not allowed." + deny: + conditions: + any: + - key: "{{ request.object.spec.hostNetwork || false }}" + operator: Equals + value: true + + - key: "{{ request.object.spec.hostPID || false }}" + operator: Equals + value: true + + - key: "{{ request.object.spec.hostIPC || false }}" + operator: Equals + value: true diff --git a/platform/kyverno/policies/30-disallow/disallow-hostpath-volumes.yaml b/platform/kyverno/policies/30-disallow/disallow-hostpath-volumes.yaml new file mode 100644 index 0000000..c659b3b --- /dev/null +++ b/platform/kyverno/policies/30-disallow/disallow-hostpath-volumes.yaml @@ -0,0 +1,20 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-hostpath-volumes +spec: + validationFailureAction: Enforce + + rules: + - name: no-hostpath + match: + any: + - resources: + kinds: ["Pod"] + + validate: + message: "hostPath volumes are not allowed (escape risk)." + pattern: + spec: + volumes: + - (hostPath): null diff --git a/platform/kyverno/policies/disallow-latest-tag.yaml b/platform/kyverno/policies/30-disallow/disallow-latest-tag.yaml similarity index 100% rename from platform/kyverno/policies/disallow-latest-tag.yaml rename to platform/kyverno/policies/30-disallow/disallow-latest-tag.yaml diff --git a/platform/kyverno/policies/30-disallow/disallow-privileged-containers.yaml b/platform/kyverno/policies/30-disallow/disallow-privileged-containers.yaml new file mode 100644 index 0000000..17a3a10 --- /dev/null +++ b/platform/kyverno/policies/30-disallow/disallow-privileged-containers.yaml @@ -0,0 +1,48 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-privileged-containers + annotations: + policies.kyverno.io/title: Disallow Privileged Containers + policies.kyverno.io/category: Pod Security + policies.kyverno.io/severity: critical + +spec: + validationFailureAction: Enforce + background: true + + rules: + - name: disallow-privileged + match: + any: + - resources: + kinds: + - Pod + + validate: + message: "Privileged containers are not allowed." + + foreach: + - list: "request.object.spec.containers" + deny: + conditions: + any: + - key: "{{ element.securityContext.privileged || `false` }}" + operator: Equals + value: true + + - list: "request.object.spec.initContainers || []" + deny: + conditions: + any: + - key: "{{ element.securityContext.privileged || `false` }}" + operator: Equals + value: true + + - list: "request.object.spec.ephemeralContainers || []" + deny: + conditions: + any: + - key: "{{ element.securityContext.privileged || `false` }}" + operator: Equals + value: true diff --git a/platform/kyverno/policies/30-disallow/kustomization.yaml b/platform/kyverno/policies/30-disallow/kustomization.yaml new file mode 100644 index 0000000..896cb9d --- /dev/null +++ b/platform/kyverno/policies/30-disallow/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - disallow-host-namespace.yaml + - disallow-hostpath-volumes.yaml + - disallow-latest-tag.yaml + - disallow-privileged-containers.yaml + +commonLabels: + app.kubernetes.io/part-of: kyverno-policies + policy-tier: disallow + +commonAnnotations: + argocd.argoproj.io/sync-wave: "2" diff --git a/platform/kyverno/policies/generate-ns-network-baseline.yaml b/platform/kyverno/policies/40-generate/generate-namespace-network-baseline.yaml similarity index 64% rename from platform/kyverno/policies/generate-ns-network-baseline.yaml rename to platform/kyverno/policies/40-generate/generate-namespace-network-baseline.yaml index 20ab409..726db6c 100644 --- a/platform/kyverno/policies/generate-ns-network-baseline.yaml +++ b/platform/kyverno/policies/40-generate/generate-namespace-network-baseline.yaml @@ -1,21 +1,28 @@ apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: - name: generate-ns-network-baseline + name: generate-namespace-network-baseline annotations: - policies.kyverno.io/title: Inject Namespace Baseline CNP - policies.kyverno.io/description: Automatically provisions a local default-deny + DNS egress CNP inside new application namespaces. + policies.kyverno.io/title: Generate Namespace Network Baseline + policies.kyverno.io/category: Network Security + policies.kyverno.io/severity: high + policies.kyverno.io/description: >- + Automatically provisions a baseline CiliumNetworkPolicy + with default deny ingress and controlled DNS egress. argocd.argoproj.io/sync-options: Force=true,Replace=true spec: background: true + rules: - - name: inject-local-cnp + - name: generate-baseline-cnp + match: any: - resources: kinds: - Namespace + exclude: any: - resources: @@ -24,37 +31,53 @@ spec: - kube-system - kube-public - kube-node-lease - - argocd - kyverno - - cilium-ingress - - cilium-secrets + - argocd - cert-manager - - sealed-secrets - - nfs-subdir-external-provisioner - monitoring - - tekton-pipelines-resolvers + - cilium-secrets + - cilium-ingress + - sealed-secrets - tekton-pipelines + - tekton-pipelines-resolvers - pipelines-as-code + generate: + synchronize: true + apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy + name: baseline-network-security - namespace: "{{request.object.metadata.name}}" - synchronize: true + namespace: "{{ request.object.metadata.name }}" + data: + metadata: + labels: + security-tier: baseline + spec: endpointSelector: {} - ingress: - - {} + + policyTypes: + - Ingress + - Egress + + ingress: [] + egress: - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: kube-system k8s-app: kube-dns + toPorts: - ports: - port: "53" - protocol: ANY + protocol: UDP + - port: "53" + protocol: TCP + rules: dns: - matchPattern: "*" diff --git a/platform/kyverno/policies/40-generate/kustomization.yaml b/platform/kyverno/policies/40-generate/kustomization.yaml new file mode 100644 index 0000000..4373ffa --- /dev/null +++ b/platform/kyverno/policies/40-generate/kustomization.yaml @@ -0,0 +1,12 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - generate-namespace-network-baseline.yaml + +commonLabels: + app.kubernetes.io/part-of: kyverno-policies + policy-tier: generate + +commonAnnotations: + argocd.argoproj.io/sync-wave: "3" diff --git a/platform/kyverno/policies/50-cleanup/cleanup-evicted-pods.yaml b/platform/kyverno/policies/50-cleanup/cleanup-evicted-pods.yaml new file mode 100644 index 0000000..0746c14 --- /dev/null +++ b/platform/kyverno/policies/50-cleanup/cleanup-evicted-pods.yaml @@ -0,0 +1,22 @@ +apiVersion: kyverno.io/v2beta1 +kind: ClusterCleanupPolicy +metadata: + name: cleanup-evicted-pods +spec: + match: + any: + - resources: + kinds: + - Pod + + conditions: + all: + - key: "{{ target.status.reason }}" + operator: Equals + value: Evicted + + - key: "{{ time_diff('{{ target.metadata.creationTimestamp }}','{{ time_now_utc() }}') }}" + operator: GreaterThan + value: 1h + + schedule: "0 * * * *" diff --git a/platform/kyverno/policies/50-cleanup/cleanup-old-replicasets.yaml b/platform/kyverno/policies/50-cleanup/cleanup-old-replicasets.yaml new file mode 100644 index 0000000..be06182 --- /dev/null +++ b/platform/kyverno/policies/50-cleanup/cleanup-old-replicasets.yaml @@ -0,0 +1,22 @@ +apiVersion: kyverno.io/v2beta1 +kind: ClusterCleanupPolicy +metadata: + name: cleanup-old-replicasets +spec: + match: + any: + - resources: + kinds: + - ReplicaSet + + conditions: + all: + - key: "{{ target.spec.replicas || `0` }}" + operator: Equals + value: 0 + + - key: "{{ time_diff('{{ target.metadata.creationTimestamp }}','{{ time_now_utc() }}') }}" + operator: GreaterThan + value: 168h + + schedule: "0 3 * * *" diff --git a/platform/kyverno/policies/purge-terminal-pods.yaml b/platform/kyverno/policies/50-cleanup/cleanup-terminal-pods.yaml similarity index 59% rename from platform/kyverno/policies/purge-terminal-pods.yaml rename to platform/kyverno/policies/50-cleanup/cleanup-terminal-pods.yaml index d974772..44b975d 100644 --- a/platform/kyverno/policies/purge-terminal-pods.yaml +++ b/platform/kyverno/policies/50-cleanup/cleanup-terminal-pods.yaml @@ -1,21 +1,24 @@ apiVersion: kyverno.io/v2beta1 kind: ClusterCleanupPolicy metadata: - name: purge-terminal-pods + name: cleanup-terminal-pods spec: match: any: - resources: kinds: - Pod - schedule: "*/15 * * * *" + conditions: all: - - key: "{{ request.object.status.phase }}" + - key: "{{ target.status.phase }}" operator: AnyIn value: - Succeeded - Failed - - key: "{{ request.object.metadata.creationTimestamp }}" - operator: DurationGreaterThan + + - key: "{{ time_diff('{{ target.metadata.creationTimestamp }}','{{ time_now_utc() }}') }}" + operator: GreaterThan value: 30m + + schedule: "*/15 * * * *" diff --git a/platform/kyverno/policies/50-cleanup/kustomization.yaml b/platform/kyverno/policies/50-cleanup/kustomization.yaml new file mode 100644 index 0000000..6615d07 --- /dev/null +++ b/platform/kyverno/policies/50-cleanup/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - cleanup-evicted-pods.yaml + - cleanup-old-replicasets.yaml + - cleanup-terminal-pods.yaml + +commonLabels: + app.kubernetes.io/part-of: kyverno-policies + policy-tier: cleanup + +commonAnnotations: + argocd.argoproj.io/sync-wave: "4" diff --git a/platform/kyverno/policies/cleanup-rbac.yaml b/platform/kyverno/policies/cleanup-rbac.yaml deleted file mode 100644 index d008ca4..0000000 --- a/platform/kyverno/policies/cleanup-rbac.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: kyverno:cleanup-pods - labels: - rbac.kyverno.io/aggregate-to-cleanup-controller: "true" - annotations: - argocd.argoproj.io/sync-wave: "-1" -rules: - - apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "watch", "delete"] diff --git a/platform/kyverno/policies/kustomization.yaml b/platform/kyverno/policies/kustomization.yaml index e6819f3..fdd672f 100644 --- a/platform/kyverno/policies/kustomization.yaml +++ b/platform/kyverno/policies/kustomization.yaml @@ -2,9 +2,9 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - cleanup-rbac.yaml - - purge-terminal-pods.yaml - - disallow-latest-tag.yaml - - kyverno-cilium-rbac.yaml - - generate-ns-network-baseline.yaml - - require-requests-limits.yaml +- 00-meta +- 10-defaults +- 20-require +- 30-disallow +- 40-generate +- 50-cleanup