The Jenkins Permissions Problem
Your Jenkins server has AdministratorAccess. You know this. Your security team knows this. Everyone agrees it should be fixed. And yet it persists — because fixing it is hard and breaking it is worse.
Here’s how it happened:
- The first pipeline needed
s3:PutObjectto deploy an artifact. A developer added S3 access to the Jenkins role. - The second pipeline needed
ecs:UpdateService. ECS permissions were added. - The third pipeline needed
ecr:PushImage. ECR permissions were added. - By pipeline number 15, the IAM policy was 200 lines long, nobody fully understood it, and someone said “just give it Admin and we’ll scope it down later.”
- “Later” never came.
Now your Jenkins server — a build host that runs arbitrary code from every PR in your organization — has the keys to your entire AWS account. Permanently. 24/7. Even when no builds are running.
Why This Is Your Biggest Blast Radius
Human access has improved. Your engineers use SSO, MFA, and maybe even JIT for cloud console access. But the Jenkins server:
- Runs 24/7 with permanent credentials.
- Executes untrusted code (every dependency in every build is code the pipeline runs).
- Has a single static role that doesn’t rotate because rotation breaks 47 pipelines.
- Cannot be MFA’d (it’s a machine identity).
- Audit stops at the role name. CloudTrail says
JenkinsDeployRoledeleted the bucket. Which build? Which commit? Which engineer’s PR triggered it? The trail ends.
If someone pops shell on your Jenkins box — through a vulnerable plugin, a malicious dependency, a compromised build step — they inherit AdministratorAccess. Not for 30 seconds while a deploy runs. Forever. Until someone notices and rotates the role.
This isn’t hypothetical. Supply chain attacks targeting CI/CD systems are among the most common attack vectors in modern cloud environments. Your Jenkins server is simultaneously your most powerful non-human identity and your least governed one.
The Insight: Pipelines Need Privilege for Seconds, Not Hours
Look at a typical Jenkins pipeline:
Pipeline: my-app (build #142)
Total duration: 4 minutes
00:00 - 01:30 Checkout code, run tests → needs: nothing
01:30 - 02:00 Build container image → needs: nothing
02:00 - 02:30 Push to ECR → needs: ecr:PushImage (30 seconds)
02:30 - 03:30 Run integration tests → needs: nothing
03:30 - 04:00 Deploy to ECS → needs: ecs:UpdateService (30 seconds)
Out of a 4-minute build, the pipeline needs elevated cloud privilege for 60 seconds total. The other 3 minutes are code compilation, test execution, and container building — operations that need zero AWS permissions.
Yet the Jenkins role has AdministratorAccess for all 4 minutes. And for the 23 hours and 56 minutes between builds. And overnight. And on weekends.
Agentic JIT: Elevate for the Step, Not the Pipeline
Cloudanix Agentic JIT flips the model: the Jenkins role starts with zero (or minimal) privileges. The pipeline calls elevate() at the moment it needs cloud access, runs the privileged operation, and calls revoke() when it’s done. The privilege exists for 30 seconds, not permanently.
How It Works with Jenkins
One-time setup:
- Register Jenkins as an Agent in Cloudanix Console (name, use case: Build Workflow, max session: 15 minutes).
- Define the cloud boundary: which AWS account, which IAM role, which policies this agent is ever allowed.
- Add two secrets to Jenkins:
CLOUDANIX_AUTH_TOKENandCLOUDANIX_AGENT_ID. - Add the Cloudanix Jenkins shared library to your pipeline.
Per-pipeline usage:
@Library('cloudanix-jit') _
pipeline {
agent any
stages {
stage('Test') {
steps { sh 'npm test' } // No elevation needed
}
stage('Build') {
steps { sh 'docker build .' } // No elevation needed
}
stage('Deploy') {
steps {
cloudanixElevate(policy: 'AmazonECS-FullAccess', ttl: '15m') {
sh 'aws ecs update-service --cluster prod --service api'
}
}
}
}
}
The cloudanixElevate() block:
- Calls the Cloudanix API with the policy request and TTL.
- Cloudanix verifies the request is within the agent’s boundary (permitted policies, max duration).
- Policy is attached to the Jenkins IAM role.
- The enclosed block executes (the
aws ecscommand runs with the now-elevated role). - When the block exits (success or failure), the policy is detached immediately.
- If the block fails to call revoke (crash, timeout), the TTL hard ceiling revokes automatically.
Elevation duration: 38 seconds out of a 4-minute build. Not 24 hours.
What the Audit Trail Shows
Elevation: elev-9c11f3a8
Agent: jenkins-deploy-agent
Build: my-app #142
Commit: a7c1f24
Policy: AmazonECS-FullAccess
TTL: 15m (used 38s)
Actions:
[14:00:18] ecs:UpdateService → cluster prod → service api
[14:00:19] ecs:DescribeServices → cluster prod
Revoked: 14:00:52 (SDK call, build step finished)
CloudTrail no longer says “JenkinsDeployRole did it.” The Cloudanix audit says: “Build #142, commit a7c1f24, triggered by engineer-X’s PR, elevated for 38 seconds, called ecs:UpdateService on the prod cluster, and the policy was detached.”
The audit chain: action → agent → build → commit → engineer. Forensics-grade. SOC 2-ready.
Beyond Jenkins: Every CI/CD System
The same pattern applies to:
| Platform | Integration |
|---|---|
| Jenkins | Shared library (@Library('cloudanix-jit')) |
| GitHub Actions | Cloudanix Action (marketplace) |
| GitLab CI | CI template (include in .gitlab-ci.yml) |
| Bitbucket Pipelines | Bitbucket Pipe |
| CircleCI | Orb |
| ArgoCD | SDK call in sync hooks |
| AWS CodeBuild | SDK call in buildspec |
| Anything with HTTPS | Raw API call |
One model for every CI/CD system. Same elevate() → action → revoke() contract. Same audit trail. Same boundary enforcement.
The Boundary: What Pipelines Can Never Request
When you register a Jenkins agent in Cloudanix, you define its permitted policies: the set of IAM policies this agent is ever allowed to be granted. The pipeline can request any policy within this set. Requests outside the set are denied at the API:
Agent: jenkins-deploy-agent
Permitted policies:
- AmazonECS-FullAccess
- AmazonECR-PushImage
- AmazonS3-DeployBucket-Write
Max session: 15 minutes
Denied: AdministratorAccess (not in permitted set)
Denied: AmazonRDS-FullAccess (not in permitted set)
Denied: AmazonEC2-FullAccess (not in permitted set)
A compromised build step that tries to escalate beyond the agent’s boundary gets a 403. The pipeline can’t request privileges it was never registered to receive. The boundary is enforced at the Cloudanix API, not in the pipeline code.
What About Long-Lived Secrets?
“But you’re still putting secrets in Jenkins (CLOUDANIX_AUTH_TOKEN).”
True. But compare the blast radius:
| Scenario | Standing Admin | Agentic JIT |
|---|---|---|
| Token stolen | Full AdministratorAccess 24/7 | Can request specific policies for max 15 min, boundary-limited |
| Pipeline compromise | Full AWS account access | Limited to agent’s permitted policies, for declared TTL |
| Credential rotation | Requires coordinating 47 pipelines | Rotate one auth token in Jenkins secret store |
| Audit after incident | “JenkinsDeployRole did something” | “Build #142, commit abc, policy X, 38 seconds” |
The auth token grants the ability to request elevation within a boundary. It doesn’t grant standing cloud access. A stolen token without the pipeline context (specific build, specific step) provides limited value — and the boundary prevents escalation beyond the permitted set.
The ROI: Security Teams and Platform Teams Both Win
Security Team Gets
- Zero standing NHI (non-human identity) access.
- Every pipeline action linked to agent → build → commit → engineer.
- Boundary enforcement that prevents privilege escalation.
- Evidence-grade audit for SOC 2 / ISO / PCI.
- Blast radius reduced from “entire account” to “30-second window with bounded policies.”
Platform Team Gets
- No more 200-line IAM policies to maintain per pipeline.
- No more “just give it Admin” conversations.
- Clear, code-reviewable policy declarations in Jenkinsfiles.
- One rotation target per pipeline (auth token) instead of managing the underlying IAM role policy.
- Faster debugging: elevation failures point to boundary mismatches, not opaque IAM denials.
Developers Get
- Pipeline still works the same way from their perspective.
- PR reviews include the
cloudanixElevate(policy: 'X', ttl: 'Y')declaration — visible security, not hidden configuration. - No more “the Jenkins role doesn’t have permission, ask DevOps to add it.” The request is in the code.
Still Running Jenkins with AdministratorAccess?
If your CI/CD pipelines have standing broad permissions — and you’ve been meaning to scope them down “when there’s time” — Agentic JIT eliminates standing pipeline access without requiring you to decompose your 200-line IAM policy into per-pipeline scoped roles. Register the agent, declare elevations in the pipeline, and the 200-line policy becomes irrelevant because the role starts empty and fills only for the seconds it needs privilege.
Learn more about Agentic JIT or book a demo to see Jenkins elevation in action.