Skip to content
Back to skills

K8s Audit

ASecurity

Live, read-only audit of a running cluster on a context the user confirmed: API removals, CIS posture, runtime vs declared, utilisation.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 20, 2026
ai-agentsbashnodekubernetesgitapibackenddevopssecurity

Works with

  • api

Security analysis

A100/100

Scanned October 20, 2026

npx -y skills add adnanmokhtar/refract --skill k8s-audit --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of K8s Audit?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for K8s Audit
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/adnanmokhtar-k8s-audit/badge)](https://www.skillsdirectory.com/skills/adnanmokhtar-k8s-audit)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: k8s-audit
description: "Live, read-only audit of a running cluster on a context the user confirmed: API removals, CIS posture, runtime vs declared, utilisation."
kind: skill
pack: infrastructure
allowed-tools: [Read, Grep, Glob, Bash]
---

# k8s-audit

## Boundary — this skill audits the CLUSTER, not the YAML

Everything decidable from the repo is decided offline on the render: `k8s-render-scan` (render, Helm, Kustomize, GitOps spec), `k8s-availability-scan` (CPU request + memory request + memory limit, probes, PDBs, HPA/KEDA floors, spread, rollout, shutdown), `k8s-wiring-scan` (Services, config propagation, NetworkPolicy paths, Jobs, RBAC wiring) and `k8s-api-version-scan` (removed APIs in files, schema, admission), judged by `@k8s-reviewer`. Pod privilege, RBAC breadth and secrets in manifests are security `iac-scan` § 3's when the security pack is installed.

**Do not restate those here.** If a finding can be made from the files, hand it to its detector. What is left needs the live cluster:

| Axis | Why only the cluster can answer it |
|---|---|
| 1. API removals in what is deployed | Helm release manifests stored in the cluster and objects applied by hand are not in the repo |
| 2. CIS / node posture | kubelet flags, etcd, API-server config — none of it is in the repo |
| 3. Runtime vs declared | restarts, OOMKills, pending pods, out-of-band drift, `disruptionsAllowed`, expiring certs |
| 4. Live utilisation | requests are declared; *usage* is measured |

## Premise

Every finding cites the tool-output line it fired on: the pluto row, the failed `kube-bench` control id, the `kubectl top` figure beside the declared request, the `kubectl get` query and its output, **and the context it was read from**. No cite → no finding. "Looks over-provisioned" is a vibe; `requests.cpu: 2 / p95 usage 0.08 cores over 7d` is a finding.

## Halt conditions

- **Context halt (2026-10-10 rule).** Never read the default or current context. Before any live command, name the cluster context (and kubeconfig file) and run only after the user confirms that exact context **in this session**. Every command passes it explicitly: `kubectl --context=<ctx>`, `pluto … --kube-context=<ctx>`, `helm --kube-context <ctx>`. A command that takes no context flag runs with `KUBECONFIG=<the file the user named>`. No confirmed context → every axis prints `NO-DATA(<context>)`, never a pass.
- **Read-only verbs only:** `get`, `describe`, `top`, `events`, `auth can-i`, `version`. Never `apply`, `delete`, `patch`, `edit`, `drain`, `cordon`, `scale`, `rollout restart`, `exec`, `port-forward`, `helm install/upgrade/rollback`, or any `--dry-run=server` against the user's cluster (a dry-run belongs on a scratch kind cluster, precedents § 56).
- **Never run `kubent`.** It is stale (0.7.3, 2024-08), contacts the ambient context when run bare, and exits 0 with findings (precedents § 55). pluto replaces it.
- **Never loop over binaries with `version`**: some ignore their arguments and list the ambient cluster.
- Halt on any finding that cannot name the tool line or parsed field it fired on, or the context it came from.
- Halt on a utilisation claim without BOTH numbers — the declared request AND the measured usage, with the window.
- Halt on a deprecation finding without the cluster's current minor AND the removal minor from the deprecation guide.
- Halt on anything a static detector owns (see Boundary) — hand it over instead of restating it.
- Do not propose auto-fix against a running cluster. Report; humans decide.
- High or critical rows pass through [`refute-before-report.md`](../../../../snippets/refute-before-report.md) with [`precedents/infrastructure.md`](../../../../snippets/precedents/infrastructure.md) before they are reported as confirmed; under `/infra-audit --live=<context>` the judge is `@k8s-reviewer`.

## Tools

| Axis | Tool (always with the confirmed context) | The citation it produces |
|---|---|---|
| API removals | `pluto detect-helm --kube-context=<ctx> -o json --target-versions k8s=v<next minor>`; `pluto detect-api-resources --kube-context=<ctx> …` | release or resource + apiVersion + removal minor |
| CIS posture | `kube-bench` (a Job the platform team runs on worker nodes; the skill reads its output, it never schedules it) | the failed control id + its remediation text |
| Runtime | `kubectl --context=<ctx> get`/`describe`/`get events`, the GitOps controller's sync status | the field or event line |
| Utilisation + cost | `kubectl --context=<ctx> top` (metrics-server), OpenCost / Kubecost | the measured series beside the declared request |

pluto's default target minor is stale: always pass `--target-versions` with the minor read from the cluster (precedents § 54). If a tool is not installed, say so and mark that axis **UNVERIFIED**. Never infer an axis you did not measure.

## Checks

### 1. API versions the cluster is about to stop serving

- Record the control-plane minor first (`kubectl --context=<ctx> version -o json`) — every finding is relative to it.
- Kubernetes maintains **only the three most recent minors** (https://kubernetes.io/releases/). A cluster outside that window is a finding on its own, at the top of the report. Dated support facts are re-read at audit time (precedents § 62).
- Run pluto on the Helm releases and live API resources. Report each hit as: `<resource> · <apiVersion> · removed in <minor> · your next upgrade target is <minor>`, citing the deprecation guide (https://kubernetes.io/docs/reference/using-api/deprecation-guide/), never memory.
- What the repo renders is `k8s-api-version-scan`'s; report only what is deployed and absent from the render (a Helm release from a runbook, an object applied by hand).
- Include CRDs and third-party controllers, each against its own supported-minor matrix.

### 2. Node + control-plane posture (CIS)

- `kube-bench` output for the node roles the cluster runs (managed control planes are the provider's — scope to worker nodes and say so).
- Report failed control ids with their remediation text; suppress the provider-owned ones, naming which.

### 3. Runtime reality vs declared intent

- **Crash/restart pressure** — non-zero restart counts and the last termination reason (`OOMKilled` is a memory-limit or request problem; `Error` is the app's).
- **Unschedulable work** — `Pending` pods and the scheduler's reason from the event line. A `Pending` pod is an HA claim that is not true.
- **Out-of-band drift** — a live spec that differs from git, or an object no GitOps `Application`/`Kustomization` owns. HPA- or KEDA-driven `replicas` drift is expected; a changed image or resource block is not. Reconciliation drift is devops `gitops-audit`'s when that pack is installed.
- **Drain blockers** — PDBs with `status.disruptionsAllowed: 0` now. The static PDB arithmetic is `k8s-availability-scan` § 3; this is the cluster's answer today.
- **Certificate expiry** — cert-manager `Certificate` objects whose last renewal failed or that are near expiry. The inventory and signal are `dns-tls-lifecycle-scan`'s.
- **Node conditions** — `MemoryPressure` / `DiskPressure` / `PIDPressure` on any node, and the workloads evicted next.

### 4. Live utilisation

- **Over-provisioned requests** — requests vs p95 usage. The audit's default threshold: usage below one quarter of request, sustained over at least seven days; state it in the report.
- **Under-provisioned requests** — usage at or above request. A missing CPU **limit** is not a finding (precedents § 1); a CPU limit that throttles a served workload is.
- **Unattached PVCs** — bound to no pod, with size; a price only from a bill line or a dated pricing page (`/cost-audit`).
- **Idle Deployments** — zero request rate over the window, from the metrics backend.

## Output

```
K8s audit — context <ctx> (confirmed in session) · control plane v<minor> (<in / OUT of> the supported window)

HIGH:
  ✗ helm release <name>: <kind> <apiVersion> removed in v<minor> (pluto detect-helm, target v<next>) — the upgrade fails on it
  ✗ kube-bench control <id> FAIL on worker nodes — <remediation summary>
  ✗ Deployment <name>: <n> restarts, last reason OOMKilled, memory limit <x> vs peak <y>
MEDIUM:
  ⚠ PDB <name>: status.disruptionsAllowed=0 — no node holding these pods can be drained today
  ⚠ Deployment <name>: requests.cpu <r>, p95 usage <u> over 7d (below the 1/4 threshold)
Handed to the static detectors (not audited here): k8s-availability-scan · k8s-wiring-scan · k8s-api-version-scan
UNVERIFIED (tool absent): <axis> — <tool> not installed · NO-DATA(<ctx>) when no context was confirmed
```

## Rules

- Audit production separately from staging; one confirmed context per run.
- Every axis is measured-and-cited, UNVERIFIED or NO-DATA. There is no fourth state.
- Blocking findings become a ticket with an owner and a deadline; API-removal rows get the upgrade window as their deadline.

## Related

- `k8s-render-scan`, `k8s-availability-scan`, `k8s-wiring-scan`, `k8s-api-version-scan` (this pack) — everything decidable from the render; `@k8s-reviewer` judges them.
- `@kubernetes-architect` — upgrade cadence and addon-matrix policy that axis 1 measures.
- `network-exposure-audit` (this pack) — network correctness and the live exposure reads; exposure classes are security's.
- `dr-audit` — backup and restore posture, including PV snapshots and operator backups.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.