A Kubernetes security scanner is not one tool. It is a set of checks spread across five layers: the container images you build, the manifests and infrastructure code that describe how they run, the open source dependencies inside them, the configuration of the cluster itself, and the workloads that are actually running. Each layer has a natural place to run its check (the build pipeline, the pull request, the admission step, a scheduled audit, or the node), and a gap at any one of them lets a problem through that the others were never designed to see.
This guide is the coverage map. It takes each layer in turn and answers three questions: what to scan, where the check belongs, and what the authoritative source says good looks like. Kubernetes behaviour is taken from the Kubernetes project documentation, configuration baselines from the CIS Kubernetes Benchmark, and the container threat model from NIST SP 800-190. If you want the background first, our explainer on what Kubernetes security is covers the fundamentals, and Kubernetes security risks walks through the threats. This post is about coverage: making sure every layer has a check, and every check runs in the right place.
Key takeaways
- "Kubernetes security scanner" usually means five different checks: image scanning, manifest and IaC checks, dependency scanning, cluster configuration audits and runtime monitoring. No single check covers all five.
- Image and dependency scanning belong in the build pipeline, where a failing check stops a vulnerable image before it reaches a registry. Rescan the same images continuously, because a clean image becomes vulnerable the day a new CVE is published.
- Manifest checks belong in the pull request and again at admission. Kubernetes ships its own admission control for pod security, Pod Security Admission, which enforces the Privileged, Baseline and Restricted Pod Security Standards per namespace.
- Cluster configuration is audited against the CIS Kubernetes Benchmark and the Kubernetes security checklist: API server flags, kubelet settings, RBAC, etcd encryption, audit logging and network policy.
- Running workloads need their own signal. Kubernetes audit logs record what happened through the API; detection on the node watches what containers actually do. Neither is replaced by scanning images.
- Map each finding back to the layer that should have caught it. If a vulnerable package shows up in production, the fix belongs in the pipeline gate, not in a runtime alert.
Why one scanner is never enough
NIST SP 800-190, the Application Container Security Guide, groups container risk into five areas: image risks, registry risks, orchestrator risks, container risks and host OS risks. Each area is attacked differently and observed from a different place. An image vulnerability is visible the moment the image is built. An overly permissive RoleBinding is visible only in the cluster's RBAC objects. A container that suddenly spawns a shell and opens an outbound connection is visible only at runtime, on the node.
The practical answer is to stop asking "which scanner" and start asking "which layer is uncovered". The table below is the map the rest of this post fills in.
| Layer | What you scan | Where the check runs | Authoritative reference | What it cannot see |
|---|---|---|---|---|
| Container images | OS packages, language packages, base image age, image configuration | CI after docker build, registry on push, continuous rescans | NIST SP 800-190 image and registry risks | How the pod is configured, what the cluster allows |
| Manifests and IaC | securityContext, privilege, host namespaces, capabilities, resource limits, image references | Pull request, CI, admission | Kubernetes Pod Security Standards | Vulnerabilities inside the image, live cluster state |
| Dependencies | Application libraries (npm, PyPI, Maven, Go modules and others) and their transitive tree | Pull request and CI on the source repository | Package manager lockfiles, SBOMs | Image OS packages if you only scan source; runtime behaviour |
| Cluster configuration | API server, kubelet, etcd, RBAC, network policy, audit logging, Secret encryption | Scheduled audit against the live cluster | CIS Kubernetes Benchmark, Kubernetes security checklist | What a specific workload does after it starts |
| Running workloads | Process, file and network behaviour; API activity; drift from the deployed image | On the node and in the audit log pipeline | NIST SP 800-190 container risks, Kubernetes auditing | Whether the image had a known CVE before it shipped |
The last column matters as much as the others. Every layer has a blind spot, and the next layer exists to cover it.
Layer 1: Container images
What to scan in an image
A container image is a stack of filesystem layers plus a configuration object. A scanner reads both.
- Operating system packages. The base image brings a distribution's package database (dpkg, rpm, apk). Each installed package version is matched against the distribution's security advisories and public CVE data. This is where most image findings come from, because base images carry far more packages than the application uses.
- Language packages copied into the image. A Node.js image carries
node_modules, a Python image carries site-packages, a Java image carries JARs. These are the same dependencies Layer 3 scans in source, but the image copy is what actually runs, and it can differ from the lockfile if the build step resolved something different. - Image configuration. The image config records the default user, exposed ports, environment variables and entrypoint. An image that defaults to running as root is a finding even before Kubernetes sees it, because a pod that does not override the user inherits it.
- Base image provenance and age. Which upstream image did this build start from, and how old is it? A base image that has not been rebuilt in months accumulates OS package CVEs whether or not your application code changed.
NIST SP 800-190 lists the image risks it wants organisations to address, among them image vulnerabilities, image configuration defects and the use of untrusted images, and recommends that organisations use tools that see vulnerabilities across every layer of an image, not only the base layer, and that they enforce compliance with configuration best practices through policy. The guide is explicit that scanning must happen as part of the build and continue afterwards, because the vulnerability data changes after the image does not.
Where the image check runs
Images get scanned in three places, and a mature setup uses all three.
- In CI, right after the build. This is the cheapest place to stop a bad image, because nothing downstream has consumed it yet. The scan runs on the build runner against the image it just produced, and the job fails if the image breaks policy.
- At the registry. Images arrive in registries from places other than your main pipeline: vendor images, one-off builds, images pulled through from public registries. A registry-side scan catches what CI never saw.
- Continuously, after the fact. The image does not change, but the world's knowledge about it does. A new CVE published on Thursday makes Monday's clean image vulnerable. Continuous rescanning re-evaluates stored images against new vulnerability data without a rebuild.
How Safeguard covers the image layer
This is the layer where Safeguard does most of the scanning work. Per the Safeguard container image docs, you add one step after your pipeline builds an image. The step scans that image on the runner and records it in Safeguard under the image's repository, one version per environment and tag. The documented command is:
safeguard ci scan --image "ghcr.io/acme/api:$TAG" --environment prod --wait --fail-on critical
A few details from the docs are worth knowing before you wire it up:
- The scan reads the image from the local Docker daemon when it runs on the machine that built it, with nothing to configure. Otherwise it pulls from the registry using
SAFEGUARD_REGISTRY_USERNAMEandSAFEGUARD_REGISTRY_PASSWORDset as CI secrets. - Only the results leave the runner, and no repository integration is needed for an image scan.
--environmentnames the version (for exampleprod-1.4.2orstaging-1.4.2), so you can see what runs where.--fail-onwith--waitfails the job on a vulnerability at or above the severity you choose. The docs suggest failing on critical in production and not failing in development, which keeps developer builds fast while production stays strict.- Every container registry card under Integrations in the app has a Set up CI/CD button that generates the steps for your CI and environments.
The documented GitHub Actions example installs the CLI on the runner and runs the scan as a plain shell step:
- name: Build
run: docker build -t "ghcr.io/acme/api:${{ github.sha }}" .
- name: Safeguard image scan (prod)
env:
SAFEGUARD_API_KEY: ${{ secrets.SAFEGUARD_API_KEY }}
SAFEGUARD_INSTALL_DIR: ${{ runner.temp }}/safeguard-bin
IMAGE: ghcr.io/acme/api:${{ github.sha }}
run: |
set -eu
curl -fsSL https://cli.safeguard.sh/install | bash
"$SAFEGUARD_INSTALL_DIR/safeguard" ci scan --image "$IMAGE" --environment prod --wait --fail-on critical
The secret key needs the scans:trigger and scans:read scopes. The same pattern works in GitLab CI/CD, Bitbucket Pipelines, Jenkins and Azure Pipelines.
After the build, Safeguard continuous scanning re-evaluates stored assets, including container images and SBOMs, when vulnerability feeds change: a new CVE, a GHSA or OSV entry, a CISA KEV addition or an EPSS score change that affects a component you use. That is the "clean on Monday, vulnerable on Thursday" problem handled without a rebuild.
When a base image or package in a container needs patching, Safeguard's self-healing containers capability can plan and rebuild the patched image through your own tests. We cover it in its own post, so this guide only points to the docs.
Image layer: practical rules
- Pin images by digest in production manifests, not by mutable tag. A digest guarantees the scanned image is the deployed image.
- Scan the exact image you push. Rebuilding for "release" after scanning a "test" build means the scanned artifact and the shipped artifact are different files.
- Prefer small base images. Fewer packages mean fewer findings and fewer patch cycles.
- Rebuild base images on a schedule, not only when application code changes.
- Record the image digest, environment and scan result together so an auditor can trace any running pod back to a scan.
Layer 2: Manifests and infrastructure as code
What to check in a manifest
Kubernetes manifests (Deployments, StatefulSets, DaemonSets, Jobs, Helm charts, Kustomize overlays) decide how a container runs, and most dangerous settings live here, not in the image. The Kubernetes project defines what "dangerous" means in the Pod Security Standards, which describe three cumulative policies:
- Privileged: unrestricted. It permits known privilege escalations and is meant for system and infrastructure workloads run by trusted users.
- Baseline: minimally restrictive. It prevents known privilege escalations while allowing the default pod configuration.
- Restricted: heavily restricted, following current pod hardening practice at some cost to compatibility.
Translated into concrete manifest checks, the Baseline and Restricted profiles cover settings such as these.
| Manifest field | What to check | Profile that enforces it |
|---|---|---|
spec.hostNetwork, hostPID, hostIPC | Must be unset or false | Baseline |
securityContext.privileged | Must be unset or false | Baseline |
securityContext.capabilities.add | Only the defined safe set is allowed | Baseline |
volumes[*].hostPath | Host path volumes are forbidden | Baseline |
ports[*].hostPort | Host ports disallowed or limited to a known list | Baseline |
| AppArmor and SELinux settings | Cannot override to unconfined or set disallowed options | Baseline |
/proc mount type | Must be the default | Baseline |
| Seccomp profile | Cannot be set to Unconfined (Baseline); must be RuntimeDefault or Localhost (Restricted) | Baseline and Restricted |
sysctls | Only a safe subset is allowed | Baseline |
| Volume types | Limited to a list of safe types such as configMap, emptyDir, projected, secret and persistentVolumeClaim | Restricted |
allowPrivilegeEscalation | Must be false | Restricted |
runAsNonRoot | Must be true | Restricted |
runAsUser | Must not be 0 | Restricted |
| Capabilities | Must drop ALL; may add back only NET_BIND_SERVICE | Restricted |
The Pod Security Standards page is the reference for the exact field lists and allowed values, and it changes between Kubernetes versions, so check the version you run.
A manifest scan should also look beyond pod security:
- Resource requests and limits. Missing limits let one workload starve a node. The Kubernetes security checklist recommends setting memory limits and considering CPU limits.
- Image references. Flag
:latestand untagged images; prefer digests. - Service account usage. Pods that do not need the Kubernetes API should set
automountServiceAccountToken: false, which the Kubernetes security checklist and RBAC good practices both recommend. - Secret handling. Secrets should be mounted from Kubernetes Secret objects (or an external store), not written into manifests or environment variables in plain text.
- Network exposure. A Service of type LoadBalancer or NodePort exposes a workload outside the cluster; that should be an intentional choice, reviewed like any other change.
Where the manifest check runs
Manifests should be checked twice: once before merge and once at admission.
Before merge. Rendered manifests (after Helm templating or Kustomize overlays) are checked in the pull request. Checking unrendered templates misses values that only appear after rendering, so render first, then check. Policy-as-code tools that evaluate YAML against rules are common here; choose one whose rules you can map one to one onto the Pod Security Standards.
At admission. Kubernetes has a built-in admission controller for exactly this purpose. Pod Security Admission enforces the Pod Security Standards per namespace using labels. Each namespace can set three modes independently:
enforce: policy violations cause the pod to be rejected.audit: violations add an audit annotation to the event in the audit log, but the pod is allowed.warn: violations trigger a user-facing warning, but the pod is allowed.
A namespace that enforces Baseline while warning and auditing at Restricted looks like this:
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
Pinning enforce-version to a specific Kubernetes minor version instead of latest keeps the policy stable across cluster upgrades, which is useful when you want to adopt a new version's rules deliberately.
Before turning on enforcement, the Kubernetes docs show how to test the effect with a server-side dry run, which reports which existing pods would violate the new level without changing anything:
kubectl label --dry-run=server --overwrite ns --all \
pod-security.kubernetes.io/enforce=baseline
Pod Security Admission only covers pod security fields. For anything else (requiring digests, requiring labels, blocking certain registries), Kubernetes offers ValidatingAdmissionPolicy, which lets you write admission rules in the Common Expression Language without running a webhook, and the long-standing validating and mutating admission webhooks for external policy engines.
One caution from the Kubernetes docs: Pod Security Admission evaluates pods, and it also checks workload resources such as Deployments, but for those it only warns and audits. The pods they create are what get rejected. A Deployment can be accepted and then fail to create any pods, so watch the ReplicaSet events after rollout.
What Safeguard does and does not do at this layer
Safeguard does not enforce admission control in your cluster. Use Pod Security Admission, ValidatingAdmissionPolicy or an admission webhook of your choice for that step. Where Safeguard does help at this layer is the pipeline around the manifest: the image a manifest references is scanned in CI, and Safeguard policies and gates decide whether a build may proceed. With policy outcomes switched on for a workspace, the docs show safeguard ci scan --wait --fail-on-gate <gate> failing a CI job when the gate fails. Put that job in the same pipeline that renders and checks your manifests, and a release cannot ship a manifest that points at an image the gate rejected.
Layer 3: Dependencies
What to scan
Every image contains two dependency trees: the operating system packages from the base image and the application libraries your code imports. Layer 1 reads both from the image. Layer 3 reads the application tree from source, before the image exists, and that earlier read is what makes it valuable.
A dependency scan on source covers:
- Direct and transitive dependencies from lockfiles:
package-lock.json,pnpm-lock.yaml,yarn.lock,poetry.lock,requirements.txtwith hashes,go.sum,Cargo.lock, Maven and Gradle resolution, and the rest. - Known vulnerabilities in each resolved version, with affected and fixed version ranges.
- License obligations for each component.
- Maintenance signals, such as abandoned packages or components far behind their current release.
- An SBOM recording exactly what was resolved, so the image scan and the source scan can be compared later.
Where it runs
On every pull request and on the default branch. The pull request check catches a new vulnerable dependency before it merges. The default branch scan establishes the baseline that later rescans compare against.
How Safeguard covers it
Safeguard's software composition analysis scans the dependencies in your repositories, and deep dependency scanning follows the transitive tree, where most vulnerable components in a Kubernetes service actually sit. Findings are ranked using reachability analysis, so the vulnerable code paths your application actually calls come first. The effect of reachability on your backlog depends on your codebase, so measure it during an evaluation rather than assuming a number.
Policies apply here too. The policies and gates docs list conditions such as vulnerability severity, CVSS score, EPSS score, license, component age, maintenance and SLSA level, and templates such as No Critical Vulnerabilities, License Compliance and No Abandoned Packages. Each policy's action decides what a failure does to the gate: Block fails the gate, Warn passes with a warning, Notify passes and sends a notification. Exceptions are requested with a business justification and an expiry date and go through an approval workflow with an audit trail.
A worked example of layer handoff
Consider a Go service built on a distroless base image and deployed to a cluster.
- A developer bumps a JSON library. The pull request dependency scan flags a transitive module with a known high severity vulnerability and a fixed version two patch releases ahead. The developer pins the fixed version in
go.modbefore merge. - CI builds the image. The image scan finds no application findings (Layer 3 already fixed it) but reports a medium severity issue in a base image package. Policy allows mediums in production with a warning, so the build passes and the warning is recorded.
- The rendered Deployment fails the manifest check because the container has no
runAsNonRoot. The pull request is updated. - At admission, the namespace enforces Baseline and warns at Restricted. The pod is admitted; the warning about seccomp profile is logged.
- Three weeks later, a new CVE lands for the base image package. Continuous scanning flags the stored image as affected. The base image is rebuilt and the service redeployed.
Layer 4: Cluster configuration
What to audit
The cluster itself is configuration: API server flags, kubelet settings, etcd, controller manager and scheduler options, RBAC objects, network policies, audit policy and the way Secrets are stored. The Kubernetes security checklist and the CIS Kubernetes Benchmark are the two baselines most teams audit against. The CIS Benchmark is a consensus document with numbered, testable recommendations for control plane components, worker nodes and policies, published in versions that track Kubernetes releases, and with separate variants for several managed Kubernetes services.
The main audit areas look like this.
| Area | What to check | Reference |
|---|---|---|
| Authentication | Anonymous access to the API server disabled or tightly scoped; no static token files; client certificates rotated | CIS Benchmark control plane section; Kubernetes security checklist |
| Authorization | RBAC enabled; AlwaysAllow not used | CIS Benchmark; Kubernetes RBAC good practices |
| RBAC objects | No wildcard verbs or resources; minimal cluster-admin bindings; review of escalate, bind and impersonate; limited get, list and watch on Secrets | Kubernetes RBAC good practices |
| Kubelet | Anonymous auth off; authorization mode Webhook; read-only port disabled | CIS Benchmark worker node section |
| etcd | Client and peer TLS; access restricted to the API server | CIS Benchmark; Kubernetes security checklist |
| Secrets at rest | Encryption at rest configured for Secret resources | Kubernetes security checklist |
| Audit logging | An audit policy configured and audit logs shipped off the control plane | Kubernetes auditing docs; CIS Benchmark |
| Network policy | A network plugin that enforces NetworkPolicy; default deny per namespace | Kubernetes network policies docs |
| Pod security | Every namespace labelled with an explicit Pod Security Admission level | Pod Security Admission docs |
RBAC: the audit that finds the most
The Kubernetes RBAC good practices page is unusually direct about privilege escalation paths that RBAC audits should look for. Some of the most important:
- Least privilege. Assign permissions at namespace level with RoleBindings where possible, avoid wildcard permissions, and avoid adding users to the
system:mastersgroup, which bypasses RBAC entirely. - Listing Secrets. The page warns that list access to Secrets lets a user read Secret contents, not only names, so treat
listandwatchon Secrets likeget. - Workload creation. Anyone who can create pods (or Deployments, Jobs and other workload resources) in a namespace can mount any Secret, ConfigMap or service account in that namespace. Workload creation rights are effectively Secret read rights.
- Escalation verbs.
escalate,bindandimpersonatelet a user grant themselves or act with more privilege than they hold. Audit every role that has them. - Persistent volume creation. Rights to create PersistentVolumes can allow
hostPathvolumes, which give access to the node's filesystem. - Node proxy and the
nodes/proxysubresource. These give access to the kubelet API and should be held by very few identities. - Certificate signing requests and token requests. The page lists CSR and certificate issuing rights, and create rights on
serviceaccounts/token, as escalation paths, because both can be used to obtain credentials. - Admission webhook control. Identities that can change validating or mutating webhook configurations can alter or read objects as they are admitted. Treat those rights like cluster administration.
A cluster audit should export all Roles, ClusterRoles, RoleBindings and ClusterRoleBindings, resolve which subjects hold which effective permissions, and flag any binding that grants the risky permissions above to a subject that is not a known control plane component or platform team.
# Who can read Secrets cluster wide? (run as an administrator)
kubectl auth can-i list secrets --all-namespaces --as=system:serviceaccount:payments:api
# Review every ClusterRoleBinding to cluster-admin
kubectl get clusterrolebindings -o json \
| jq -r '.items[] | select(.roleRef.name=="cluster-admin") | .metadata.name + " -> " + ([.subjects[]? | .kind + ":" + .name] | join(", "))'
Network policy: what the audit should prove
Kubernetes network policies control traffic at the IP address and port level for pods. Two facts from the docs drive the audit:
- NetworkPolicy objects only take effect if the cluster's network plugin implements them. Creating a policy without a supporting plugin has no effect at all.
- By default, pods are non-isolated: they accept traffic from any source. A pod becomes isolated for ingress or egress only when a policy selects it for that direction.
So the audit has two parts: confirm the network plugin enforces NetworkPolicy, and confirm each application namespace has a default deny policy with explicit allows layered on top.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Remember that a default deny on egress also blocks DNS, so an explicit allow to the cluster DNS service usually follows.
Audit logging: the evidence trail
Kubernetes auditing records a chronological set of records describing actions taken through the API server. The audit policy decides what is recorded at four levels: None, Metadata (request metadata such as user, timestamp, resource and verb), Request (metadata plus request body) and RequestResponse (metadata, request body and response body). Events can be recorded at the stages RequestReceived, ResponseStarted, ResponseComplete and Panic.
A practical policy logs Secret, ConfigMap and TokenReview access at Metadata level (so Secret contents never land in logs), logs writes to workload and RBAC resources at Request level, and drops noisy read-only health checks.
apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
- "RequestReceived"
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets", "configmaps"]
- group: "authentication.k8s.io"
resources: ["tokenreviews"]
- level: Request
verbs: ["create", "update", "patch", "delete"]
resources:
- group: "rbac.authorization.k8s.io"
- group: "apps"
- level: None
nonResourceURLs: ["/healthz*", "/readyz*", "/livez*", "/version"]
- level: Metadata
On a managed Kubernetes service you usually cannot set the policy file yourself; the provider exposes control plane audit logs through its own logging service. Check that they are switched on and retained.
Where the configuration audit runs
Against the live cluster, on a schedule, and after every upgrade. Cluster configuration drifts: a node pool gets added with different kubelet flags, a namespace is created without Pod Security labels, an engineer adds a ClusterRoleBinding during an incident and never removes it. A scheduled audit against the CIS Benchmark catches drift that no pull request ever showed. Benchmark checks for control plane components assume access to node files and process flags, so on a managed service you run the worker node checks and rely on the provider's documentation for the control plane.
Where Safeguard fits at this layer
Safeguard reads cluster configuration through its Kubernetes compliance connector, which is documented as read-only. You connect it with the API server URL and a service account token (or a kubeconfig), and the grant is get and list on RBAC objects, namespaces, nodes, pods, service accounts and network policies, plus Gatekeeper constraints and Kyverno policies if you run them. It reads no Secrets and changes nothing. Safeguard's automated tests map what it reads to CIS Kubernetes Benchmark recommendations (the docs cite v1.9.0), and the results show on the Compliance Tests page as evidence for your controls, not as a certificate. Cite the benchmark edition you test against, and keep using your own node-level audit job for checks that need node files or process flags. Our 8 November post on cloud and Kubernetes benchmark connectors walks through the setup and the permissions in detail. More generally, compliance connectors link Safeguard to systems you run, such as your cloud account, code host or ticket tracker, with read-only access, and turn what they read into evidence for your controls. Pair that with the CIS Controls framework in Safeguard's compliance module when your Kubernetes audit results feed a broader control program.
Layer 5: Running workloads
What to watch
Everything above runs before or around deployment. Layer 5 watches what actually happens after a pod starts. NIST SP 800-190 describes container risks such as vulnerabilities within the runtime software, unbounded network access from containers, insecure container runtime configurations, app vulnerabilities and rogue containers, and recommends tools that monitor container behaviour and detect anomalies, profile normal behaviour, and prevent or alert on drift from the image.
Runtime signals fall into three groups:
- API activity. What identities did through the Kubernetes API: exec into a pod, create a privileged pod, read a Secret, bind a role. This comes from the audit log in Layer 4, now read as a detection source rather than an evidence store.
- Node and container behaviour. Process execution, file writes to unexpected paths, new listening sockets, outbound connections to unusual destinations. This comes from tooling on the node, typically built on kernel instrumentation.
- Drift and inventory. What images are actually running, which digests, in which namespaces, and whether any running container differs from the image that was scanned.
Where the runtime check runs
On the node, for behaviour; in the log pipeline, for API activity; and in a scheduled inventory job, for drift. Each team picks its own runtime detection tooling. Whatever you choose, make sure it reads Kubernetes metadata (pod, namespace, service account, image digest) so an alert can be traced back to the manifest and image that produced it.
Close the loop with image inventory
The one runtime question every team can answer with standard tools is "which images are running right now". A scheduled job can list them:
kubectl get pods --all-namespaces \
-o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{range .status.containerStatuses[*]}{.imageID}{"\n"}{end}{end}' \
| sort -u
imageID carries the digest the node actually pulled, which is more reliable than the tag in the spec. Compare that list with the images your pipeline scanned and recorded. Any digest that is running but was never scanned is a coverage gap: an image that came from outside the pipeline, a manual deployment, or a third-party chart. You can then scan those image references with the same documented safeguard ci scan --image step from Layer 1, pulling from the registry with registry credentials, so they join the continuously rescanned inventory.
Be clear about what this does and does not give you. It tells you whether the images in your cluster carry known vulnerabilities. It does not tell you what those containers are doing. Behaviour on the node is the job of runtime detection tooling; Safeguard does not monitor container behaviour in your cluster.
Putting the layers together: a pipeline and cluster blueprint
Here is how the five layers map onto a typical GitOps style delivery flow.
| Stage | Layer | Check | Fails on |
|---|---|---|---|
| Pull request (application repo) | 3 | Dependency scan and SBOM | Policy gate: new critical, blocked license |
| Pull request (application repo) | 2 | Dockerfile review: non-root user, pinned base image | Missing USER, unpinned base |
| CI build | 1 | Image scan on the runner, --environment per target | --fail-on critical in prod |
| CI build | 1, 3 | Policy gate evaluation | --fail-on-gate |
| Pull request (deploy repo) | 2 | Rendered manifest check against Pod Security Standards | Privileged, hostPath, missing runAsNonRoot |
| Admission | 2 | Pod Security Admission per namespace; ValidatingAdmissionPolicy for extra rules | Baseline violations (enforce), Restricted (warn) |
| Scheduled | 4 | CIS Benchmark audit, RBAC review, network policy coverage | New wildcard roles, unlabelled namespaces |
| Continuous | 1, 3 | Rescan stored images and SBOMs on new vulnerability data | New CVE affecting a deployed component |
| Continuous | 5 | Audit log detection, node runtime detection, running image inventory | Exec into prod pod, unscanned digest running |
Two rules make this blueprint work in practice.
Fail early, warn late. The earlier stages should block; the later stages should mostly alert. Blocking at admission for a CVE that was knowable at build time punishes the deploy, not the build that caused it. Use admission for configuration and identity rules, not for vulnerability thresholds you could have enforced in CI.
Every finding gets a home layer. When something reaches production, ask which layer should have caught it, and fix that layer. A vulnerable library in production is a Layer 3 gap. A privileged pod in production is a Layer 2 gap. A ClusterRoleBinding nobody remembers is a Layer 4 gap.
Common mistakes
- Scanning images but not manifests. A vulnerability free image that runs privileged with a hostPath mount to
/is still a node compromise waiting to happen. - Scanning manifests but not images. A perfectly hardened pod spec does not fix a remote code execution bug in a library.
- Checking unrendered Helm templates. Values injected at render time are where privileged flags and host mounts often appear. Render, then check.
- Turning on Pod Security Admission enforce without a dry run. Existing workloads can fail to restart. Use the server-side dry run, then warn and audit for a cycle, then enforce.
- Treating a scan at build time as permanent. Images age. Without continuous rescanning, your production images become vulnerable silently.
- Writing NetworkPolicy without a plugin that enforces it. The objects exist, nothing is enforced, and the audit passes on paper.
- Granting workload creation rights casually. Create rights on pods or Deployments in a namespace are effectively read rights on its Secrets.
- Logging Secret access at RequestResponse level. That writes Secret values into the audit log. Use Metadata for Secrets.
- Deploying by tag. The scanned image and the running image can differ when tags move. Deploy by digest.
- One exception, no expiry. Policy exceptions without an expiry date become permanent holes. Require a justification and an end date.
Fix the findings, not just the reports
Scanning is half the job. The half that reduces risk is getting from a finding to a merged fix quickly, at the layer where the finding belongs. For images and dependencies, that means upgrade pull requests that move a vulnerable component to a fixed version, tested and gated like any other change, and a reliable source of fixed components when upstream is slow. Kubernetes workloads are built from the same open source packages and base images as everything else you ship, so the same supply chain hygiene applies to them.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Griffin, Safeguard's security model, opens a policy gated pull request that moves the affected dependency to the safe version, and your own CI runs your tests on it before anyone merges. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Auto-merge is a separate switch and stays off by default, so your team decides what merges on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Safeguard Gold rebuilds patched versions of open-source packages and images from source, signs each build, attaches an SBOM, SLSA provenance and VEX, and serves them from registry.safeguard.sh to your organization. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The demo below is a real Safeguard CLI session. It shows the CLI being installed, the assistant asked to fix a dependency finding, each step approved, and a scan run to confirm the result. The session uses an npm project, but the same flow applies to the application dependencies inside a container image you deploy to Kubernetes: fix the finding in source, rebuild, and let the image scan confirm it.
Click through it at your own pace, or open it full screen.
This demo sets up the Safeguard local runner on your own machine, approves it with a device code and then scans from the terminal. For a platform team it is a way to scan a service repository where the code lives, before an image is ever built.
Click through it at your own pace, or open it full screen.
The last demo connects Claude to the Safeguard MCP server with OAuth and chooses which tools Claude can use. Once connected, you can ask about the findings, images and dependency paths behind the services you run on Kubernetes from the assistant you already use.
Click through it at your own pace, or open it full screen.
To try it on your own images, download Safeguard and follow the container image guide.
FAQ
What is a Kubernetes security scanner?
It is a tool or set of tools that checks a Kubernetes environment for security problems. In practice that means five kinds of checks: container image vulnerability scanning, manifest and IaC checks against the Pod Security Standards, application dependency scanning, cluster configuration audits against the CIS Kubernetes Benchmark, and runtime monitoring of running workloads. Most tools cover one or two of these well.
What should I scan first in Kubernetes?
Start with container images in CI, because it is the cheapest place to stop known vulnerabilities and it needs no cluster access. Then add namespace labels for Pod Security Admission, because they are built into Kubernetes and stop the most dangerous pod configurations. Cluster audits and runtime monitoring come next.
Does image scanning cover Kubernetes security?
No. Image scanning finds known vulnerabilities and some configuration problems inside an image. It cannot see how the pod is configured, what RBAC allows, whether network policy is enforced or what a container does at runtime. It is one layer out of five.
What replaced PodSecurityPolicy?
Pod Security Admission, a built-in admission controller that enforces the Pod Security Standards (Privileged, Baseline and Restricted) per namespace through labels, with enforce, audit and warn modes. PodSecurityPolicy was removed from Kubernetes in version 1.25. For rules beyond pod security, use ValidatingAdmissionPolicy or an admission webhook.
How do I check my cluster against the CIS Kubernetes Benchmark?
Download the benchmark version that matches your Kubernetes release (and managed service, if you use one) from the Center for Internet Security, then run an audit job that implements its checks against your control plane and worker nodes. On managed services you usually test only the worker node and policy sections, because the provider runs the control plane.
Where should Kubernetes security checks run in a CI/CD pipeline?
Dependency scans run on the pull request. Image scans run right after the image is built, on the runner, before the image is pushed for deployment. Rendered manifest checks run on the pull request that changes deployment configuration. Pod Security Admission runs at admission in the cluster. Cluster audits run on a schedule against the live cluster.
Can a Kubernetes scanner detect attacks at runtime?
Only if it includes runtime monitoring. Image, manifest and configuration scanners work before or around deployment. Detecting an attack in progress needs Kubernetes audit logs and node level behaviour monitoring, which are separate capabilities.
Does Safeguard scan Kubernetes clusters?
Safeguard scans the container images you build in CI, records them per environment, gates builds with policies and keeps rescanning stored images and SBOMs as new vulnerabilities are published. It also scans the application dependencies in your repositories. For cluster configuration, its read-only Kubernetes connector reads RBAC objects, namespaces, nodes, pods, service accounts and network policies and maps them to CIS Kubernetes Benchmark checks as compliance evidence. It does not enforce admission control or monitor running containers; use Kubernetes' own Pod Security Admission and runtime tooling for those layers.
Quick check
A namespace has pod-security.kubernetes.io/enforce=baseline and warn=restricted. A pod sets no runAsNonRoot and has no seccomp profile but is otherwise default. Is it admitted?
Yes. Those settings violate Restricted, not Baseline, so the pod is admitted and the user sees a warning. It would be rejected only if the namespace enforced Restricted.
You created a default deny NetworkPolicy in every namespace, but pods can still reach each other. What is the most likely cause?
The cluster's network plugin does not implement NetworkPolicy. Policies only take effect when the plugin enforces them; otherwise the objects are stored and ignored.
An image passed its CI scan last month with no critical findings. Is it still safe to run?
Not necessarily. New CVEs may now affect components inside it. Continuous rescanning of stored images answers that question without a rebuild.
A developer can create Deployments in the payments namespace but has no RBAC rule allowing them to read Secrets. Can they get at those Secrets?
Yes, in practice. Anyone who can create workloads in a namespace can mount any Secret in that namespace into a pod they control. The Kubernetes RBAC good practices page treats workload creation as access to the namespace's Secrets.
Sources
- Kubernetes v1.25 release notes: PodSecurityPolicy removed
- NIST SP 800-190: Application Container Security Guide
- Center for Internet Security: CIS Kubernetes Benchmark
- Kubernetes: Pod Security Standards
- Kubernetes: Pod Security Admission
- Kubernetes: Enforce Pod Security Standards with namespace labels
- Kubernetes: Security checklist
- Kubernetes: Role Based Access Control good practices
- Kubernetes: Network policies
- Kubernetes: Auditing
- Kubernetes: Validating Admission Policy
- Kubernetes: Encrypting confidential data at rest
- Kubernetes: Images
- Safeguard docs: Container images
- Safeguard docs: Policies and gates
- Safeguard docs: Continuous scanning
- Safeguard docs: Self-healing containers
- Safeguard docs: Compliance connectors