Sign inSign up

n8500x/jenkins-jnlp-agent

By n8500x

Updated 4 days ago

Patched Jenkins inbound (JNLP) agent on Alpine, hardened for OpenShift restricted-v2

Image
0

180

n8500x/jenkins-jnlp-agent repository overview

jenkins-jnlp-agent

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

What's inside

Base imagejenkins/inbound-agent:3391.va_37fa_a_305d6d-1-alpine3.24-jdk21
OSAlpine Linux 3.24
JDKTemurin 21.0.12.1 LTS
Remoting3391.va_37fa_a_305d6d
TimezoneAsia/Dubai (override with --build-arg TZ=...)
Default userjenkins (uid 1000), overridden by OpenShift
Size157 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.

Why Alpine

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 trixieAlpine 3.24
Packages13046
Critical / High / Medium13 / 43 / 1190 / 0 / 0
Fixes available upstream0 of 175all 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.

Running on OpenShift

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 behaviourConsequence on OCPFix
/home/jenkins is owned by jenkins(1000) and not group-writableThe assigned UID cannot create the remoting work directorychgrp -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) failBuilds 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
Deploying

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.

Notes for on-prem clusters
  • Use WebSocket. 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.
  • Air-gapped clusters cannot reach Docker Hub. Mirror the image into the internal registry first:
    oc image mirror docker.io/n8500x/jenkins-jnlp-agent:latest \
      image-registry.openshift-image-registry.svc:5000/<namespace>/jenkins-jnlp-agent:latest
    
  • Internal CA. If the controller route is served by a private CA, pass the bundle to remoting with REMOTING_OPTS="-cert @/path/to/ca.crt" rather than disabling TLS verification.

Building

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.

Files

PathPurpose
DockerfileAlpine build — this is what latest is
Dockerfile.debianDebian trixie equivalent, for glibc
ocp-entrypointRegisters the arbitrary UID in /etc/passwd, then execs the stock jenkins-agent
openshift/podtemplate.yamlKubernetes-plugin pod template
openshift/deployment.yamlStandalone permanent-agent Deployment
.push_tagTimestamp tag of the most recent push

Tag summary

Content type

Image

Digest

sha256:faa8b1a15

Size

94.7 MB

Last updated

4 days ago

docker pull n8500x/jenkins-jnlp-agent