Intro
Security monitoring for Kubernetes is a discipline, not a product you install once and forget. Clusters are dynamic, attacks evolve, and an alert that fires without a clear runbook is just noise. This guide is written for platform engineers, DevOps consultants, and startup teams who need to move from "we saw something weird" to "we observed, verified, and recovered."
We will cover the four pillars of Kubernetes security monitoring: what to collect (metrics, logs, audit events), how to alert on it, how to visualize it, and how to respond when an alert fires. For every recommendation, you will find concrete commands, expected outputs, and decision points. The goal is operational safety: observe before changing, limit blast radius, use placeholders instead of secrets, and verify before closing an incident.
This guide assumes a Kubernetes cluster (version 1.24 or later) with kubectl access and a monitoring stack such as Prometheus and Grafana. If you are using a managed service like EKS, GKE, or AKS, most principles still apply, but some setup details will differ.
Version and Environment Inventory
Before you can monitor security, you must know what you are running. A surprising number of security incidents start with an unpatched component or a misconfigured API server flag that nobody noticed for months.
Inventory Checklist
Run these commands and record the output in your operations log or Git repository:
# Cluster version
kubectl version --short
# Node versions and OS
kubectl get nodes -o wide
# Control plane pods (if self-managed)
kubectl get pods -n kube-system -l tier=control-plane
# CNI plugin and version
kubectl get pods -n kube-system | grep -E 'calico|cilium|flannel|weave'
Expected output for a healthy 1.26 cluster might look like:
Client Version: v1.26.3
Server Version: v1.26.3
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
worker-node-1 Ready <none> 34d v1.26.3 10.0.1.5 <none> Ubuntu 22.04.2 LTS 5.15.0-1031-aws containerd://1.6.18
Why This Matters for Security Monitoring
- Version skew: A node running 1.24 while the API server runs 1.26 may miss security fixes or behave unexpectedly.
- Node OS: If you see Ubuntu 20.04 on one node and 22.04 on another, your security baselines are inconsistent.
- CNI plugin: Network policy enforcement depends on the CNI. Calico, Cilium, and others have different capabilities and logging formats.
Smallest Justified Change
If you find a version mismatch, do not upgrade everything at once. Upgrade one node, verify it joins the cluster and pods reschedule, then proceed. Document the rollback path (e.g., kubectl cordon and kubectl drain before upgrade).
Safe Configuration Path
Security monitoring configuration should follow the same principles as any infrastructure change: small, tested, and reversible.
Default Deny Network Policy
One of the highest-impact security improvements is a default deny network policy. But applying it blindly can break applications. Follow this safe path:
- Observe current traffic with a network observability tool (e.g., Cilium Hubble, Calico Enterprise, or a service mesh).
- Write a policy that allows known necessary traffic.
- Apply in audit mode first if your CNI supports it.
- Enforce the policy in a test namespace before cluster-wide.
Example default-deny policy for a namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: payments
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Then add allow rules for specific flows. For example, allow the payments-api pods to talk to the database:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-payments-to-db
namespace: payments
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
Verify with kubectl describe networkpolicy and by testing connectivity from a pod with the label and without.
API Server Audit Policy
Kubernetes audit logs are your primary source for "who did what and when." Enable audit logging with a policy file that captures security-relevant events without drowning in noise.
Example audit policy file (audit-policy.yaml):
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
verbs: ["create", "update", "patch", "delete"]
resources:
- group: ""
resources: ["secrets", "configmaps"]
- level: RequestResponse
verbs: ["create", "update", "patch"]
resources:
- group: "rbac.authorization.k8s.io"
resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"]
- level: Metadata
userGroups: ["system:authenticated"]
omitStages: ["RequestReceived"]
This policy logs metadata for secret and configmap changes, full request/response for RBAC changes, and metadata for all other authenticated requests. Adjust levels based on your risk tolerance and log storage budget.
Test the audit policy in a staging cluster first, then roll out to production with a log shipping pipeline (e.g., Fluentd to Elasticsearch or Loki).
Verification and Diagnostics
Once security monitoring is configured, you need to verify it works as expected. This section covers practical diagnostics.
Checking Prometheus Targets
If Prometheus is your metrics backend, start by confirming targets are up:
kubectl port-forward -n monitoring svc/prometheus-k8s 9090:9090
# Then open http://localhost:9090/targets
All targets should show state UP with green bars. If a target is down, check the error message and scrape logs.
Validating Alert Rules
Use promtool to check rule syntax before applying:
promtool check rules /etc/prometheus/rules/*.yaml
Expected output: Checking /etc/prometheus/rules/alerts.yaml SUCCESS for each file.
Then, in the Prometheus UI, go to Alerts and see if any rules are in inactive or pending state. You can also use the expression browser to simulate an alert condition. For example, if you have an alert for high API server request rate:
rate(apiserver_request_total{job="apiserver"}[5m]) > 100
Paste this in the expression browser and see if any series appear. If none, either the metric name is wrong or the condition is too strict.
Testing Alertmanager Routing
Create a test alert and send it to Alertmanager to confirm routing labels and receivers:
amtool alert add alertname=TestAlert severity=critical --endpoint=localhost:9093
Then check amtool alert query to see if it was processed and routed. Verify that the intended receiver (e.g., Slack, PagerDuty) received a notification.
Failure Modes and Recovery
Security monitoring itself can fail. Here are common failure modes and how to recover.
Alert Fatigue
Symptom: Too many low-priority alerts, team starts ignoring all alerts.
Recovery: Review alert rules quarterly. Remove or adjust thresholds for alerts that have not led to action in the last 90 days. Add inhibition rules so that a high-level alert (e.g., cluster down) suppresses lower-level alerts (e.g., pod down).
Missing Audit Logs
Symptom: Audit logs stop appearing in your SIEM.
Recovery: Check the audit log backend. If using a file backend, ensure the file size does not exceed rotation limits. If using a webhook backend, verify the endpoint is reachable and not rate-limiting. Run kubectl get --raw /healthz on the API server to check health.
Prometheus Disk Full
Symptom: Prometheus stops ingesting metrics, alert rules stop evaluating.
Recovery: Increase disk size or retention, or reduce scrape interval. If the disk is full, delete old blocks carefully using promtool tsdb commands or expand the volume. Set up alerts for disk usage above 80%.
Grafana Dashboard Broken
Symptom: Dashboard shows no data or errors.
Recovery: Check data source configuration in Grafana. Verify Prometheus URL and credentials. Test with a simple query like up. If the query works, the issue may be in dashboard variables or panel queries.
Operations Checklist
Use this checklist as a starting point for your security monitoring operations. Assign an owner and review cadence for each item.
| # | Item | Owner | Cadence | Verification |
|---|---|---|---|---|
| 1 | Review and update cluster version inventory | Platform lead (e.g., Priya Shah) | Monthly | kubectl version --short output matches expected matrix in runbook |
| 2 | Test default deny network policies in staging | Network engineer (e.g., Alex Chen) | After any app or policy change | Connectivity tests in staging pass without unexpected drops |
| 3 | Validate Prometheus alert rules | SRE on-call (e.g., rotating) | Weekly | promtool check rules succeeds, no firing alerts without runbook |
| 4 | Simulate a security incident (tabletop) | Security engineer (e.g., Maria Garcia) | Quarterly | Team completes response within defined SLO (e.g., 30 min to acknowledge) |
| 5 | Review audit logs for anomalies | Security analyst (e.g., John Doe) | Weekly | No unexplained RBAC changes or secret access |
| 6 | Test Alertmanager routing to all receivers | SRE lead (e.g., David Kim) | Monthly | Test alert reaches Slack, email, and PagerDuty |
| 7 | Check Grafana dashboards load and show data | Dashboard owner (e.g., Emily Brown) | Weekly | All panels render without error, data is fresh (<5 min old) |
Common Pitfalls and How to Avoid Them
Pitfall 1: Collecting Everything, Alerting on Everything
Why it happens: Fear of missing an attack leads to over-collection and over-alerting.
How to avoid: Start with the four golden signals for security: API server request rate, error rate, latency, and saturation. Add audit log collection for RBAC and secrets. Alert only on conditions that require human action. For everything else, create dashboards for later review.
Pitfall 2: Ignoring the Control Plane
Why it happens: Managed Kubernetes services hide the control plane, so teams focus only on workloads.
How to avoid: Even in managed services, you can enable audit logs and metrics for the API server (e.g., EKS control plane logging, GKE audit logging). Monitor control plane components for unusual restarts, high CPU, or certificate expiration.
Pitfall 3: Hardcoding Secrets in Monitoring Configs
Why it happens: Quick setup scripts embed credentials for Prometheus, Grafana, or Alertmanager.
How to avoid: Use Kubernetes Secrets or external secret management (e.g., Vault). Reference secrets via environment variables or mounted files. Never commit secrets to Git.
Pitfall 4: No Runbooks for Alerts
Why it happens: Teams set up alerts but never document what to do when they fire.
How to avoid: For every alert rule, write a runbook that includes: what the alert means, how to triage (commands to run), how to mitigate, and how to verify resolution. Store runbooks in the same repo as the alert definitions.
Pitfall 5: Not Testing Recovery
Why it happens: Recovery procedures are assumed to work because they were written once.
How to avoid: Schedule regular fire drills. For example, intentionally kill a node, trigger a high CPU alert, or revoke an RBAC permission, then follow the runbook and time the recovery. Record gaps and update runbooks.
Conclusion
Kubernetes security monitoring is an ongoing practice. The tools and configurations described here provide a foundation, but they must be adapted to your environment and risk profile. Start with a version and environment inventory, then implement safe configuration paths for network policies and audit logging, verify your monitoring stack, and prepare for failure modes.
Prioritize alerts that signal a real security incident over informational noise. Assign clear ownership for monitoring operations and review your setup regularly. A well-monitored cluster is not one with the most dashboards; it is one where the team can quickly detect, understand, and respond to security events.
As a next step, choose one low-risk verification from this guide, such as checking your audit log policy or testing a default deny network policy in a staging namespace. Run it, record the result, and then expand from there.