feat(kyverno): add policy engine with security baseline

- Kyverno 3.3.4 via Helm (HA config: 3 admission, 2 background replicas)
- Validation policies:
  - disallow-privileged-containers (Enforce)
  - require-resource-limits (Enforce)
  - require-labels (Audit - standard k8s labels)
  - require-run-as-non-root (Audit)
  - disallow-latest-tag (Enforce - GitOps reproducibility)
- Mutating policy:
  - add-default-securitycontext (seccomp, drop caps, read-only fs)
- System namespaces excluded (kube-system, kyverno, istio-system)
- Auto-discovered by ArgoCD ApplicationSet

Reference: CIS Kubernetes Benchmark, Pod Security Standards
This commit is contained in:
Greg Hendrickson
2026-02-09 18:02:21 +00:00
parent 124a29a0a9
commit 3752fd0386
10 changed files with 445 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
# infrastructure/kyverno/kustomization.yaml
# Kyverno Policy Engine - GitOps-native Kubernetes policy enforcement
# CNCF Graduated project, integrates seamlessly with ArgoCD
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kyverno
resources:
- namespace.yaml
- policies/
# Kyverno deployment via Helm
helmCharts:
- name: kyverno
repo: https://kyverno.github.io/kyverno/
version: "3.3.4"
releaseName: kyverno
namespace: kyverno
valuesInline:
# Admission controller replicas for HA
admissionController:
replicas: 3
resources:
limits:
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
# Background controller for generate/mutate policies
backgroundController:
replicas: 2
resources:
limits:
memory: 256Mi
requests:
cpu: 50m
memory: 128Mi
# Reports controller for policy reports
reportsController:
replicas: 2
# Cleanup controller
cleanupController:
replicas: 2
# Enable policy exception support
features:
policyExceptions:
enabled: true
namespace: "kyverno"
# Webhooks config
config:
webhooks:
# Exclude system namespaces from validation
- namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values:
- kube-system
- kube-public
- kube-node-lease
- kyverno

View File

@@ -0,0 +1,10 @@
# infrastructure/kyverno/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: kyverno
labels:
app.kubernetes.io/name: kyverno
app.kubernetes.io/component: policy-engine
# Exempt from Pod Security Standards (Kyverno needs privileges)
pod-security.kubernetes.io/enforce: privileged

View File

@@ -0,0 +1,68 @@
# infrastructure/kyverno/policies/add-default-securitycontext.yaml
# Mutating policy: adds secure defaults to pods missing securityContext
# Implements defense-in-depth by setting secure defaults
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-securitycontext
annotations:
policies.kyverno.io/title: Add Default Security Context
policies.kyverno.io/category: Best Practices
policies.kyverno.io/severity: low
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Mutating policy that adds secure default securityContext to pods
that don't specify one. Reduces attack surface by dropping
capabilities and making filesystem read-only where possible.
pod-policies.kyverno.io/autogen-controllers: DaemonSet,Deployment,Job,StatefulSet,ReplicaSet
spec:
# Mutate rules apply during admission
rules:
- name: add-pod-security-context
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
- istio-system
mutate:
patchStrategicMerge:
spec:
# Add pod-level securityContext if missing
+(securityContext):
seccompProfile:
type: RuntimeDefault
# Don't allow privilege escalation by default
runAsNonRoot: true
- name: add-container-security-context
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
- istio-system
mutate:
foreach:
- list: "request.object.spec.containers"
patchStrategicMerge:
spec:
containers:
- name: "{{ element.name }}"
+(securityContext):
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true

View File

@@ -0,0 +1,60 @@
# infrastructure/kyverno/policies/disallow-latest-tag.yaml
# Prevents use of 'latest' image tag for reproducibility
# GitOps best practice: always use explicit image versions
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
annotations:
policies.kyverno.io/title: Disallow Latest Tag
policies.kyverno.io/category: Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
The 'latest' tag is mutable and can change unexpectedly, making
deployments non-reproducible. This policy requires explicit
image tags for GitOps traceability.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: require-image-tag
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
validate:
message: "Images must use an explicit tag (not 'latest'). Specify a version tag like ':v1.2.3' or SHA digest."
pattern:
spec:
containers:
- image: "*:*"
=(initContainers):
- image: "*:*"
- name: validate-not-latest
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
validate:
message: "The 'latest' tag is not allowed. Use a specific version tag."
pattern:
spec:
containers:
- image: "!*:latest"
=(initContainers):
- image: "!*:latest"

