Architecting Just-in-Time Privileged Access

Standing privilege is the single largest attack surface in most enterprise identity estates, and it persists because provisioning teams default to convenience over containment. A service account with permanent administrator rights on a domain controller, or an SRE with an always-on AdministratorAccess IAM policy, represents a credential that an attacker only needs to steal once. Just-in-time privileged access inverts this model: privilege is minted on demand, scoped to a task, bound to a session, and destroyed on expiry. This article defines the control plane required to implement just-in-time privileged access at enterprise scale, without introducing approval latency that drives engineers back to shadow-IT workarounds.
#The Standing Privilege Problem
Traditional Privileged Access Management (PAM) vaults solved credential storage — rotating passwords, checking secrets in and out — but they rarely solved credential duration. A user checks out a domain admin password, uses it for eight minutes, and the credential remains valid until the next scheduled rotation, often 24 to 90 days later. That window is where lateral movement happens. Static role bindings in Kubernetes RBAC, permanent sudoers entries, and long-lived cloud IAM roles all share this same flaw: the authorisation boundary is set at provisioning time, not at request time.
Just-in-time privileged access moves the authorisation decision to the moment of use. Instead of asking “does this identity have the role?”, the control plane asks “does this identity have a validated, time-boxed, contextually justified reason to hold this role right now?”. This is a fundamentally different data model — it requires an active broker, a policy engine, and a revocation mechanism, not just a credential store.
Part 2: Non-Human Identity for Autonomous Agents
#Architectural Breakdown: Decoupling Identity from Standing Access
A production-grade just-in-time privileged access system has four discrete components, and conflating any two of them creates a single point of failure:
- Policy Decision Point (PDP) — evaluates the elevation request against ABAC/RBAC rules, business hours, risk score, and approval chain requirements. Typically implemented with Open Policy Agent (OPA) or a commercial PAM policy engine.
- Credential Broker — mints the ephemeral credential (short-lived IAM role, dynamic database secret, temporary AD group membership, or SSH certificate) only after the PDP returns an
allowdecision. - Session Binding Layer — ties the minted credential to a specific host, source IP, or client certificate so it cannot be replayed outside the originating session.
- Revocation and Audit Ledger — an append-only record of every grant, with a TTL-triggered revocation worker that actively tears down access rather than relying on passive expiry.
The critical architectural decision is that the broker never issues standing credentials — it issues leases
#Broker-Mediated Session Issuance
The broker must be the only entity capable of writing to the privileged store. If engineers can still directly modify an IAM policy or an AD group via a break-glass path outside the broker, the entire just-in-time privileged access model degrades into an audit exercise rather than an enforcement boundary. This means CI/CD pipelines, Terraform runners, and even platform administrators must request elevation through the same PDP as end users — no bypass lane except a heavily instrumented emergency break-glass procedure with mandatory post-incident review.
#Implementation Logic: The JIT Elevation Workflow
The workflow below assumes an OPA-based PDP, a Vault broker, and short-lived AWS IAM sessions as the elevation target, but the same sequence maps cleanly onto Azure PIM, AD LAPS, or SSH certificate authorities.
#Step 1: Request Intake and Policy Evaluation
The engineer submits a structured elevation request — role, resource scope, justification, and requested duration — through a CLI or chatops interface. This request is forwarded to OPA as a JSON input document, and the PDP returns a decision plus a maximum permitted TTL.

