Cloud, Infra & Least-Privilege Access
Design access that enables engineers without handing out the keys
Least-privilege & JIT access
After this you can design a least-privilege, just-in-time cloud access system.
Cursor's job description names one project outright: build a least-privilege and JIT system for cloud access that enables engineers without granting unnecessary standing permissions. If you walk into the onsite with a clean design for this, you are answering the question they wrote down.
The threat you are designing against is mundane and constant. An engineer's laptop gets phished, a CI token leaks into a build log, a session cookie is stolen. The damage is bounded by exactly one thing: what that identity could do at the moment of compromise. Standing access is the variable you control.
So the first principle is blunt. Permanent permissions are stored attacker value, sitting there whether anyone uses them or not.
Every role a human or service holds 24/7 is a permission an attacker inherits the instant they take over that identity. The goal is not to make access hard. It is to make most access absent most of the time, so that on any given Tuesday the blast radiusHow much breaks if a change goes wrong; the scope of potential damage. of a stolen credential is close to nothing.
The JIT flow, end to endrequest → approve → time-box → revoke → audit
Just-in-time access means an engineer holds no production privilege by default and elevates only when there is a task and a reason. The grant is scoped, short and self-expiring. Walk the loop in order.
- 1Request. The engineer asks for a specific role on a specific resource, with a reason and a duration. "Read access to the billing DB for 30 minutes to debug ticket #4821," not "admin on prod."
- 2Approve. A policy decides: auto-approve low-risk scopes, route high-risk ones to a human or a second approver. The reason and the ticket travel with the request so the approver isn't guessing.
- 3Time-boxed grant. The system mints a short-lived credential or attaches a role binding with a hard TTL. The clock starts immediately.
- 4Auto-revoke. On expiry the grant is removed with no human in the loop. Revocation is the default, not a cleanup job someone has to remember.
- 5Audit. Who requested what, who approved it, what they touched during the window and when it expired - all logged immutably for later review and detection.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Approval and audit are the gates; expiry is enforced at the identity layer, not by a background job.
The detail that separates a real design from a hand-wave is expiry must not depend on the happy path. If revocation only fires when a tidy background process runs, a crash leaves the grant standing. Time-box at the identity layer (short-lived tokens that simply stop being valid) so that doing nothing is the safe outcome.
Break-glass without permanent backdoorsincidents are when controls get bypassed badly
At 3am during an outage, nobody should be blocked waiting for an approver who is asleep. But the fix for that cannot be a standing god-mode role, because that role is the exact thing you spent the rest of the design eliminating. Break-glass is a controlled escape hatch with its own rules.
Self-service elevation that works without an approver, but fires a loud alert to the security channel the moment it's used.
Same TTL and auto-revoke as any other grant - the emergency doesn't buy permanence.
Mandatory post-hoc review: every break-glass use is read by a human within a day.
A shared admin account whose credentials live in a password manager.
A standing "on-call" role nobody revokes because removing it might break the next incident.
Silent elevation that no one notices until the audit, if anyone audits at all.
Friction is a security risk toodeveloper empathy is in the JD for a reason
A control that is annoying enough gets routed around. Engineers share a long-lived key in Slack because the JIT flow took four minutes and they were in a hurry. Now you have a worse problem than you started with and you can't even see it. The role explicitly asks for frictionless security, so treat speed as a requirement, not a nicety.
- Low-risk, read-only scopes
- Auto-approve, sub-second grant, no human in the loop. Make the paved road faster than the workaround.
- Write or destructive scopes
- Require a reason and maybe a second approver; this is where a few seconds of friction buys real safety.
- Renewal
- One click to extend an active window, so people don't pre-request huge durations "just in case."
- Discovery
- Make it obvious what you can request and how; hidden permissions get hoarded.
When they hand you the JIT design prompt, state the loop first (request, approve, time-box, revoke, audit), then immediately name the two failure modes that make it real: expiry that doesn't depend on a healthy background job and a break-glass path that is loud and TTL'd rather than a standing backdoor. Close by naming developer friction as a security risk. That arc shows you've built this, not just read about it.
Takeaway. Eliminate standing access: hold nothing by default, elevate just-in-time with a scoped, self-expiring grant, make break-glass loud-and-temporary and keep the paved road faster than the workaround so friction never pushes people to long-lived keys.
Self-check
QIn a JIT access design, why is it dangerous to implement grant expiry as a background job that periodically removes expired role bindings?
IAM & cloud hardening
After this you can reason about IAM models and common cloud misconfigurations.
Most cloud breaches are not exotic. They are an over-broad role, a public bucket or a metadata endpoint reachable from a request you didn't sanitize. The hardening round tests whether you can name the misconfiguration classes cold and reason about why each one happens.
Identity-based vs resource-based policiestwo sides of the same access decision
An access decision in the cloud is the intersection of two questions: what is this identity allowed to do and what does this resource allow to be done to it. Get the distinction crisp because interviewers probe it.
- Policy type
- Identity-based
- Attached to
- A user, group or role
- Answers
- "What can this principal do across resources?" - the role's own permissions.
- Policy type
- Resource-based
- Attached to
- A bucket, queue, KMS key, function
- Answers
- "Who, including cross-account principals, may act on this resource?"
- Policy type
- Effective access
- Attached to
- The intersection (mostly)
- Answers
- Both must allow; an explicit deny on either side wins. This is where surprises hide.
| Policy type | Attached to | Answers |
|---|---|---|
| Identity-based | A user, group or role | "What can this principal do across resources?" - the role's own permissions. |
| Resource-based | A bucket, queue, KMS key, function | "Who, including cross-account principals, may act on this resource?" |
| Effective access | The intersection (mostly) | Both must allow; an explicit deny on either side wins. This is where surprises hide. |
The classic over-share is a resource policy that allows a wildcard principal, granting access nobody's identity policy ever mentioned.
The misconfiguration classes to name on sightthese recur across every cloud
Wildcards in actions or resources (s3:* on *).
"Temporary" admin grants that became permanent.
Fix: scope to specific actions and ARNs; review with access analyzers that flag unused permissions.
Buckets open to the internet or to a wildcard principal.
Snapshots and AMIs shared publicly by accident.
Fix: block-public-access at the account level; default deny; alert on any policy that adds a public grant.
An SSRF in your app fetches the instance metadata service and reads the attached role's credentials.
This turns a web bug into cloud credential theft - the Capital One pattern.
Fix: enforce IMDSv2 (token-required), restrict egress and never give instances roles broader than they need.
Access keys checked into code or baked into images.
No rotation, no expiry, full reach if leaked.
Fix: workload identity / role assumption instead of static keys (next subsection).
The SSRF-to-credentials chain is the one interviewers love because it crosses the app-security and cloud-security boundary the role straddles. If you describe an SSRF and stop at "it can read internal URLs," you've missed the punchline: on a cloud instance with IMDSv1, that internal URL is 169.254.169.254 and the response is a usable IAM credential.
Network segmentation and service-to-service authshrink what's reachable, kill the long-lived key
Two systemic moves shrink the threat surface more than any single fix. First, minimize internet exposure: private subnets by default, public ingress only through a load balancer or gateway and explicit egress controls so a compromised box can't freely phone home. Second, stop authenticating services with secrets they have to carry.
- Anti-pattern
- A static access key in an env var, shared across deploys, rotated never.
- Better
- The workload assumes a role and gets short-lived credentials from the platform - no secret to leak.
- Mechanism
- Cloud workload identity (instance/pod role, OIDCOpenID Connect. A modern standard that powers single sign-on, built on OAuth. federation, mTLS-based service identity in a mesh).
- Payoff
- Credentials expire on their own; identity is verifiable; there's no long-lived key for an attacker to find.
"Cursor's public posture is least-privilege plus MFA plus monitoring, no infrastructure in China and vetted subprocessors. I'd map an IAM review onto that: kill wildcard roles and static keys, enforce IMDSv2 so an app SSRF can't become a credential, default storage to private and move service identity to short-lived workload credentials so there's no standing key to steal."
Takeaway. Effective cloud access is the intersection of identity- and resource-based policy and most breaches trace to four classes - over-broad roles, public storage, exposed metadata endpoints and long-lived static keys - so default to private networks and short-lived workload identity instead of secrets services have to carry.
Self-check
Isolation & sandboxing
After this you can choose isolation primitives for running untrusted/agent code.
This is the section closest to the heart of the role. Cursor's agents read, write and execute code, often code the agent itself just generated, sometimes on infrastructure that also touches customer data. Running that code safely is not a checkbox. It is the product's central security problem.
Treat every agent execution as untrusted by default. The prompt could be poisoned, the generated command could be hostile, a tool call could try to read a file it has no business touching. Your job is to give that code somewhere to run where being hostile doesn't matter.
The isolation spectrumstronger isolation costs latency and density
Isolation primitives trade strength against cost. A bare container starts in milliseconds and packs densely; a full VM is far harder to escape but slow and heavy. Know the rungs and what each one actually defends.
- Primitive
- Plain container
- Isolation boundary
- Shared host kernel; namespaces + cgroups
- Cost / fit
- Fast and dense, but a kernel exploit escapes to the host. Weak boundary for hostile code.
- Primitive
- Hardened container (seccomp, gVisor)
- Isolation boundary
- Filtered syscalls or a user-space kernel shim
- Cost / fit
- Shrinks kernel attack surface a lot for modest overhead. A strong default for agent workloads.
- Primitive
- microVM (Firecracker)
- Isolation boundary
- Real hardware virtualization, minimal device model
- Cost / fit
- VM-grade isolation with ~100ms boots and small footprint. Built for exactly this.
- Primitive
- Full VM
- Isolation boundary
- Full hypervisor, full guest OS
- Cost / fit
- Strongest, slowest, heaviest. Use when you need a complete OS, not for ephemeral code runs.
| Primitive | Isolation boundary | Cost / fit |
|---|---|---|
| Plain container | Shared host kernel; namespaces + cgroups | Fast and dense, but a kernel exploit escapes to the host. Weak boundary for hostile code. |
| Hardened container (seccomp, gVisor) | Filtered syscalls or a user-space kernel shim | Shrinks kernel attack surface a lot for modest overhead. A strong default for agent workloads. |
| microVM (Firecracker) | Real hardware virtualization, minimal device model | VM-grade isolation with ~100ms boots and small footprint. Built for exactly this. |
| Full VM | Full hypervisor, full guest OS | Strongest, slowest, heaviest. Use when you need a complete OS, not for ephemeral code runs. |
For per-task agent execution, the sweet spot is usually a microVM or a gVisor-hardened container: VM-ish isolation without VM-ish startup cost.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Climbing the stack buys a stronger boundary; each rung costs more latency and density.
Container and Kubernetes hardeningthe floor, even when you also have a stronger boundary
Whatever primitive you choose, the container itself should be locked down. These are cheap, well-understood controls and skipping them is what turns a contained process into a host-level foothold.
- Drop all Linux capabilities, then add back only the few a workload genuinely needs.
- Run as a non-root user with a read-only root filesystem; mount writable scratch only where required.
- Apply a
seccompprofile to deny the dangerous syscalls the workload never calls. - Enforce network policy: default-deny egress, so an escaped process can't reach the metadata endpoint or your internal services.
- Forbid privileged pods, host-path mounts and host networking - these dissolve the boundary entirely.
Assume the sandbox breaksdefense in depth, designed around the blast radius
Every isolation layer has a nonzero chance of being escaped. A mature design accepts that and asks the next question: when this sandbox is breached, what does the attacker reach? The answer should be "almost nothing of value."
The sandbox holds no long-lived prod credentials.
If it needs cloud access, give it a narrowly-scoped, short-lived token for that one task.
Default-deny egress; allowlist only what the task needs.
Block the metadata endpoint and internal service CIDRs outright.
One sandbox per task, destroyed after.
No shared state between runs, so a compromise can't persist or poison the next user.
A compromised agent sandbox is annoying. A compromised sandbox that can reach production secrets, the metadata endpoint or another customer's data is a breach. Design so the worst case of an escape is "they trashed a disposable box," by making sure that box never held anything worth stealing and couldn't route to anything that did.
When asked how you'd run agent-generated code, don't just say "a container." Lay out the spectrum, pick a microVM or gVisor with a reason (per-task isolation at acceptable latency), then pivot to defense in depth: "I assume it can be escaped, so the sandbox has no standing secrets, default-deny egress including the metadata endpoint and it's destroyed after one task." Naming the post-escape blast radiusHow much breaks if a change goes wrong; the scope of potential damage. is what reads as senior.
Running an agent against real privileged infrathe same instinct, from the operator's chair
The sandbox model isn't only for code Cursor runs at scale. It's also how a security engineer should drive an agent against their own cloud. Travis, who runs this workshop, is GitHub admin and AWS admin. He uses an agent to run elevated AWS commands every day and never hands it his credentials. The trick is a layer of indirection between the agent and the shell.
He keeps a small helper script and tells the agent to call it - he types something like creds prod. The script sets the credential environment variables in the shell. The agent drives that terminal through a tool, but it is not the terminal: it sees terminal output, not the shell's env properties. So the agent can run aws ... | jq against production while the credentials are never exposed to it. Every elevated command surfaces for his approval and he reads each one.
The agent drives a terminal through a tool, but it is not the terminal - it sees output, never the shell's environment.
"That script sets environment variables of its shell that the agent itself never sees. So now it can run AWS commands that I will approve every single one, but it actually like never had the credentials exposed."
His approval policy is a threat model, not a blanket setting. Reading files runs free almost full-stop. Anything that mutates state or needs network access asks him every single time, because - in his words - his user has more permissions to systems than he's comfortable just handing to the agent. Writing outside the workspace is a non-issue because he works in git workspaces: if the agent changes something, he resets it.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
The gate tracks the user's own permission reach, not the action's name. Threat models are personal.
Travis is candid that his policy is his, not a universal rule: "I honestly don't care if it runs rm -rf on my hard drive - it's not on my top list of 50 things I worried about," precisely because he's in a recoverable git workspace with no standing creds in the agent's reach. A local crypto-wallet developer would lock it down completely differently. Don't copy a policy; copy the method - map the gate to what your identity can reach.
Letting the agent deploy securely, toosecure-by-default IaC, reviewed before the PR opens
The same operator can use an agent to evaluate and stand up a third-party AI tool safely. Asked whether a coworker could run a locally-exposed tool, Travis aimed for safe-but-usable: don't put it on the internet. The tool's own docs recommended a Tailscale connector, so the service stays locked down and still reachable from a phone.
In Plan modeA mode that makes no edits: it researches the codebase and produces an editable plan you review before any code changes. he has the agent write Terraform into an infra/ directory with "good defaults based on the docs" - explicitly not open to the internet, reasonable plugins, VPC flow logs explained inline. Then a security sub-agentA child agent a main agent spawns to work in parallel with its own context window, handing results back so the parent's context stays clean. reviews the plan for risks before any PR opens.
"They're recommending Tailscale, which makes a lot of sense to me. So this is not on the internet. It's just locked down. You have a Tailscale connector and then you can put that on your phone."
Takeaway. Run agent code as untrusted in a per-task microVM or gVisor-hardened container, lock the container down (drop caps, non-root, read-only FS, default-deny egress), then design as if it will be escaped so the blast radiusHow much breaks if a change goes wrong; the scope of potential damage. is a disposable box holding no secrets and routing to nothing valuable - and when you drive an agent against your own cloud, apply the same instinct: never expose credentials to it (set them in a shell env the agent can't read), gate every mutating or network action by your own blast radius and have a security sub-agentA child agent a main agent spawns to work in parallel with its own context window, handing results back so the parent's context stays clean. review IaCInfrastructure as Code. Managing servers and cloud resources through version-controlled config files (e.g. Terraform). before the PR.
Self-check
QWhy might a microVM like Firecracker be a better fit than a plain container for executing untrusted agent code and what is the main thing you give up?
Secrets & key management
After this you can design secret and key management that scales securely.
Secrets management is where good intentions go to die in env files. The senior version of this topic is short-lived and centralized: secrets that rotate themselves, keys whose location you can name and an audit trail on every read.
Start from the failure you are preventing. A leaked secret is only dangerous in proportion to its lifetime and reach. Cut both and most leaks become non-events.
The shape of a sane secrets systemcentralize, shorten, rotate, audit
- Centralize in a real secret store (a vault or cloud secrets manager), never in code, images or shared docs.
- Prefer dynamic, short-lived secrets the store mints on demand over static credentials that live forever.
- Rotate on a schedule and on demand and make rotation a non-event the app handles automatically.
- Audit every secret read as a detective control, so an anomalous access pattern is visible.
Two patterns sink candidates instantly. A .env file with real production secrets committed to the repo - once it's in git history, it's leaked forever even after you delete it. And a long-lived cloud access key copied between services and never rotated - one leak and the attacker has durable, broad reach. If you describe either as acceptable "for now," you've signaled the wrong instincts.
Encryption: in transit, at rest and where the keys livename the key location or the design is incomplete
Encryption is easy to assert and easy to get wrong. The interviewer is listening for whether you know where the keys actually live, because data encrypted with a key stored next to it isn't meaningfully protected.
- Layer
- In transit
- What protects it
- TLS 1.3 client→edge, mTLS service→service
- The real question
- Where does TLS terminate and is the next hop re-encrypted or plaintext?
- Layer
- At rest
- What protects it
- Disk/object/db encryption via a KMS
- The real question
- Who can call the KMS to decrypt and is that access audited?
- Layer
- Key custody
- What protects it
- A KMS / HSM holds the key material
- The real question
- Is the key separated from the data, rotated and access-logged?
| Layer | What protects it | The real question |
|---|---|---|
| In transit | TLS 1.3 client→edge, mTLS service→service | Where does TLS terminate and is the next hop re-encrypted or plaintext? |
| At rest | Disk/object/db encryption via a KMS | Who can call the KMS to decrypt and is that access audited? |
| Key custody | A KMS / HSM holds the key material | Is the key separated from the data, rotated and access-logged? |
"It's encrypted" is not an answer. "It's encrypted with a KMS-held key that only these two roles can decrypt and every decrypt is logged" is.
CMEK: customer-managed encryption keysCursor offers this to enterprise
Cursor offers customer-managed encryption keys to enterprise customers, so be ready to explain the model. With CMEK, the customer controls the key that encrypts their data, often in their own cloud KMS and grants the provider scoped permission to use it.
- Control
- The customer holds the master key; you use it under a grant you don't own.
- Revocation
- If trust breaks, the customer revokes key access and their data-at-rest becomes unreadable - a hard kill switch.
- Auditability
- Decrypt calls show up in the customer's own KMS logs, not just yours.
- The catch
- Key availability becomes a shared dependency: if the customer's KMS is down or the grant is revoked, your service can't decrypt either. Design for that operationally.
Before you call any encryption design done, answer three questions out loud: where is the key, who can use it to decrypt and is every decrypt logged. If the key sits in the same trust boundary as the data with no access controls, you've encoded the data, not protected it.
Takeaway. Centralize secrets, prefer short-lived dynamic credentials over static keys, rotate and audit every read; for encryption, always be able to say where the key lives, who can decrypt and whether decrypts are logged - and with CMEK the customer holds that key as a real revocation lever.
Self-check
QA teammate says the customer-data store is secure because everything is encrypted at rest with an AES key kept in the application's config so the app can decrypt fast. What's wrong and how would you fix it?
Logging, detection & data retention
After this you can design audit/detection and safe data-retention controls.
The JD lists "logging safeguards against inappropriate data retention" as a responsibility. That phrasing is the tell: at Cursor, retention is a security control, not just an ops setting. Logging both protects you (you can detect and investigate) and endangers you (you can over-retain sensitive code) and a good engineer holds both halves at once.
Log the security events, not the sensitive contentthe line that keeps your audit log from becoming a breach
Logs are where teams accidentally re-collect the data they promised not to keep. The discipline is to log the fact of an action richly while keeping the content out. You want to know that an agent wrote to a file; you do not want the file's contents living in a log nobody is protecting like primary data.
- Log this (security signal)
- Who authenticated, from where and when
- Not this (sensitive content)
- The password or token they used
- Log this (security signal)
- Privilege grants: who requested, who approved, scope, TTL
- Not this (sensitive content)
- The full contents of the resources they accessed
- Log this (security signal)
- Agent actions: command run, file path written, tool invoked
- Not this (sensitive content)
- The customer's source code or prompt body verbatim
- Log this (security signal)
- A decrypt call against a KMS key
- Not this (sensitive content)
- The plaintext that was decrypted
| Log this (security signal) | Not this (sensitive content) |
|---|---|
| Who authenticated, from where and when | The password or token they used |
| Privilege grants: who requested, who approved, scope, TTL | The full contents of the resources they accessed |
| Agent actions: command run, file path written, tool invoked | The customer's source code or prompt body verbatim |
| A decrypt call against a KMS key | The plaintext that was decrypted |
Identifiers, scopes and outcomes are the audit gold. Raw content is liability you have to defend and eventually delete.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Log the fact of an action richly; keep the content out of the log.
From audit log to detectionlogs nobody reads are just storage cost
An audit log earns its keep when it drives an alert. Detection engineering, in its basic form, is turning the events you log into signals on access that doesn't fit the normal pattern. You don't need a SIEM cathedral to start; you need a few high-signal rules.
- Break-glass used - page immediately, every time, no exceptions.
- A JIT grant for an unusually broad scope or a grant that touches a resource the requester has never touched before.
- A burst of decrypt calls or access from a new geography or a new ASN for a given identity.
- An agent sandbox attempting egress to a blocked destination like the metadata endpoint - that's an escape attempt, treat it as one.
Privacy Mode and tenant isolationthe retention promise has to be enforced, not just stated
Cursor's Privacy ModeCursor's setting that routes requests under zero-data-retention terms so providers don't store or train on your code. commits to not retaining or training on customer code when the customer hasn't permitted it. That's a contractual and reputational promise and the engineering job is to make it structurally true rather than a policy people remember to follow.
Default to not persisting code/prompt content; persist only with an explicit, recorded permission.
Set hard TTLs on anything transient and verify deletion actually happens, not just that a flag flipped.
Keep training pipelines physically unable to read Privacy-Mode data, so a config slip can't leak it in.
Scope every query and storage path by tenant so one customer's data can't surface in another's context.
Carry the tenant boundary into logs, caches and any retrieval/index - these are the sneaky leak paths.
Test cross-tenant access as an abuse case, not just a unit test of the happy path.
The principle from section one applies to data, not just access: the safest data is the data you never kept. Standing access and standing data are the same liability in two shapes. Minimize both, time-box both and audit access to both.
"I treat logging as two jobs that fight each other. For detection I log identities, scopes and actions richly and alert on break-glass and anomalous access. For privacy I keep raw code and prompts out of those logs, enforce Privacy ModeCursor's setting that routes requests under zero-data-retention terms so providers don't store or train on your code. by defaulting to no retention with hard TTLs and carry the tenant boundary into every store, cache and index so one customer's data can never appear in another's context."
Takeaway. Log identities, scopes and actions richly while keeping raw code and secrets out, turn those events into a few high-signal alerts (break-glass, anomalous scope, blocked sandbox egress) and make retention a real control - default to no persistence, hard TTLs with verified deletion and tenant isolation carried into logs, caches and indexes.