Patched Jenkins inbound (JNLP) agent on Alpine, hardened for OpenShift restricted-v2
180
A patched Jenkins inbound (JNLP) agent image, hardened to run under OpenShift's default restricted-v2 SCC — and built on Alpine so it scans clean.
docker pull n8500x/jenkins-jnlp-agent
| Base image | jenkins/inbound-agent:3391.va_37fa_a_305d6d-1-alpine3.24-jdk21 |
| OS | Alpine Linux 3.24 |
| JDK | Temurin 21.0.12.1 LTS |
| Remoting | 3391.va_37fa_a_305d6d |
| Timezone | Asia/Dubai (override with --build-arg TZ=...) |
| Default user | jenkins (uid 1000), overridden by OpenShift |
| Size | 157 MB |
The upstream entrypoint, WORKDIR and AGENT_WORKDIR are inherited unchanged, so every option and environment variable documented upstream — JENKINS_URL, JENKINS_SECRET, JENKINS_AGENT_NAME, JENKINS_WEB_SOCKET, REMOTING_OPTS and the rest — behaves identically here.
Tags are latest plus a YYYYMMDD-HHMMSS build stamp; both point at the same digest.
Scanner findings track package count, and a bare JNLP agent needs almost none of what a general-purpose distro ships. Measured with Trivy (OS packages, same remoting version, same JDK):
| Debian trixie | Alpine 3.24 | |
|---|---|---|
| Packages | 130 | 46 |
| Critical / High / Medium | 13 / 43 / 119 | 0 / 0 / 0 |
| Fixes available upstream | 0 of 175 | all four, and applied |
The Debian result is the important one: not a single one of its 175 findings had a fix available, so apt-get upgrade cannot improve it — rebuilding just reproduces the same list. Twelve of its thirteen criticals were in perl (perl, perl-base, perl-modules-5.40, libperl5.40), which Alpine does not ship at all; the thirteenth was openssh-client. Alpine's only findings were four libexpat CVEs, all fixed by apk upgrade in the build, leaving the published image at zero.
Caveat: musl, not glibc. Fine for a bare agent, but glibc-linked binaries — AWS CLI v2 is the usual casualty — will not run if you later add tooling. If you need glibc, build
Dockerfile.debian instead and accept the scan noise.
OpenShift ignores the image's USER and runs the pod as an arbitrary, per-namespace UID that belongs to group 0 and has no /etc/passwd entry. The stock agent image breaks in two ways under those conditions, both fixed here:
| Upstream behaviour | Consequence on OCP | Fix |
|---|---|---|
/home/jenkins is owned by jenkins(1000) and not group-writable | The assigned UID cannot create the remoting work directory | chgrp -R 0 + chmod -R g=u on /home/jenkins and /usr/share/jenkins, giving group 0 the owner's rights |
The UID has no passwd entry, so CRI-O falls back to HOME=/, and getpwuid() callers (git, ssh) fail | Builds write to the wrong place, or abort with "no passwd entry for uid" | HOME is set in the image, and ocp-entrypoint appends a passwd entry for the running UID before handing off to the stock agent script |
The result needs no anyuid SCC, no privileged access and no added capabilities — it runs under restricted-v2 as shipped.
$ docker run --rm --user 1000750000:0 n8500x/jenkins-jnlp-agent -version
3391.va_37fa_a_305d6d
Two ready-to-use manifests are in openshift/:
podtemplate.yaml — a pod template for the Jenkins Kubernetes plugin, for ephemeral per-build agents. Reference it with agent { kubernetes { yamlFile 'openshift/podtemplate.yaml' } }.deployment.yaml — a standalone long-lived agent registered as a permanent node on the controller.oc create secret generic jenkins-agent-secret --from-literal=secret='<agent secret>'
oc apply -f openshift/deployment.yaml
Do not set runAsUser in either manifest. Letting OpenShift assign the namespace UID is what the entrypoint is built for; pinning uid 1000 will be rejected by restricted-v2 anyway.
JENKINS_WEB_SOCKET=true lets the agent connect out over the controller's existing HTTPS route, so you do not have to expose a separate inbound TCP agent port through the OCP router.oc image mirror docker.io/n8500x/jenkins-jnlp-agent:latest \
image-registry.openshift-image-registry.svc:5000/<namespace>/jenkins-jnlp-agent:latest
REMOTING_OPTS="-cert @/path/to/ca.crt" rather than disabling TLS verification.docker build -t n8500x/jenkins-jnlp-agent:latest . # Alpine, the published image
docker build -f Dockerfile.debian -t jnlp:debian . # Debian trixie equivalent
docker build --build-arg TZ=UTC -t jnlp:utc .
Alpine's apk repositories are HTTPS. On a host that intercepts TLS, apk update fails with "server certificate not trusted" — build with the repositories temporarily flipped to http:// and flipped back inside the same RUN, so no layer ships a plain-HTTP repo list. APKINDEX signatures are verified by apk regardless of transport, so integrity is preserved. The Debian build needs no such workaround: deb.debian.org is plain HTTP already.
| Path | Purpose |
|---|---|
Dockerfile | Alpine build — this is what latest is |
Dockerfile.debian | Debian trixie equivalent, for glibc |
ocp-entrypoint | Registers the arbitrary UID in /etc/passwd, then execs the stock jenkins-agent |
openshift/podtemplate.yaml | Kubernetes-plugin pod template |
openshift/deployment.yaml | Standalone permanent-agent Deployment |
.push_tag | Timestamp tag of the most recent push |
Content type
Image
Digest
sha256:faa8b1a15…
Size
94.7 MB
Last updated
4 days ago
docker pull n8500x/jenkins-jnlp-agent