Skip to content

AKS Platform: Security Policy

This page describes the guardrails that protect the cluster and the workloads on it: Azure Policy for Kubernetes, the Kyverno policies, network policies, namespace quotas, image rules and the exception process. Workload teams meet these rules every time they deploy.

Reference design, your deployment may differ

The Azure-side assignments are read from Terraform. The in-cluster policies (Kyverno, network policies, quotas) come from the manifests that were migrated to the platform GitOps repository; the copies left in the Terraform repository are deprecated and are used here as the reference for their content. Effects (Audit or Enforce) change per environment and over time. Ask your platform team for the current state.

Defence in depth

Stage Control
Azure Azure Policy assignments on the resource group, Defender for Cloud (optional), diagnostic settings, alerts
Cluster Private API server, Entra ID only, managed identities, hardened node pools, upgrade strategy
Admission Azure Policy add-on (Gatekeeper) for Microsoft's built-in initiatives, Kyverno for platform-defined validation and mutation
Runtime Network policies, workload identity, Key Vault, TLS at the gateway

Azure Policy and Defender

The Azure Policy layer assigns these built-in definitions to a resource group (see the policy scope remark in Architecture):

Assignment Effect in the reference configuration
Kubernetes cluster pod security baseline standards for Linux workloads Variable azure_policy_baseline_effect, Audit in both environments. The comment says to move to Deny after a validation period.
Pod security restricted standards for Linux workloads (production only) Audit
Kubernetes clusters should not allow container privilege escalation Deny
Kubernetes clusters should be accessible only over HTTPS Deny
Containers should not share host process ID or IPC namespace Deny
Custom assignments A map in the configuration, empty by default (for example "AKS must use a private API server")

Namespaces excluded from these assignments in the reference files: kube-system, gatekeeper-system, azure-arc, kyverno, gateway-system, monitoring.

The design says the baseline is enforced in production and audited in non-production. The configuration files set Audit in both, so check the live effect before relying on it.

Optional switches of the layer, all off in the reference files:

Switch What it does
deploy_defender Defender for Cloud plans Containers, KeyVaults and CloudPosture at subscription level, and a security contact
deploy_diagnostic_settings Diagnostic settings for the listed resources to a Log Analytics workspace
deploy_security_alerts An action group with email receivers and scheduled query alert rules on Log Analytics

Defender for Containers is described as unsupported on Azure Local (AKS on Azure Local).

Kyverno policies

Kyverno is the recommended policy engine for admission rules: YAML instead of Rego, validation, mutation and generation in one tool, and policy reports as Kubernetes resources. It is deployed by the GitOps repository and is optional. Until it is deployed, Azure Policy gives the baseline pod security.

The policy lifecycle is draft (pull request, reviewed by platform and security), then audit in non-production, then enforce in non-production after 7 days without new violations, then enforce in production after 14 days stable. All promotions are pull requests.

Pod security and image controls

Control Non-production Production Engine
Block privileged containers Enforce Enforce Azure Policy and Kyverno
Block hostPath, hostNetwork, hostPID, hostIPC Enforce Enforce Azure Policy and Kyverno
Block privilege escalation Enforce Enforce Azure Policy and Kyverno
Require resource requests and limits Enforce Enforce Kyverno
Require approved registries Enforce Enforce Kyverno
Require runAsNonRoot Audit Enforce Kyverno
Drop all capabilities Audit Enforce Kyverno
Require seccomp RuntimeDefault Audit Enforce Kyverno
Block the latest tag Audit Enforce Kyverno
Read-only root file system Audit Audit Kyverno

The table is the design. The Kyverno manifests in the repository all carry validationFailureAction: Audit; the production and non-production difference is meant to be set by environment values in the GitOps repository.

Approved registries are the organisation's container registry (*.azurecr.io) and mcr.microsoft.com.

