lookout audit
lookout audit asks a different question from everything else: not “what
is broken” but “what has no safety net, while it is still healthy”. A
posture finding is a standing claim — it never self-clears, because a
workload with one replica has one replica until somebody decides
otherwise.
That is also why every claim here is --exemptions-auditable: the answer
is often “yes, deliberately”, and a git-reviewed exemption file records the
reason and an expiry instead of the finding quietly disappearing. Covered
findings are annotated with their reason and counted as exempt=<n>,
never dropped.
Reachable two ways: the subcommands below, or lookout scan --include=audit
for the whole sweep alongside the incident checks. Like scan, it needs
nothing deployed — a kubeconfig is the whole setup.
Cluster-level security configuration posture, read from the cloud provider: Workload Identity off cluster-wide or bypassed by a node pool, node pools still serving the legacy metadata endpoints, and a control-plane endpoint the internet can reach with nothing narrowing it. Reads the provider’s cluster record, not Kubernetes objects, so it takes no —namespace/-A/—workload; scanned counts the cluster plus its node pools. Without a provider capability it reports an explicit unavailable rather than silence.
| Kind | Severity | What it means |
|---|---|---|
audit.workload_identity_off | warning | Workload Identity is off cluster-wide, or a node pool bypasses it — pods authenticate to the cloud as the node |
audit.legacy_metadata | warning | a node pool still serves the pre-v1 instance-metadata endpoints, which any pod can read |
audit.public_control_plane | warning, info | the control-plane endpoint is reachable from the internet; info when authorized networks narrow it |
cloud.unavailable | info | the cloud capability this check needs is unavailable, so nothing was examined — an explicit degradation record, never silence |
Audit the exemption file itself: which reviewed exemptions have lapsed (and are therefore no longer annotating anything) and which are about to. The mechanism that keeps an exemption file from becoming a permanent, unread list of things nobody checks any more.
| Kind | Severity | What it means |
|---|---|---|
audit.exemption_expired | warning | an exemption entry has lapsed: the findings it used to annotate are being reported unqualified again |
audit.exemption_expiring | info | an exemption entry lapses within —within — renew it or let it go deliberately |
Workload security posture: containers running privileged or holding node-root capabilities, pods sharing the host network/PID/IPC namespaces, hostPath mounts, default-ServiceAccount tokens that something actually uses, and namespaces with no Pod Security Admission enforcement. Judges every pod-template owner in scope — Deployments, StatefulSets, DaemonSets, CronJobs, unowned Jobs and unowned Pods — plus the namespaces around them. Scope with —namespace or -A; scanned counts pod templates examined, the namespaces note counts namespaces.
| Kind | Severity | What it means |
|---|---|---|
audit.privileged_container | warning | a container runs privileged or holds a node-root capability (ALL, SYS_ADMIN): a container escape is a node compromise |
audit.host_namespace | warning | the pod shares the node’s network, PID, or IPC namespace |
audit.hostpath_mount | warning, info | the pod mounts a host path; warning when it is writable, info when read-only |
audit.default_sa_automount | warning | the pod runs as the namespace’s default ServiceAccount with its token automounted, and something in the pod can use it |
audit.podsecurity_gaps | warning | the namespace enforces no Pod Security Admission level, so none of the above is prevented |
NetworkPolicy coverage posture: namespaces where nothing restricts ingress or egress at all, and individual workloads that fell through the selectors of the policies covering their neighbours. Coverage means isolation — some policy selects the pod and names the direction — not that the rules it then applies are tight. hostNetwork templates are excluded, since NetworkPolicy cannot constrain them. Scope with —namespace or -A; scanned counts pod templates examined.
| Kind | Severity | What it means |
|---|---|---|
audit.netpol_missing | warning, info | nothing restricts this direction for the subject — a namespace with no policy at all, or a workload the covering policies’ selectors miss; info for the egress direction, where no policy is a defensible default |
Upgrade and patch readiness, read from the cloud provider: how far the control plane and its node pools are behind what the provider publishes, and whether anything is set up to close that gap on its own — release channel, node auto-upgrade and auto-repair, a maintenance window, active maintenance exclusions, node images on the removed Docker runtime, and upgrade notifications. Reads the provider’s cluster record, not Kubernetes objects, so it takes no —namespace/-A/—workload; scanned counts the cluster plus its node pools. Without a provider capability it reports an explicit unavailable rather than silence.
| Kind | Severity | What it means |
|---|---|---|
audit.version_behind | warning, info | the control plane or a node pool is behind what the provider publishes, or a node pool has skewed from the control plane; info while the gap is still within the supported skew |
audit.upgrade_unmanaged | warning | nothing will close that gap on its own: no release channel, or node auto-upgrade/auto-repair off |
audit.upgrade_blocked | warning, info | an active maintenance exclusion, or a node image on the removed Docker runtime, will stop the upgrade when it comes |
audit.upgrade_unattended | info | upgrades will happen with nobody watching: no maintenance window, or no upgrade notifications |
cloud.unavailable | info | the cloud capability this check needs is unavailable, so nothing was examined — an explicit degradation record, never silence |
Workload reliability posture for workloads that are healthy right now: no PodDisruptionBudget, only one replica, no readiness/liveness probe, no spread across nodes, placement pinned to too few nodes, autoscalers that structurally cannot scale, and CronJobs left suspended long enough to have skipped runs. Answers “what has no safety net”, as against stab drain, which answers “what breaks if I drain THIS node now”. Scope with —namespace, -A, or —workload; scanned counts workloads examined.
| Kind | Severity | What it means |
|---|---|---|
audit.no_pdb | warning | the workload has no PodDisruptionBudget: a drain can take every replica at once |
audit.single_replica | warning | the workload runs a single replica, so any disruption is an outage |
audit.no_readiness_probe | warning | a container has no readiness probe, so traffic reaches it before it can serve |
audit.no_liveness_probe | info | a container has no liveness probe, so a wedged process is never restarted |
audit.no_spread | info | the workload’s replicas are not spread across nodes or zones |
audit.rigid_scheduling | warning, info | placement constraints pin the workload to too few nodes to survive losing one |
audit.hpa_cannot_scale | warning | the autoscaler structurally cannot scale: min equals max, the target is missing, or a container has no request for its utilization target to divide by |
audit.suspended_cronjob | warning | a CronJob has been suspended past —cron-suspended and has skipped activations because of it: whatever it does is not happening, and nothing else reports that |
See also
Section titled “See also”- Exemptions reference — the file format, and the check that audits the exemption file itself.
- What
lookout scanfinds — the incident half. - Finding kinds — every read-path kind in one
flat table, when you have a
kind=and want its claim.