Defending Kubernetes against supply-chain attacks
A compromised dependency can smuggle a cryptominer into your build, past code review, and quietly burn CPU on a production node with no application symptoms at all. Here is how that attack works and the pipeline and runtime defenses that stop it.
The scariest class of compromise is the quiet one. No outage, no error spike, no alert from the application. Just a node running hotter than it should, with a process like xmrig (a Monero cryptominer) burning CPU inside an otherwise normal container. This is a write-up of how that happens through the software supply chain, and the layered defenses that make it both hard to pull off and fast to catch.
How a miner gets in without touching a pull request
The dangerous part of a supply-chain attack is that the malicious code is never in a diff anyone reads. A package deep in the dependency tree (a transitive dependency of a dependency) is compromised upstream. When the frontend builds, the bad code is pulled in and assembled into the bundle. Code review structurally cannot catch this, because reviewers look at the change set, not at every line of every transitive package. The result ships to production looking completely normal.
Why code review is the wrong layer for this
Human review inspects what changed in the pull request. A poisoned transitive dependency changes nothing in the diff; it changes what gets downloaded and built. That is why supply-chain defense has to live in the pipeline and the runtime, not in the review.
Detection: watch the node, not just the app
Because the application behaves normally, the only early signal is at the infrastructure layer. A cryptominer's whole purpose is to consume CPU, so node-level CPU anomaly detection is the tripwire. Concretely:
- Alert on anomalous CPU on any node, with a short threshold window (for example five minutes), routed to the on-call channel.
- When it fires and the application metrics look fine, suspect the node, not the service. Inspect the processes running inside the containers on that node.
- A process that does not belong to any of your images (a miner, a shell, an unexpected binary) is the smoking gun.
Containment, if one gets in
The response is standard Kubernetes hygiene, done quickly:
- Cordon the affected node so no new workloads land on it.
- Drain it and let the workloads reschedule onto clean nodes.
- Remediate the source: update every vulnerable library and pin the transitive versions so the poisoned package cannot return on the next build.
The defenses that actually stop it
Detection and cleanup are the last resort. These are the layers that prevent it, or neuter it if it still gets through.
- Audit in CI. Gate every build on
npm audit --audit-level=highso known-vulnerable dependencies fail the build. - Scan the image. Run an image scanner such as Trivy on every pre-deploy build, so a malicious payload baked into the image is caught before it ships.
- Enforce lockfiles. Require a committed lockfile in every repo and fail CI if it is missing or changed without review, so dependency versions are deterministic and auditable.
- Pin transitive versions. Floating versions are how a poisoned package gets a second chance; pinning closes that door.
- Restrict the runtime. Apply Pod Security Standards (restricted) across namespaces so a compromised container cannot escalate privileges.
- Default-deny egress. Network policies that block outbound connections to mining pools and unknown endpoints turn a successful intrusion into a dead end: the miner cannot reach its pool and cannot phone home.
Defense in depth, in one sentence
CI audit and image scanning make it hard to get in, lockfiles and pinning make it hard to come back, Pod Security Standards make it hard to escalate, default-deny egress makes it useless if it does get in, and node CPU anomaly alerts make it fast to catch. No single layer has to be perfect.
What I would tell someone hardening a cluster
- Assume a dependency will be compromised eventually, and design so that it does not matter much when it is.
- Put the supply-chain checks in the pipeline. They catch what code review structurally cannot.
- Default-deny egress is the highest-leverage control: it turns most intrusions into dead ends with almost no downside.
- Watch infrastructure signals, not just application ones. The quiet compromises only show up at the node.