This guide demonstrates how to configure Kyverno to enforce proper security contexts for containers, ensuring they run with appropriate security settings and restrictions.
Security context enforcement involves controlling how containers run by setting security-related parameters. Proper security context configuration prevents:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-run-as-nonroot
annotations:
policies.kyverno.io/title: Require Run As Non-Root User
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Containers must run as a non-root user. This policy ensures runAsNonRoot is set to true.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: run-as-non-root
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Running as root is not allowed. Either the field spec.securityContext.runAsNonRoot
must be set to true, or the field spec.containers[*].securityContext.runAsNonRoot
must be set to true.
anyPattern:
- spec:
securityContext:
runAsNonRoot: "true"
- spec:
containers:
- securityContext:
runAsNonRoot: "true"
# Apply the policy
kubectl apply -f require-run-as-nonroot.yaml
# Try to create a container explicitly running as root (should fail)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-root
spec:
containers:
- name: nginx
image: nginx
securityContext:
runAsUser: 0
runAsNonRoot: false
EOF
# Try to create a container with non-root user (should work)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-nonroot
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: nginx
image: nginx
EOF
# Clean up
kubectl delete pod test-root test-nonroot --ignore-not-found
Prevent containers from escalating privileges:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privilege-escalation
annotations:
policies.kyverno.io/title: Disallow Privilege Escalation
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Privilege escalation, such as via set-user-ID or set-group-ID file mode, should not be allowed.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: privilege-escalation
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Privilege escalation is disallowed. The fields
spec.containers[*].securityContext.allowPrivilegeEscalation,
spec.initContainers[*].securityContext.allowPrivilegeEscalation,
and spec.ephemeralContainers[*].securityContext.allowPrivilegeEscalation
must be set to false.
pattern:
spec:
=(ephemeralContainers):
- securityContext:
allowPrivilegeEscalation: "false"
=(initContainers):
- securityContext:
allowPrivilegeEscalation: "false"
containers:
- securityContext:
allowPrivilegeEscalation: "false"
Ensure containers run with specific user IDs:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-user-id-range
annotations:
policies.kyverno.io/title: Require User ID Range
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Containers must run with a specific user ID range to prevent privilege escalation.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: user-id-range
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Containers must run with user ID between 1000 and 65535.
deny:
conditions:
any:
# Check pod-level security context
- key: "{{ request.object.spec.securityContext.runAsUser || 0 }}"
operator: LessThan
value: 1000
- key: "{{ request.object.spec.securityContext.runAsUser || 0 }}"
operator: GreaterThan
value: 65535
# Check container-level security contexts
- key: "{{ request.object.spec.containers[?securityContext.runAsUser && (securityContext.runAsUser < `1000` || securityContext.runAsUser > `65535`)] | length(@) }}"
operator: GreaterThan
value: 0
Ensure containers run with non-root group IDs:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-non-root-groups
annotations:
policies.kyverno.io/title: Require Non-Root Groups
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Containers should be required to run with a non-root group ID or supplemental groups.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: non-root-groups
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Containers must run with non-root group ID. Either spec.securityContext.runAsGroup
or spec.containers[*].securityContext.runAsGroup must be set and not be 0.
deny:
conditions:
any:
# Check if pod-level runAsGroup is 0
- key: "{{ request.object.spec.securityContext.runAsGroup || 0 }}"
operator: Equals
value: 0
# Check if any container has runAsGroup set to 0
- key: "{{ request.object.spec.containers[?securityContext.runAsGroup == `0`] | length(@) }}"
operator: GreaterThan
value: 0
Enforce secure seccomp profiles:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-seccomp-strict
annotations:
policies.kyverno.io/title: Restrict Seccomp (Strict)
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Seccomp profile must be explicitly set to one of the allowed values.
Both the Unconfined profile and the absence of a profile are prohibited.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: seccomp-strict
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Use of custom Seccomp profiles is disallowed. The field
spec.securityContext.seccompProfile.type must be set to RuntimeDefault or Localhost.
anyPattern:
- spec:
securityContext:
seccompProfile:
type: RuntimeDefault
- spec:
securityContext:
seccompProfile:
type: Localhost
- spec:
containers:
- securityContext:
seccompProfile:
type: RuntimeDefault
- spec:
containers:
- securityContext:
seccompProfile:
type: Localhost
Ensure containers drop all capabilities:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-drop-all-capabilities
annotations:
policies.kyverno.io/title: Require Drop ALL Capabilities
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Containers must drop all capabilities and only add back those that are specifically needed.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: require-drop-all
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Containers must drop ALL capabilities.
foreach:
- list: request.object.spec.[ephemeralContainers, initContainers, containers][]
deny:
conditions:
all:
- key: ALL
operator: AnyNotIn
value: "{{ element.securityContext.capabilities.drop || `[]` }}"
Control AppArmor profile usage:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-apparmor-profiles
annotations:
policies.kyverno.io/title: Restrict AppArmor Profiles
policies.kyverno.io/category: Pod Security Standards (Baseline)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
On supported hosts, the runtime/default AppArmor profile is applied by default.
The baseline policy should prevent overriding or disabling the default AppArmor profile.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: apparmor-profiles
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
AppArmor profile must be set to runtime/default or a custom profile.
Unconfined profiles are not allowed.
pattern:
metadata:
=(annotations):
=(container.apparmor.security.beta.kubernetes.io/*): "!unconfined"
Different security requirements for different environments:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: environment-security-contexts
spec:
validationFailureAction: Enforce
background: true
rules:
# Production: Strict security contexts
- name: production-strict-security
match:
any:
- resources:
kinds:
- Pod
namespaces:
- production
- prod-*
validate:
message: "Production environments require strict security contexts"
pattern:
spec:
securityContext:
runAsNonRoot: "true"
runAsUser: "1000-65535"
runAsGroup: "1000-65535"
seccompProfile:
type: RuntimeDefault
containers:
- securityContext:
allowPrivilegeEscalation: "false"
readOnlyRootFilesystem: "true"
runAsNonRoot: "true"
capabilities:
drop:
- ALL
# Development: Basic security requirements
- name: development-basic-security
match:
any:
- resources:
kinds:
- Pod
namespaces:
- development
- dev-*
- staging
validate:
message: "Development environments require basic security contexts"
pattern:
spec:
containers:
- securityContext:
allowPrivilegeEscalation: "false"
runAsNonRoot: "true"
Different security contexts for different application types:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: application-security-contexts
spec:
validationFailureAction: Enforce
background: true
rules:
# Database applications: Specific user/group IDs
- name: database-security-context
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
app.type: database
validate:
message: "Database applications must use specific security contexts"
pattern:
spec:
securityContext:
runAsUser: "999"
runAsGroup: "999"
fsGroup: "999"
containers:
- securityContext:
runAsNonRoot: "true"
readOnlyRootFilesystem: "true"
# Web applications: Standard security context
- name: web-app-security-context
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
app.type: web
validate:
message: "Web applications must use standard security contexts"
pattern:
spec:
containers:
- securityContext:
runAsNonRoot: "true"
allowPrivilegeEscalation: "false"
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
Implement progressive security context requirements:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: graduated-security-contexts
spec:
validationFailureAction: Enforce
background: true
rules:
# Level 1: Basic security (all namespaces)
- name: basic-security-level
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
validate:
message: "All containers must have basic security contexts"
pattern:
spec:
containers:
- securityContext:
allowPrivilegeEscalation: "false"
# Level 2: Enhanced security (sensitive namespaces)
- name: enhanced-security-level
match:
any:
- resources:
kinds:
- Pod
namespaces:
- finance-*
- hr-*
- security-*
validate:
message: "Sensitive namespaces require enhanced security contexts"
pattern:
spec:
securityContext:
runAsNonRoot: "true"
containers:
- securityContext:
readOnlyRootFilesystem: "true"
capabilities:
drop:
- ALL
# Level 3: Maximum security (critical namespaces)
- name: maximum-security-level
match:
any:
- resources:
kinds:
- Pod
namespaces:
- critical-*
- payment-*
validate:
message: "Critical namespaces require maximum security contexts"
pattern:
spec:
securityContext:
runAsNonRoot: "true"
runAsUser: "1000-1999"
runAsGroup: "1000-1999"
seccompProfile:
type: RuntimeDefault
containers:
- securityContext:
allowPrivilegeEscalation: "false"
readOnlyRootFilesystem: "true"
runAsNonRoot: "true"
capabilities:
drop:
- ALL
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-root-user
spec:
containers:
- name: test
image: nginx
securityContext:
runAsUser: 0
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-privilege-escalation
spec:
containers:
- name: test
image: nginx
securityContext:
allowPrivilegeEscalation: true
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-missing-drop-all
spec:
containers:
- name: test
image: nginx
securityContext:
capabilities:
add:
- NET_ADMIN
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-secure-context
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: test
image: nginx
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
EOF