Skip to content

Security Reference

Known risks, mitigations, and hardening decisions for this Jenkins platform.


Docker socket mount

Risk: Critical

/var/run/docker.sock is mounted into the Jenkins controller container. Any pipeline running on this instance can use the socket to create containers, mount host paths, and escape to the host machine with root-equivalent access.

Mitigations in place: - Fork PR trust set to Contributors — untrusted fork PRs require manual approval before running - Webhook signature verification prevents fake build triggers - Only GitHub org members can trigger builds via the developer role

Remaining exposure: Any code running in a build agent (your own repos) can access the socket. This is an accepted trade-off for a single-developer platform on a personal machine. For a shared team environment, replace the socket mount with a dedicated Docker daemon over TCP or use Kaniko for container builds.


Untrusted Jenkinsfile execution (fork PRs)

Risk: Critical → mitigated

The GitHub Organization Folder auto-discovers and runs Jenkinsfile from every repo, including PRs from forks. A malicious fork PR with a crafted Jenkinsfile could execute arbitrary code in your build environment.

Mitigation: During the Organization Folder setup (Quick Deploy Step 8), set Discover fork pull requests → Trust to:

Setting Behavior
Contributors Fork PRs only run if the author has a previously merged PR — recommended
Nobody All fork PRs require manual approval — maximum security
Everyone All fork PRs run automatically — do not use

Webhook secret

Risk: High → mitigated

Without a webhook secret, anyone who discovers your webhook URL can send fake GitHub events and trigger builds.

Mitigation: GITHUB_WEBHOOK_SECRET is set in .env and registered as a Jenkins credential. The GitHub plugin verifies the HMAC signature on every incoming webhook, rejecting requests that don't match.

To generate a secret:

openssl rand -hex 32

Set the same value in your .env and in the Jenkins CI App settings on GitHub.


GitHub org restriction

Risk: High → mitigated

Without org restriction, any GitHub user who can authenticate via OAuth gets the developer role and can trigger builds.

Mitigation: The developer role in casc.yml is assigned to ${GITHUB_ORG} — only members of that org (or, for personal accounts, only that username's group) receive build permissions. Non-members can authenticate but receive no permissions.

For personal accounts: set GITHUB_ORG in .env to your GitHub username.


Build agents running as root

Risk: High → partially mitigated

The Docker agent attach connector no longer explicitly requests root — agents run as the container image's default user. However, most official Docker Hub images (python:3.14, node:20, etc.) default to root internally.

Full mitigation path: Build custom agent images that create a non-root user and set USER in the Dockerfile. Example:

FROM python:3.14
RUN useradd -m -u 1000 jenkins
USER jenkins
WORKDIR /home/jenkins/agent

Reference this image in casc.yml instead of the official image. This prevents privilege escalation within the container and limits the blast radius of a container escape.


Shared library trust

Risk: High

jenkins-shared-library runs as system-level Groovy inside Jenkins — not sandboxed like pipeline script. If that repo is compromised, all pipelines on the platform are affected.

Mitigations: - Enable branch protection on jenkins-shared-library with required PR reviews - Never allow direct pushes to main on that repo - Audit changes to the shared library carefully — they have elevated trust compared to project Jenkinsfiles


Docker image pinning

Risk: Medium

Agent templates reference images by tag (python:3.14, node:20). Tags are mutable — a compromised or broken image update could silently affect all builds.

Recommended mitigation: Pin images to their digest in casc.yml:

dockerTemplateBase:
  image: "python:3.14@sha256:<digest>"

Get the digest:

docker pull python:3.14
docker inspect python:3.14 --format='{{index .RepoDigests 0}}'

Update digests during planned maintenance windows rather than tracking floating tags.


Secret leakage in build logs

Risk: Medium

Jenkins masks credentials() values in build logs but does not catch all variants — base64-encoded secrets, values split across lines, or secrets written to files can appear in logs unmasked.

Best practices: - Never echo a credential value in a pipeline script - Use withCredentials blocks and let Jenkins handle masking - Set log retention limits — Manage Jenkins → Configure System → Discard Old Builds (already set to 30 days) - Restrict log read access to the developer role and above (enforced by Role Strategy)


Rate limiting

Risk: Low

The Jenkins URL is publicly accessible via Cloudflare Tunnel with no rate limiting. Login enumeration and credential stuffing are possible.

Mitigation: Enable Cloudflare rate limiting on the tunnel domain:

  1. Go to Cloudflare dashboard → Security → WAF → Rate limiting rules
  2. Create a rule targeting your Jenkins domain
  3. Recommended: limit to 20 requests/minute per IP on /login and /securityRealm/
  4. Action: Block or Challenge

Cloudflare's free plan includes basic rate limiting. This is separate from the tunnel configuration and does not require changes to config.yml.


Private key on disk

Risk: Low

github-app.pem is stored as a plaintext file on the host machine. If the host is compromised, the key is immediately available.

Mitigations in place: - Mounted read-only into the container (:ro flag in docker-compose.yml) - Listed in .gitignore — cannot be accidentally committed - Limited to only the Jenkins container process

Further hardening: Store the key in a secrets manager (HashiCorp Vault, AWS Secrets Manager) and inject it at runtime rather than reading from disk. This is outside the scope of this platform's current architecture but is the recommended path for production team deployments.