Build1 distinct publisher3 min readPublished
One team's audit found a single pool labelled linux-docker scheduling outside contributors' builds onto the same agents that held internal deploy credentials, an arrangement that sat outside anything a controller permission model was built to check.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Controller permissions answer one question: which principal may configure, trigger or view which job [2]. Once the job is dispatched, the build steps are ordinary OS processes on an agent, and the audit's blunt finding is that the controller's UI restrictions do not follow them down there [3]. Nothing in a matrix consults itself when a build process opens a socket.
Now look at what those processes are. The write-up lists four kinds of code that run on agents: the team's own pipelines, third-party fork-PR builds, community plugins pulled during a build step, and whatever a compromised dependency decides to do at build time [4]. None of the four executes on the controller, so controller-side authorization covers none of them [1]. The boundary those checks depend on sits in a different building than the RBAC implementation itself.
The label is where the accident actually lived. One pool tagged `linux-docker` served internal repositories and external fork-PR builds, same credentials binding scope, same images [7], because one pool is cheaper to maintain than two and nobody wrote down that they were accepting the risk [9]. A Jenkins label is a scheduling hint, not an isolation boundary, unless you build one underneath it [8]. The scheduler believes the label; the kernel has never heard of it.
The socket mount is the same mistake with a shorter fuse. Several jobs needed to build and push images, so `/var/run/docker.sock` went into the agent container [11], which the author describes as functionally equivalent to handing that container root on the node [12]. The layers that might have blunted it were all absent: containers ran as root, no capabilities dropped, no seccomp or AppArmor profile [13]. A malicious npm package or a poisoned base image gets from container to node with almost no extra work [14], and the author says to treat any such mount on a build node as a critical finding rather than a shortcut, citing Docker's own security documentation [15].
This becomes your story under two conditions: your fork-PR builds have to land on a pool that also holds credentials or a route to internal services, and your agent images have to be permissive enough that arbitrary build code can use that route. The post, published on kuryzhev.cloud and on dev.to [17], does not name the metadata endpoint or its address, and nothing was exfiltrated [1], so this is a near miss reported by one team about its own fleet, not a measured base rate.
The detail I keep returning to is the fleet inventory. The old mental model had agents running a build, printing logs and getting recycled [2], while the audit found static VMs alive for months, sometimes years, patched in place after one provisioning [16]. The word "recycled" turned out to be doing more work in that sentence than the fleet actually did [2].
Ranked by verification strength, evidence, and original report placement.
A pull request from an external contributor triggered a build that, for a brief moment, had a live network path to an internal metadata endpoint it had no business touching; nothing was exfiltrated.
For years the team's model was that the Jenkins controller has RBAC, matrix-based security and folder permissions and is therefore the thing to lock down, while agents were 'just workers' that ran the build, printed logs and got recycled.
The write-up states that the assumption that controller UI restrictions extend to what code running on an agent can do at the OS level is wrong, and a common assumption.
Agents execute arbitrary code: the team's own pipelines, third-party fork-PR builds, community plugins pulled during a build step, and whatever a compromised dependency decides to do at build time.
The team's agents connected over standard JNLP/Remoting, and the team never restricted what Groovy code running on an agent could call back into the controller JVM.
A single pool of agents labelled linux-docker served both internal trusted repositories and external fork-PR builds, with the same credentials binding scope and the same agent images.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · September 2, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Partition, not consolidation: what a 43-minute Jenkins queue actually cost1 distinct publisher
build
A file-copy Allure adapter for Katalon, and the history IDs that make retries useful1 distinct publisher
build
752 Nigerian institutions, shipped as a repo instead of an endpoint1 distinct publisher
build
Jenkins static AWS keys work from anywhere; the OIDC replacement fails in four known ways1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
One team's word, no artefacts
Every fact in this story comes from the people who made the mistakes, published on dev.to and cross-posted from their own site — no dates, no ticket or finding IDs, no scan output, no named employer, and nobody outside the team who looked at the same Jenkins estate. The configuration claims are credible precisely because they are self-incriminating, and the design claims underneath them (a label is a scheduling hint; a mounted socket is host root) can be checked against Jenkins and Docker behaviour rather than against the author. What has no support at all is the reach: the 'plausible path' to the credentials store is hedged on unnamed plugins, and the assertion that socket mounts are pervasive in Jenkins-on-Kubernetes arrives with no data behind it.
One fleet, changes reported not shown
The only movement in evidence is inside a single unnamed team: two findings that prompted an audit, and a reported migration to pod-per-build Kubernetes agents with pull-request work pulled off shared infrastructure. Nobody says how many agents, how many pipelines, or how far the migration got. Nothing indicates whether other organisations are making the same shift, so this reads as one shop's remediation rather than a trend anyone has counted.
Headline oversells the near-miss, undersells the actual leak
'3 Mistakes That Cost Us' promises a bill the piece never itemises: the fork-PR build touched a metadata endpoint and took nothing, and the Docker socket was caught by a reviewer rather than an attacker. Meanwhile the one thing that demonstrably went wrong — a previous job's temporary credentials file surviving in a workspace and turning up unmasked in an unrelated job's artifacts — is buried two thirds down as an aside. The technical reasoning is sober and mostly under-claimed; the framing around it leans on the near-miss because near-misses make better openings than housekeeping failures.
Reputation-building, nothing to sell
The post routes readers from dev.to to kuryzhev.cloud, which is the usual arrangement for an engineer building a public track record — a mild reason to make the story tidier and the lessons more quotable than they were at the time. Against that: no product, no sponsor, no vendor recommendation, no consultancy pitch, and the author volunteers three unflattering facts about their own operation. The pressure runs toward narrative polish, not toward selling anything.
Trust the design lesson, not the details
Confidence splits cleanly. The architectural claims are the sort you can confirm in your own cluster in ten minutes, and they survive without the storyteller. The specifics — how close the metadata endpoint call came, what a build step could actually have reached in the controller JVM, how widespread socket mounts are — rest entirely on one uncorroborated account with no dates, no scope and no second observer, and the piece is cut off before the hardened pod template it builds toward.