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:
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:
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:
Get the digest:
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:
- Go to Cloudflare dashboard → Security → WAF → Rate limiting rules
- Create a rule targeting your Jenkins domain
- Recommended: limit to 20 requests/minute per IP on
/loginand/securityRealm/ - 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.