294 lines
8.6 KiB
HCL
294 lines
8.6 KiB
HCL
# =============================================================================
|
|
# Pod Security Policies (Audit Mode)
|
|
# =============================================================================
|
|
# Kyverno validate policies for pod security standards.
|
|
# All policies start in Audit mode - violations are logged but not blocked.
|
|
|
|
resource "kubernetes_manifest" "policy_deny_privileged" {
|
|
manifest = {
|
|
apiVersion = "kyverno.io/v1"
|
|
kind = "ClusterPolicy"
|
|
metadata = {
|
|
name = "deny-privileged-containers"
|
|
annotations = {
|
|
"policies.kyverno.io/title" = "Deny Privileged Containers"
|
|
"policies.kyverno.io/category" = "Pod Security"
|
|
"policies.kyverno.io/severity" = "high"
|
|
"policies.kyverno.io/description" = "Privileged containers have full host access. Deny unless explicitly exempted."
|
|
}
|
|
}
|
|
spec = {
|
|
validationFailureAction = "Audit"
|
|
background = true
|
|
rules = [{
|
|
name = "deny-privileged"
|
|
match = {
|
|
any = [{
|
|
resources = {
|
|
kinds = ["Pod"]
|
|
}
|
|
}]
|
|
}
|
|
exclude = {
|
|
any = [{
|
|
resources = {
|
|
namespaces = ["frigate", "nvidia", "monitoring"]
|
|
}
|
|
}]
|
|
}
|
|
validate = {
|
|
message = "Privileged containers are not allowed. Use specific capabilities instead."
|
|
pattern = {
|
|
spec = {
|
|
containers = [{
|
|
"=(securityContext)" = {
|
|
"=(privileged)" = false
|
|
}
|
|
}]
|
|
"=(initContainers)" = [{
|
|
"=(securityContext)" = {
|
|
"=(privileged)" = false
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
|
|
depends_on = [helm_release.kyverno]
|
|
}
|
|
|
|
resource "kubernetes_manifest" "policy_deny_host_namespaces" {
|
|
manifest = {
|
|
apiVersion = "kyverno.io/v1"
|
|
kind = "ClusterPolicy"
|
|
metadata = {
|
|
name = "deny-host-namespaces"
|
|
annotations = {
|
|
"policies.kyverno.io/title" = "Deny Host Namespaces"
|
|
"policies.kyverno.io/category" = "Pod Security"
|
|
"policies.kyverno.io/severity" = "high"
|
|
"policies.kyverno.io/description" = "Sharing host namespaces enables container escapes. Deny hostNetwork, hostPID, hostIPC."
|
|
}
|
|
}
|
|
spec = {
|
|
validationFailureAction = "Audit"
|
|
background = true
|
|
rules = [{
|
|
name = "deny-host-namespaces"
|
|
match = {
|
|
any = [{
|
|
resources = {
|
|
kinds = ["Pod"]
|
|
}
|
|
}]
|
|
}
|
|
exclude = {
|
|
any = [{
|
|
resources = {
|
|
namespaces = ["frigate", "monitoring"]
|
|
}
|
|
}]
|
|
}
|
|
validate = {
|
|
message = "Host namespaces (hostNetwork, hostPID, hostIPC) are not allowed."
|
|
pattern = {
|
|
spec = {
|
|
"=(hostNetwork)" = false
|
|
"=(hostPID)" = false
|
|
"=(hostIPC)" = false
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
|
|
depends_on = [helm_release.kyverno]
|
|
}
|
|
|
|
resource "kubernetes_manifest" "policy_restrict_capabilities" {
|
|
manifest = {
|
|
apiVersion = "kyverno.io/v1"
|
|
kind = "ClusterPolicy"
|
|
metadata = {
|
|
name = "restrict-sys-admin"
|
|
annotations = {
|
|
"policies.kyverno.io/title" = "Restrict SYS_ADMIN Capability"
|
|
"policies.kyverno.io/category" = "Pod Security"
|
|
"policies.kyverno.io/severity" = "high"
|
|
"policies.kyverno.io/description" = "SYS_ADMIN is nearly equivalent to root. Restrict to explicitly exempted namespaces."
|
|
}
|
|
}
|
|
spec = {
|
|
validationFailureAction = "Audit"
|
|
background = true
|
|
rules = [{
|
|
name = "restrict-sys-admin"
|
|
match = {
|
|
any = [{
|
|
resources = {
|
|
kinds = ["Pod"]
|
|
}
|
|
}]
|
|
}
|
|
exclude = {
|
|
any = [{
|
|
resources = {
|
|
namespaces = ["nvidia", "monitoring"]
|
|
}
|
|
}]
|
|
}
|
|
validate = {
|
|
message = "Adding SYS_ADMIN capability is not allowed."
|
|
deny = {
|
|
conditions = {
|
|
any = [{
|
|
key = "{{ request.object.spec.containers[].securityContext.capabilities.add[] || `[]` }}"
|
|
operator = "AnyIn"
|
|
value = ["SYS_ADMIN"]
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
|
|
depends_on = [helm_release.kyverno]
|
|
}
|
|
|
|
# =============================================================================
|
|
# Image Pull Policy Governance
|
|
# =============================================================================
|
|
# Mutate imagePullPolicy to IfNotPresent for all containers with pinned tags
|
|
# (non-:latest). This prevents pods from getting stuck in ImagePullBackOff
|
|
# when the pull-through cache at 10.0.20.10 has transient failures.
|
|
# For :latest or untagged images, set to Always so stale images don't persist.
|
|
|
|
resource "kubernetes_manifest" "policy_set_image_pull_policy" {
|
|
manifest = {
|
|
apiVersion = "kyverno.io/v1"
|
|
kind = "ClusterPolicy"
|
|
metadata = {
|
|
name = "set-image-pull-policy"
|
|
annotations = {
|
|
"policies.kyverno.io/title" = "Set Image Pull Policy"
|
|
"policies.kyverno.io/category" = "Best Practices"
|
|
"policies.kyverno.io/severity" = "medium"
|
|
"policies.kyverno.io/description" = "Set imagePullPolicy to IfNotPresent for pinned tags and Always for :latest to prevent ImagePullBackOff from transient cache failures."
|
|
}
|
|
}
|
|
spec = {
|
|
background = false
|
|
rules = [
|
|
{
|
|
name = "set-ifnotpresent-for-pinned-tags"
|
|
match = {
|
|
any = [{
|
|
resources = {
|
|
kinds = ["Pod"]
|
|
}
|
|
}]
|
|
}
|
|
mutate = {
|
|
foreach = [{
|
|
list = "request.object.spec.containers"
|
|
preconditions = {
|
|
all = [{
|
|
key = "{{ ends_with(element.image, ':latest') || !contains(element.image, ':') }}"
|
|
operator = "Equals"
|
|
value = false
|
|
}]
|
|
}
|
|
patchStrategicMerge = {
|
|
spec = {
|
|
containers = [{
|
|
name = "{{ element.name }}"
|
|
imagePullPolicy = "IfNotPresent"
|
|
}]
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
},
|
|
{
|
|
name = "set-always-for-latest"
|
|
match = {
|
|
any = [{
|
|
resources = {
|
|
kinds = ["Pod"]
|
|
}
|
|
}]
|
|
}
|
|
mutate = {
|
|
foreach = [{
|
|
list = "request.object.spec.containers"
|
|
preconditions = {
|
|
all = [{
|
|
key = "{{ ends_with(element.image, ':latest') || !contains(element.image, ':') }}"
|
|
operator = "Equals"
|
|
value = true
|
|
}]
|
|
}
|
|
patchStrategicMerge = {
|
|
spec = {
|
|
containers = [{
|
|
name = "{{ element.name }}"
|
|
imagePullPolicy = "Always"
|
|
}]
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
depends_on = [helm_release.kyverno]
|
|
}
|
|
|
|
resource "kubernetes_manifest" "policy_require_trusted_registries" {
|
|
manifest = {
|
|
apiVersion = "kyverno.io/v1"
|
|
kind = "ClusterPolicy"
|
|
metadata = {
|
|
name = "require-trusted-registries"
|
|
annotations = {
|
|
"policies.kyverno.io/title" = "Require Trusted Image Registries"
|
|
"policies.kyverno.io/category" = "Pod Security"
|
|
"policies.kyverno.io/severity" = "medium"
|
|
"policies.kyverno.io/description" = "Images must come from trusted registries to prevent supply chain attacks."
|
|
}
|
|
}
|
|
spec = {
|
|
validationFailureAction = "Audit"
|
|
background = true
|
|
rules = [{
|
|
name = "validate-registries"
|
|
match = {
|
|
any = [{
|
|
resources = {
|
|
kinds = ["Pod"]
|
|
}
|
|
}]
|
|
}
|
|
validate = {
|
|
message = "Images must be from trusted registries (docker.io, ghcr.io, quay.io, registry.k8s.io, or local cache)."
|
|
pattern = {
|
|
spec = {
|
|
containers = [{
|
|
image = "docker.io/* | ghcr.io/* | quay.io/* | registry.k8s.io/* | 10.0.20.10* | */*"
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
|
|
depends_on = [helm_release.kyverno]
|
|
}
|