Secure Private Access for AI Agents
Give AI agents and automated workflows their own identity and least-privilege access to private apps and APIs, without shared secrets or network-level access
- Overview
- How agent access works
- Plan your setup
- Publish a narrow interface
- Set up agent access
- Authenticate with a workload identity
- Authenticate with an API key
- What’s next
Overview
AI agents increasingly need to reach private resources: an internal inventory API, a ticketing system, a reporting database, an internal wiki. An agent is not a person. It works without a sign-in prompt, it acts at machine speed, and it can be steered by the content it reads. A shared human account, a long-lived API key in a configuration file, or broad network access makes each of those traits more dangerous.
Cloud Secure Edge (CSE) treats an agent as a non-human identity. Each agent gets a Service Account that CSE Roles and Web Policies can authorize, scope to specific apps and paths, audit, and revoke, independently of any person’s account. Agents that run on a platform with its own identity, such as GitHub Actions, Google Cloud, or Kubernetes, authenticate with short-lived tokens the platform issues, so no secret is stored with the agent at all.
This guide covers agents that act on their own. If the agent runs on an employee’s device and acts for that person, such as a desktop assistant or a coding agent, it has no identity of its own and nothing here applies to it. See Secure agents that run on a user’s device instead.
How agent access works
An agent with its own identity reaches a private app through a Hosted Website. Every request follows the same path.
- The agent obtains a credential. Either the platform’s OIDC issuer signs a short-lived JWT for a specific audience, or the platform holds an API key.
- The agent calls the Hosted Website. It sends the credential to the website’s public URL, in the location you configure.
- CSE validates the credential at the edge. For a JWT, CSE retrieves the issuer’s signing keys through OIDC Discovery, verifies the signature, and checks that the issuer, audience, and subject match the Service Account.
- Roles and policies decide access. The Service Account belongs to a Role, and a Web Policy grants that Role access to the Hosted Website, optionally limited to specific paths and actions.
- CSE forwards allowed requests to the app. Requests travel through your Connector or Access Tier. Every request, allowed or denied, is recorded in Events.
The calling platform does not need to be added to the Unregistered Devices allowlist. Hosted Websites are served with publicly trusted TLS certificates, so the platform does not need to import a certificate either.
Plan your setup
Setting this up means answering two independent questions. They compose, so answer both before you start. An agent on Kubernetes that queries a database answers the first question with a narrow interface and the second with a workload identity.
Choose what to publish
CSE publishes HTTP. What the agent needs decides whether you can publish it directly.
| What the agent needs | What you publish |
|---|---|
| A web app or an HTTP API | The app itself, as a Hosted Website |
| A database, a server, or another non-HTTP resource | A narrow HTTP interface in front of the resource, as a Hosted Website. See Publish a narrow interface |
Choose an authentication method
Where the agent runs decides what credential it can present.
| Where the agent runs | Credential | Service Account type |
|---|---|---|
| On a platform that issues OIDC tokens, such as GitHub Actions, Google Cloud, or Kubernetes | A short-lived JWT the platform signs | External JSON Web Token (JWT). See Authenticate with a workload identity |
| On a platform that can send an HTTP header but cannot issue OIDC tokens | A static key | SonicWall CSE Generated Key or External API Key. See Authenticate with an API key |
Prefer a JWT wherever the platform supports it. CSE validates the token against your issuer on every request and the token expires on its own, so there is no secret to store, share, or rotate.
Publish a narrow interface
Skip this section if the agent calls a web app or an HTTP API. Publish that app as a Hosted Website and go to Set up access for an agent.
When the task involves a database, a server, or another non-HTTP resource, do not give the agent network access to the resource. Put a narrow interface in front of it and publish the interface:
- An MCP server that exposes only the tools the agent needs, such as
get_stock_levelorlist_open_tickets. - An internal API with one endpoint per approved operation, such as a read-only report query or a specific runbook action.
The interface runs inside your network next to the resource, and it holds its own least-privilege credentials to that resource, such as a read-only database user. The agent never sees those credentials and never has a network path to the database.
| Agent task | Publish this instead of network access |
|---|---|
| Answer questions from a sales database | An MCP server with read-only query tools, backed by a read-only database user |
| Open or update tickets | The ticketing system’s API, limited by Layer-7 rules to the ticket paths the agent needs |
| Restart a stuck service | An internal API with one endpoint per approved runbook action |
Two design points matter here, because they are hard to change later.
Layer-7 rules match URL paths, not tool names. CSE cannot see the individual MCP tools named inside a request body. To give different agents different powers, publish read-only and write tools as separate MCP endpoints, which means separate paths, or as separate Hosted Websites with their own Web Policies.
Watch for an Authorization header collision. If the MCP server or API authenticates with the Authorization header itself, for example with OAuth, send the CSE credential in a custom header instead. That choice is made in Step 3.
Publish the interface as a Hosted Website through your Connector or Access Tier, then continue below.
Set up agent access
These four steps are the same whichever credential the agent presents.
Step 1: Create the Service Account
Go to Directory > Service Accounts and select + Add Service Account, then choose the type you settled on in Choose an authentication method and configure it:
- External JSON Web Token (JWT): Authenticate with a workload identity
- SonicWall CSE Generated Key or External API Key: Authenticate with an API key
Step 2: Add the Service Account to a Role and a Web Policy
2.1 Create a Role for the agent. Under Role Attributes, add the By Service Account attribute, select + Attach Service Account, and choose the Service Account from Step 1.
Give each agent its own Service Account and its own Role. Two agents sharing one identity cannot be told apart in your access records, and cannot be revoked separately.
2.2 Add the Role to a Web Policy, under Private Access > Access Policies.
2.3 Optionally, limit the agent to exactly what it needs with API-level (Layer-7) rules. For example, allow READ on /api/v1/stock* and nothing else. Actions are READ, WRITE, CREATE, UPDATE, or ALL. Resources are URL paths with optional wildcards, and a ! prefix denies a path.
Trust Scoring is not evaluated for Service Accounts. There is no device to evaluate, so a minimum Trust Level in the policy does nothing here. The credential, the Role, and the Layer-7 rules are the whole control. Keep all three tight.
Step 3: Allow Service Account access on the Hosted Website
3.1 Go to Private Access > Hosted Websites and open the website. If the app is not published yet, see Register a Hosted Website to Users.
3.2 On the Access Permissions step, turn on Enable Service Account Access.
3.3 Under How should the Service Account credentials be sent?, choose where the credential travels. The agent has to send it in exactly that location:
| Token location | Choose it when |
|---|---|
| Authorization header | The default choice for most agents |
| Custom header | The app authenticates with the Authorization header itself, for example with OAuth |
| Query parameter | Nothing else is possible. A credential in a query parameter is written into every log that records the URL |
3.4 Confirm that the Web Policy from Step 2 is attached, and save the website.
Step 4: Test and monitor
Send one request by hand before you let the agent run:
curl -i https://inventory.example.com/api/v1/stock -H "Authorization: Bearer $TOKEN"
A successful request returns the app’s response. A missing or invalid credential, or one sent in a different location than you configured, is denied before it reaches the app.
Then check the console. Go to Home > Events to see allowed and denied Service Account requests, or open the Service Account under Directory > Service Accounts and review its Access Activity tab. Denied requests are often the first sign of a misconfigured or misbehaving agent.
Come back to the Access Activity tab periodically once the agent is running. It shows what the agent actually reached. An agent reaching something you did not expect is a scope problem to fix in the policy, not in the agent’s instructions.
Authenticate with a workload identity
The platform running the agent proves who the agent is, and CSE decides what it can reach. There is no key to store, share, or rotate.
Prerequisites
- Your issuer publishes an OIDC discovery document at
<Issuer URL>/.well-known/openid-configuration, and CSE can reach it to retrieve signing keys. Managed Kubernetes services such as Amazon EKS, Google Kubernetes Engine, and Azure Kubernetes Service with the OIDC issuer enabled provide a public issuer URL for each cluster. - You know the audience and subject values your agent’s tokens carry:
| Platform | Issuer URL | Audience (aud) |
Subject (sub) |
|---|---|---|---|
| GitHub Actions | https://token.actions.githubusercontent.com |
The value the workflow requests, for example https://inventory.example.com |
repo:ORG/REPO:ref:refs/heads/BRANCH, or repo:ORG/REPO:environment:NAME for jobs that use an environment |
| Google Cloud | https://accounts.google.com |
The value passed in the metadata server’s audience parameter |
The unique numeric ID of the Google Cloud service account |
| Kubernetes | The cluster’s service account issuer URL | The value set on the projected service account token | system:serviceaccount:NAMESPACE:SERVICE_ACCOUNT |
Configure the Service Account
In Step 1, select External JSON Web Token (JWT). The Type field shows Generic OIDC, because CSE accepts any OIDC-compliant issuer.
1. Enter the Issuer URL, for example https://token.actions.githubusercontent.com.
2. Enter an Audience, which is a URI or URL, and the Subject allowed to use it.
3. Select Add Audience Group to allow more combinations, such as the same workflow calling a second Hosted Website with its own audience.
Use a distinct audience for each Hosted Website, such as the website’s own URL. A token minted for one app then cannot be replayed against another.
Subjects accept regular expressions, which helps when one agent runs from several branches or environments. Anchor the expression to the full value and keep it as narrow as possible, because anything it matches is admitted:
^repo:example-org/inventory-agent:ref:refs/heads/(main|release)$
Then finish Steps 2 through 4 of Set up access for an agent.
Request a token in the agent
The agent requests a token for the audience you configured, then sends it as a Bearer token. Open the platform you use.
GitHub Actions
jobs:
inventory-report:
runs-on: ubuntu-latest
permissions:
id-token: write # lets the job request an OIDC token
contents: read
steps:
- name: Call the private inventory API through CSE
run: |
TOKEN=$(curl -sS -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://inventory.example.com" | jq -r .value)
curl -sS -H "Authorization: Bearer $TOKEN" https://inventory.example.com/api/v1/stock
For a workflow on the main branch of example-org/inventory-agent, the token’s subject is repo:example-org/inventory-agent:ref:refs/heads/main.
Google Cloud, on Compute Engine, Cloud Run, and other services with a metadata server
TOKEN=$(curl -sS -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://inventory.example.com")
curl -sS -H "Authorization: Bearer $TOKEN" https://inventory.example.com/api/v1/stock
Kubernetes
Project a token with the right audience into the agent’s pod. The kubelet refreshes the token before it expires.
spec:
serviceAccountName: inventory-agent
containers:
- name: agent
image: registry.example.com/inventory-agent:1.0
volumeMounts:
- name: cse-token
mountPath: /var/run/secrets/cse
readOnly: true
volumes:
- name: cse-token
projected:
sources:
- serviceAccountToken:
path: token
audience: https://inventory.example.com
expirationSeconds: 3600
The agent reads /var/run/secrets/cse/token and sends it as a Bearer token. For a pod in the agents namespace, the subject is system:serviceaccount:agents:inventory-agent.
Authenticate with an API key
Use a key when the platform cannot issue an OIDC token you control but can send an HTTP header with every call.
In Step 1, select SonicWall CSE Generated Key if you can configure an arbitrary secret in the platform, or External API Key if the platform issues its own. For a CSE Generated Key, copy the key after saving and store it in the platform’s secret store.
Then finish Steps 2 through 4 of Set up access for an agent, and configure the platform to send the key on every call. In Salesforce, for example, store it as a Named Credential custom header.
Keep keys out of the model’s context. Do not place keys in prompts, tool descriptions, or files the agent can read, where they can be repeated in output or exfiltrated.
To rotate a key without downtime, create a replacement Service Account, add it to the same Role, update the platform, confirm the new key works in Events, and then delete the old Service Account.
If the platform later supports OIDC tokens for outbound calls, move the agent to a workload identity and remove the stored key.
To revoke an agent entirely, remove its Service Account from its Role, or delete the Service Account. Nothing needs to change on the agent side.
What’s next
- Secure agents that run on a user’s device, if an assistant or coding agent acts for a person rather than on its own
- Programmatic Access for Hosted Websites for Service Account types, fields, and limitations
- API-level (Layer-7) Policies to narrow an agent to specific paths and actions
- Events for the record of every allowed and denied request