1{
2 "input": {
3 "subject": "engineer.jsmith",
4 "requested_role": "prod-db-admin",
5 "resource": "rds:orders-primary",
6 "justification_ticket": "INC-48213",
7 "requested_ttl_minutes": 30,
8 "source_ip": "10.42.6.19",
9 "mfa_verified": true
10 }
11}Note that the policy hard-caps TTL per role rather than trusting the requested value — this is the control that prevents privilege duration creep, where a “temporary” 8-hour grant becomes the new normal because nobody pushed back on the request.
#Step 2: Ephemeral Credential Minting
Once OPA returns allow: true, the broker mints the credential with an enforced expiry. For AWS, this is an STS session; for a database, a dynamically generated user with a matching TTL in the database’s own credential table.
1vault write aws/sts/prod-db-admin-role
2 ttl=30m
3 policy_arns="arn:aws:iam::aws:policy/AmazonRDSFullAccess"
4 policy_document=@scoped-orders-primary.json
5
6# Vault returns short-lived STS credentials
7# lease_id: aws/sts/prod-db-admin-role/2f1a9c3e-...
8# lease_duration: 1800The lease_id is the linchpin of the system. It is the handle the revocation ledger uses to force-expire the credential independently of AWS’s own STS timeout, which matters when a request is later flagged as anomalous mid-session.
#Step 3: Session Binding and Revocation
The revocation worker polls the ledger every few seconds for leases
1vault lease revoke aws/sts/prod-db-admin-role/2f1a9c3e-4b7d-4e91-8a2f-1c9e0d4b7a11For Kubernetes-native workloads, the equivalent binding is a time-boxed RoleBinding generated by the broker rather than a static manifest checked into Git:
1apiVersion: rbac.authorization.k8s.io/v1
2kind: RoleBinding
3metadata:
4 name: jit-jsmith-orders-db-admin
5 namespace: prod-orders
6 annotations:
7 jit.broker/expires-at: "2024-06-01T14:32:00Z"
8 jit.broker/ticket: "INC-48213"
9roleRef:
10 apiGroup: rbac.authorization.k8s.io
11 kind: Role
12 name: db-admin
13subjects:
14- kind: User
15 name: jsmith@corp.example.com
16 apiGroup: rbac.authorization.k8s.ioA sidecar controller watches for the expires-at annotation and deletes the RoleBinding object directly — Kubernetes has no native TTL primitive for RBAC objects, so this reconciliation loop is mandatory, not optional. This is the same reconciliation pattern used across broader architectural patterns for declarative infrastructure, applied here to the identity plane instead of workload scheduling.
#Failure Modes and Edge Cases in Just-in-Time Privileged Access
The elegance of just-in-time privileged access on paper collapses quickly if the following edge cases are not explicitly handled:
Clock skew between broker and target system. If the credential broker’s TTL clock and the target IAM provider’s session clock drift by even 90 seconds, revocation can race against active use, either killing a legitimate operation early or leaving a window open past the intended boundary. NTP synchronisation with sub-second tolerance is a hard requirement, not a nice-to-have, for any just-in-time privileged access broker.

Cached credentials outliving the lease. Client-side SDKs frequently cache STS tokens in memory or on disk (~/.aws/cli/cache) beyond the logical session. Revoking the lease server-side does nothing if the target system doesn’t validate against a live session store — this is why database-native dynamic secrets, which require the credential to exist in the database’s own auth table, are more robust than IAM-only revocation for data-plane access.
Approval fatigue and rubber-stamping. When every elevation request routes through a human approver, and volume exceeds roughly 40-50 requests per engineer per week, approvers begin approving without reading justification fields. The fix is tiering: low-risk, well-scoped requests (read-only prod access for 15 minutes) auto-approve via policy; high-blast-radius requests (root on a payment-processing host) require dual human sign-off with a mandatory justification length check.
Break-glass path abuse. Every just-in-time privileged access architecture needs an emergency bypass for outages where the PDP itself is unreachable. If that break-glass credential is a static, long-lived secret stored in a sealed envelope, it becomes the softest target in the entire system. Break-glass credentials should themselves be single-use, auto-rotated after invocation, and trigger a mandatory incident review regardless of whether the outage was real.
Replay via session token forwarding. Without source-IP or mTLS
#Scaling and Security Trade-offs
Rolling just-in-time privileged access out across an estate with tens of thousands of identities and hundreds of privileged roles forces explicit trade-offs between latency, auditability, and operational overhead:
- PDP latency vs. policy expressiveness — a Rego policy set with deep attribute lookups against an external HR system can push decision latency past 200ms; cache attribute lookups locally with a short TTL (60-120s) to keep p99 elevation time under 1 second.
- Centralised broker vs. regional brokers — a single global broker simplifies the audit ledger but becomes a hard dependency for every privileged action worldwide; regional brokers with an eventually-consistent replicated ledger reduce blast radius but complicate cross-region revocation guarantees.
- Fine-grained TTLs vs. approver cognitive load — per-role TTL caps (10-30 minutes) shrink the exposure window but increase request volume; batching related sub-tasks under a single elevation ticket reduces friction without widening the window.
- Dynamic secrets vs. legacy systems — mainframes and some on-prem LDAP directories cannot accept dynamically generated accounts; for these, just-in-time privileged access degrades to time-boxed group membership with a forced logoff script, which is weaker but still materially better than standing membership.
- Audit completeness vs. storage cost — full session recording (keystroke or screen capture) for every privileged session generates significant storage overhead at scale; reserve full recording for the highest-risk role tiers and rely on structured API-call logging for the rest.
None of these trade-offs eliminate residual risk entirely — they shift the attacker’s effort from credential theft to real-time session hijacking, which is a narrower and more detectable attack surface. That shift, not the elimination of risk itself, is the actual return on investment for building out just-in-time privileged access as core enterprise infrastructure rather than treating it as a PAM vault feature checkbox.
Comments
Add a thoughtful note on Architecting Just-in-Time Privileged Access. Comments are checked for spam and held for moderation before appearing.