View File

@@ -0,0 +1,47 @@
# infrastructure/kyverno/policies/disallow-privileged.yaml
# Prevents pods from running as privileged containers
# Security baseline: CIS Benchmark 5.2.1
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 Standards (Baseline)
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Privileged containers have all Linux capabilities and can access
host resources. This policy prevents privileged containers from
being created except in system namespaces.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: privileged-containers
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
- istio-system
- cert-manager
validate:
message: "Privileged containers are not allowed. Set securityContext.privileged to false."
pattern:
spec:
containers:
- securityContext:
privileged: "false"
=(initContainers):
- securityContext:
privileged: "false"
=(ephemeralContainers):
- securityContext:
privileged: "false"

View File

@@ -0,0 +1,12 @@
# infrastructure/kyverno/policies/kustomization.yaml
# Security policies for cluster-wide enforcement
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- disallow-privileged.yaml
- require-resource-limits.yaml
- require-labels.yaml
- require-non-root.yaml
- disallow-latest-tag.yaml
- add-default-securitycontext.yaml

View File

@@ -0,0 +1,69 @@
# infrastructure/kyverno/policies/require-labels.yaml
# Enforces standard labeling for all workloads
# Enables proper resource organization and cost tracking
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-labels
annotations:
policies.kyverno.io/title: Require Labels
policies.kyverno.io/category: Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod, Deployment, StatefulSet, DaemonSet
policies.kyverno.io/description: >-
Labels are essential for organizing, filtering, and managing
Kubernetes resources. This policy requires standard labels on
all workloads.
spec:
validationFailureAction: Audit # Start in audit mode
background: true
rules:
- name: check-deployment-labels
match:
any:
- resources:
kinds:
- Deployment
- StatefulSet
- DaemonSet
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
validate:
message: "Workloads must have 'app.kubernetes.io/name' and 'app.kubernetes.io/part-of' labels."
pattern:
metadata:
labels:
app.kubernetes.io/name: "?*"
app.kubernetes.io/part-of: "?*"
- name: check-pod-labels
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
# Exclude pods created by controllers (they inherit parent labels)
selector:
matchLabels:
app.kubernetes.io/managed-by: "*"
preconditions:
all:
# Only check standalone pods (not owned by ReplicaSet, etc.)
- key: "{{ request.object.metadata.ownerReferences[] || `[]` | length(@) }}"
operator: Equals
value: 0
validate:
message: "Pods must have 'app.kubernetes.io/name' label."
pattern:
metadata:
labels:
app.kubernetes.io/name: "?*"

View File

@@ -0,0 +1,54 @@
# infrastructure/kyverno/policies/require-non-root.yaml
# Requires containers to run as non-root user
# Security baseline: CIS Benchmark 5.2.6
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-run-as-non-root
annotations:
policies.kyverno.io/title: Require Run As Non-Root
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Running as root inside a container is a security risk. If a
container breakout occurs, root in the container could become
root on the host. This policy requires containers to run as
a non-root user.
spec:
validationFailureAction: Audit # Audit first, Enforce after baseline
background: true
rules:
- name: run-as-non-root
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
- istio-system
validate:
message: "Containers must run as non-root. Set runAsNonRoot: true or specify a non-root runAsUser."
anyPattern:
# Pattern 1: Pod-level runAsNonRoot
- spec:
securityContext:
runAsNonRoot: true
# Pattern 2: Container-level runAsNonRoot
- spec:
containers:
- securityContext:
runAsNonRoot: true
# Pattern 3: Explicit non-root UID (>= 1000)
- spec:
securityContext:
runAsUser: ">999"
- spec:
containers:
- securityContext:
runAsUser: ">999"

View File

@@ -0,0 +1,41 @@
# infrastructure/kyverno/policies/require-resource-limits.yaml
# Ensures all pods have resource limits defined
# Prevents resource exhaustion and enables proper scheduling
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
annotations:
policies.kyverno.io/title: Require Resource Limits
policies.kyverno.io/category: Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Resource limits prevent a single workload from consuming excessive
cluster resources. This policy requires all containers to define
CPU and memory limits.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: validate-resources
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
validate:
message: "CPU and memory limits are required. Add resources.limits.cpu and resources.limits.memory."
pattern:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"