Control egress
ACA sandboxes are designed to run untrusted code. Egress policies restrict outbound traffic and let the egress proxy inject secret-backed headers without exposing the resolved secret to the sandbox.
Sandboxes have unrestricted outbound access when no egress policy is configured. Egress policies are opt-in.
How to deny all outbound traffic
You can define a deny policy that blocks outbound calls unless a rule allows them. Begin with deny, then add the smallest host list your workload needs.
- Open the sandbox from Sandbox groups > your group > Sandboxes.
- From the sandbox details, select Egress policy.
- Set Default action to Deny.
- Set Traffic inspection to Full.
- Add
*.github.comwith action Allow. - Select Save. Wait a few seconds before you test the policy.
- Bash
- PowerShell
- SDK
aca sandbox create --disk ubuntu --label name=demo-egress
aca sandbox egress set -l name=demo-egress --default Deny \
--rule "*.github.com:Allow" \
--traffic-inspection Full
aca sandbox egress show -l name=demo-egress
aca sandbox create --disk ubuntu --label name=demo-egress
aca sandbox egress set -l name=demo-egress --default Deny `
--rule "*.github.com:Allow" `
--traffic-inspection Full
aca sandbox egress show -l name=demo-egress
import os
from azure.identity import DefaultAzureCredential
from azure.containerapps.sandbox import (
EgressHostRule,
EgressPolicy,
SandboxGroupClient,
endpoint_for_region,
)
credential = DefaultAzureCredential()
client = SandboxGroupClient(
endpoint_for_region(os.environ["ACA_SANDBOXGROUP_REGION"]),
credential,
subscription_id=os.environ["AZURE_SUBSCRIPTION_ID"],
resource_group=os.environ["ACA_RESOURCE_GROUP"],
sandbox_group=os.environ["ACA_SANDBOX_GROUP"],
)
sandbox = client.get_sandbox_client(os.environ["SANDBOX_ID"])
sandbox.set_egress_policy(EgressPolicy(
default_action="Deny",
traffic_inspection="Full",
host_rules=[EgressHostRule(pattern="*.github.com", action="Allow")],
))
policy = sandbox.get_egress_policy()
print(policy.default_action, len(policy.host_rules))
For simple host rules, use --rule "<pattern>:<Action>". The action is Allow or Deny.
- Bash
- PowerShell
aca sandbox egress set -l name=demo-egress --default Deny \
--rule "*.github.com:Allow" \
--rule "example.com:Deny" \
--traffic-inspection Full
aca sandbox egress set -l name=demo-egress --default Deny `
--rule "*.github.com:Allow" `
--rule "example.com:Deny" `
--traffic-inspection Full
You can also set the policy when you create a sandbox:
- Bash
- PowerShell
aca sandbox create --disk ubuntu \
--label name=demo-egress \
--egress-default Deny \
--egress-rule "*.github.com:Allow" \
--traffic-inspection Full
aca sandbox create --disk ubuntu `
--label name=demo-egress `
--egress-default Deny `
--egress-rule "*.github.com:Allow" `
--traffic-inspection Full
How to allow all outbound traffic
You can define an allow policy for sandboxes that need normal outbound access, with deny rules for specific destinations.
- Bash
- PowerShell
aca sandbox create --disk ubuntu \
--label name=demo-egress-allow \
--egress-default Allow \
--egress-rule "example.com:Deny" \
--traffic-inspection Full
aca sandbox create --disk ubuntu `
--label name=demo-egress-allow `
--egress-default Allow `
--egress-rule "example.com:Deny" `
--traffic-inspection Full
For an existing sandbox:
- Bash
- PowerShell
aca sandbox egress set -l name=demo-egress --default Allow \
--rule "example.com:Deny" \
--traffic-inspection Full
aca sandbox egress set -l name=demo-egress --default Allow `
--rule "example.com:Deny" `
--traffic-inspection Full
Inject a secret into an outbound request
Reference a sandbox-group secret from an advanced Transform header rule. The secret stays outside the sandbox at runtime: the egress proxy injects it into matching outbound requests after they leave the sandbox, so code running in the sandbox never has the value.
defaultAction: Deny
trafficInspection: Full
rules:
- name: inject-model-credential
match:
host: "api.example.com"
path: "/v1/*"
methods: [POST]
scheme: https
action:
type: Transform
headers:
- operation: Set
name: Authorization
valueRef:
secretRef:
secretId: model-credentials
secretKey: API_KEY
format: "Bearer {value}"
A transform can also inject a managed identity access token via a managedIdentityRef.
Control traffic inspection
Traffic inspection controls how much request information the egress policy can evaluate. Choose the lowest inspection level that supports your policy rules, and verify exact transport behavior in the current product or CLI help.
| Mode | Use when |
|---|---|
Full | You need the policy to evaluate host rules, path or method matches, rewrites, or header transforms. |
Partial | You need limited inspection for selected policy behaviors. |
None | You do not need egress policy inspection for this sandbox. |
- Bash
- PowerShell
aca sandbox egress set -l name=demo-egress \
--default Deny \
--rule "*.github.com:Allow" \
--traffic-inspection Full
aca sandbox egress set -l name=demo-egress `
--default Deny `
--rule "*.github.com:Allow" `
--traffic-inspection Full
Create-time policy flags support the same inspection setting:
- Bash
- PowerShell
aca sandbox create --disk ubuntu \
--label name=demo-egress \
--egress-default Deny \
--egress-rule "*.github.com:Allow" \
--traffic-inspection Full
aca sandbox create --disk ubuntu `
--label name=demo-egress `
--egress-default Deny `
--egress-rule "*.github.com:Allow" `
--traffic-inspection Full
Use a policy file for advanced rules
Use policy files for path or method matches, rewrites, transforms, and header operations.
Supported rule fields:
| Field | Supported values |
|---|---|
match.host | Required host name or wildcard pattern. |
match.path | Optional path match, such as /headers. |
match.methods | Optional HTTP methods, such as [GET, POST]. |
match.scheme | http, https, or any. Controls which request schemes match the rule. |
action.type | Allow, Deny, Rewrite, or Transform. |
action.host | Rewrite target host. Only valid when action.type is Rewrite. |
action.path | Rewrite target path. Only valid when action.type is Rewrite. |
action.scheme | Rewrite destination scheme, such as https. Only valid when action.type is Rewrite. |
action.headers | Header mutations. Valid when action.type is Transform or Rewrite. |
headers[].operation | Set, Insert, or Remove. Only valid inside action.headers. |
- Bash
- PowerShell
aca sandbox egress init > egress.yaml
aca sandbox egress schema > egress.schema.json
aca sandbox egress apply -l name=demo-egress --file egress.yaml
aca sandbox egress export -l name=demo-egress > current-egress.yaml
aca sandbox egress init > egress.yaml
aca sandbox egress schema > egress.schema.json
aca sandbox egress apply -l name=demo-egress --file egress.yaml
aca sandbox egress export -l name=demo-egress > current-egress.yaml
Example advanced policy:
defaultAction: Deny
trafficInspection: Full
rules:
- name: inject-test-header
match:
host: "httpbin.org"
path: "/headers"
methods: [GET]
action:
type: Transform
headers:
- operation: Set
name: X-Test-Header
value: egress-test
Rules are evaluated in order. The first matching rule wins.
Match rules by scheme
match.scheme controls whether a rule matches http, https, or any.
When match.scheme is omitted, the default depends on the rule:
| Rule | Default scheme |
|---|---|
Allow, Transform, or Rewrite | https |
Deny | any |
With HTTPS, the egress proxy can authenticate the requested host through the remote server's TLS certificate. Plain HTTP has no certificate that binds the sandbox-controlled Host header to the destination IP. Untrusted code could connect to a different IP while presenting an allowlisted host, so positive rules exclude HTTP by default while deny rules cover both schemes.
Setting match.scheme to http or any on a positive rule opts into this risk. Use it only when another mechanism establishes trust in the destination.
defaultAction: Deny
trafficInspection: Full
rules:
- name: allow-api-https
match:
host: "api.example.com"
scheme: https
action:
type: Allow
- name: deny-example-all-schemes
match:
host: "example.com"
scheme: any
action:
type: Deny
Delegate decisions to a webhook
Replace a rule's static action with a hookRef to have an external endpoint decide, transform, or rewrite the request at runtime — for tenant-specific authorization, runtime token minting, or dynamic upstream routing. See Make dynamic egress decisions with a webhook.
Verify allowed and blocked hosts
After the policy propagates, test from inside the sandbox. An allowed host should return a normal HTTP status. A blocked host should fail or return a non-200 status.
- Open the sandbox, then select Connect > Interactive shell.
- Run
curl https://example.comto test a blocked host. - Run
curl https://api.github.comto test an allowed host. - Select Network audit to review the recorded decisions.
- Bash
- PowerShell
- SDK
aca sandbox exec -l name=demo-egress -c \
"curl -fsS -o /dev/null -w 'HTTP %{http_code}\n' --max-time 8 https://example.com" || echo "blocked"
aca sandbox exec -l name=demo-egress -c \
"curl -sS -o /dev/null -w 'HTTP %{http_code}\n' --max-time 8 https://api.github.com"
aca sandbox exec -l name=demo-egress -c `
"curl -fsS -o /dev/null -w 'HTTP %{http_code}\n' --max-time 8 https://example.com"
if ($LASTEXITCODE -ne 0) { Write-Host "blocked" }
aca sandbox exec -l name=demo-egress -c `
"curl -sS -o /dev/null -w 'HTTP %{http_code}\n' --max-time 8 https://api.github.com"
import time
time.sleep(10)
result = sandbox.exec(
"curl -fsS -o /dev/null -w '%{http_code}' --max-time 8 "
"https://example.com || echo CURL_FAIL"
)
print(result.stdout.strip())
result = sandbox.exec(
"curl -sS -o /dev/null -w '%{http_code}' --max-time 8 "
"https://api.github.com"
)
print(result.stdout.strip())
Audit decisions
The audit view shows outbound connection attempts and each host policy decision.
- Open the sandbox, then select Network audit.
- Use the host filter to find a domain.
- Review the decision, timestamp, and target host for each entry.
- Update Egress policy if the sandbox needs another allowed host.
- Bash
- PowerShell
- SDK
aca sandbox egress decisions -l name=demo-egress
aca sandbox egress decisions -l name=demo-egress -o json
aca sandbox egress decisions -l name=demo-egress
aca sandbox egress decisions -l name=demo-egress -o json
decisions = sandbox.get_egress_decisions()
if decisions.network_egress:
for entry in decisions.network_egress.denied:
print("Denied:", entry.host, entry.method, entry.path)
for entry in decisions.network_egress.allowed:
print("Allowed:", entry.host, entry.method, entry.path)
Use -o json in scripts. The JSON output separates allowed and denied decisions.