Kyverno can also mutate pods so teams do not have to set secure defaults: runAsNonRoot, no privilege escalation, seccomp RuntimeDefault, drop all capabilities. The repository contains mutation policies for non-root, dropped capabilities and seccomp.

Image rules (design)

Requirement Non-production Production
Images only from approved registries Enforce Enforce
Vulnerability scan, critical and high Warn Block (high unless accepted)
Image signing and SBOM Not required Recommended, future

Images are pulled by the kubelet identity with the AcrPull role, so no pull secrets are stored in the cluster.

Network policies

Every application namespace gets default deny for ingress and egress, and then explicit allows from the base set:

Policy Effect
default-deny-all Blocks all ingress and egress
allow-dns Egress to kube-dns
allow-ingress-from-gateway Ingress from the gateway namespace to application pods
allow-egress-to-private-endpoints Egress to the private endpoint subnet on ports 443, 5432 and 6379
allow-prometheus-scrape Ingress from the Prometheus pods in the monitoring namespace on ports 9090, 8080, 9394

Teams add their own policies for calls between namespaces and for specific egress. NetworkPolicy controls east-west traffic inside the cluster; a firewall (when present) controls north-south traffic and traffic between spokes.

Note

The private endpoint policy contains the private endpoint subnet CIDR of the cluster, so it differs per environment. The load test adds its own policies for the load-testing namespace and for the target ports (Load testing).

Namespace baseline

Each team namespace gets a resource quota and a limit range from the GitOps repository.

Setting Non-production overlay Production overlay
Requests CPU / memory 8 / 16 GiB 2 / 4 GiB
Limits CPU / memory 16 / 32 GiB 4 / 8 GiB
Pods 100 30
Base defaults 4 CPU / 8 GiB requests, 8 CPU / 16 GiB limits, 50 pods, 10 services, 10 persistent volume claims, 20 secrets, 20 config maps

Container defaults from the limit range: request 100m CPU and 128 MiB, limit 500m CPU and 512 MiB; minimum 50m and 64 MiB, maximum 4 CPU and 8 GiB per container, 8 CPU and 16 GiB per pod.

Check the production overlay

In the deprecated overlays the production quota is smaller than the non-production quota. This may be intended (small initial namespaces) or a swapped value. The values in the GitOps repository apply.

Namespaces are created only through GitOps; creating one with kubectl is blocked by admission policy for non-platform subjects (design). Each new namespace is provisioned with the quota, the limit range, the default-deny network policy and the role bindings. The design makes these labels mandatory:

Label Purpose
<labelDomain>/team Team ownership
<labelDomain>/environment dev, test, accp or prod
<labelDomain>/type application or platform
<labelDomain>/data-classification public, internal, confidential or restricted
<labelDomain>/security-tier standard or high
<labelDomain>/cost-center Cost tracking

An owner contact and the way the namespace was created are mandatory annotations. The Kyverno policy in the repository checks only the team and environment labels, so the exact mandatory set depends on the version of the GitOps repository.

Secrets in the cluster

Rule Enforcement (design)
No plaintext secrets in Git Pre-commit scanning and server-side push protection
Prefer Key Vault over Kubernetes Secrets Guidance
No secrets in config maps Kyverno, audit
Only Opaque and TLS secret types Kyverno, audit

Rotation is handled by Key Vault rotation policies and the application reads the latest value with the SDK.

Policy exceptions

An exception exempts one workload from one policy, for a limited time.

  1. The team opens a pull request in the platform GitOps repository that adds a PolicyException (a list in the environment values). The description gives the justification.
  2. The security team reviews and approves. The platform team merges and Argo CD deploys it.
  3. Quarterly review: renew with justification, or remove.
Rule Detail
Expiry Every exception has an expiry date, 90 days at most, renewable with re-approval
Scope One namespace and workload, never cluster-wide
Metadata Owner, approver, reason, risk, compensating controls, ticket reference
Tracking Policy reports show which workloads run under an exception

See also Policies and exemptions for the Azure Policy exemptions of the MCP platform itself.