#Network Policy
Control network access for sandboxes. Network policies allow you to restrict outbound traffic, block specific domains, or allow only certain endpoints.
Network Modes#
| Mode | Description | Use Case |
|---|---|---|
allow-all | Allow all outbound traffic | Default, unrestricted access |
block-all | Block all outbound traffic | Maximum security, air-gapped environments |
Egress Policy#
Egress fields are interpreted by the selected mode:
| Field | Type | Description |
|---|---|---|
allowed_domains | array | Whitelist of allowed domains. Used only in block-all mode. |
denied_domains | array | Blacklist of blocked domains. Used only in allow-all mode. |
allowed_cidrs | array | Whitelist of allowed IP CIDRs. Used only in block-all mode. |
denied_cidrs | array | Blacklist of blocked IP CIDRs. Used only in allow-all mode. |
allowed_ports | array | Whitelist of allowed destination ports. Used only in block-all mode. |
denied_ports | array | Blacklist of blocked destination ports. Used only in allow-all mode. |
In allow-all mode, traffic is permitted by default and only denied* fields are enforced. In block-all mode, traffic is denied by default and only allowed* fields are enforced.
Egress Credential Injection#
Credential injection now has two layers:
credentials.bindingsdefines sandbox-scoped bindings that map a stablerefto a manager-managed credential source.egress.rulesmatches destinations and points to one binding withcredentialRef.
This lets netd call cluster-local egress-broker and inject outbound credentials for selected destinations without handing the original source material to the sandbox.
Credential Sources#
Credential sources are managed through the manager API:
GET /api/v1/credential-sourcesPOST /api/v1/credential-sourcesGET /api/v1/credential-sources/{name}PUT /api/v1/credential-sources/{name}DELETE /api/v1/credential-sources/{name}
The current public source kind is static_headers. It stores named values that bindings can project into outbound headers.
| Field | Type | Description |
|---|---|---|
name | string | Stable source name used by sourceRef. |
resolverKind | string | Resolver kind. Current public value: static_headers. |
spec.staticHeaders.values | object | Named source values consumed by binding templates. |
Sandbox Bindings And Egress Rules#
| Field | Type | Description |
|---|---|---|
credentials.bindings | array | Sandbox-scoped credential bindings. |
credentials.bindings[].ref | string | Stable binding reference matched by egress.rules[].credentialRef. |
credentials.bindings[].sourceRef | string | Credential source name resolved by manager. |
credentials.bindings[].projection.type | string | Projection type. Current public value: http_headers. |
credentials.bindings[].projection.httpHeaders.headers[].name | string | Header name to inject. |
credentials.bindings[].projection.httpHeaders.headers[].valueTemplate | string | Go template rendered against source values, for example Bearer {{.token}}. |
credentials.bindings[].cachePolicy.ttl | string | Optional broker-side cache TTL override, such as 5m. |
egress.rules | array | Destination-scoped egress credential rules. |
egress.rules[].credentialRef | string | Required binding reference resolved against credentials.bindings[].ref. |
egress.rules[].rollout | string | Optional rollout control: enabled or disabled. Empty defaults to enabled. |
egress.rules[].protocol | string | Optional protocol hint: http, https, or grpc. |
egress.rules[].tlsMode | string | TLS handling mode for HTTPS/gRPC: passthrough or terminate-reoriginate. |
egress.rules[].failurePolicy | string | Optional enforcement policy: fail-closed or fail-open. |
egress.rules[].domains | array | Domain match list for the rule. |
egress.rules[].ports | array | Destination port constraints for the rule. |
Supported cases:
- plain HTTP header injection
- HTTPS when
netdterminates downstream TLS and re-establishes upstream TLS - gRPC over TLS when
netdproxies HTTP/2 correctly and injects metadata
Not supported or not transparent:
- certificate pinning
- runtimes that do not trust the injected cluster CA
- legacy applications that must read a static key from disk or env
Typical flow:
- Create or update a credential source.
- Bind that source into the sandbox with
credentials.bindings. - Match outbound destinations with
egress.rules.
Example credential source payload:
yamlname: example-api resolverKind: static_headers spec: staticHeaders: values: token: secret-token
Example network policy payload:
yamlmode: block-all credentials: bindings: - ref: example-api sourceRef: example-api projection: type: http_headers httpHeaders: headers: - name: Authorization valueTemplate: Bearer {{.token}} cachePolicy: ttl: 5m egress: allowedDomains: - api.example.com rules: - name: example-api credentialRef: example-api protocol: https tlsMode: terminate-reoriginate failurePolicy: fail-closed domains: - api.example.com ports: - port: 443 protocol: tcp
HTTPS and gRPC interception require cluster-level support in self-hosted deployments: egress-broker must be enabled, netd egress auth must be turned on, and netd needs a cluster-local MITM CA. infra-operator manages that CA by default, or you can override it with services.netd.mitmCaSecretName.
Get Network Policy#
Retrieve the current network policy for a sandbox.
/api/v1/sandboxes/{id}/network
go// Get current network policy policy, err := sandbox.GetNetworkPolicy(ctx) if err != nil { log.Fatal(err) } fmt.Printf("Mode: %s\n", policy.Mode) if egress, ok := policy.Egress.Get(); ok { fmt.Printf("Allowed domains: %v\n", egress.AllowedDomains) }
Update Network Policy#
Update the network policy for a sandbox.
/api/v1/sandboxes/{id}/network
Request Body#
| Field | Type | Description |
|---|---|---|
mode | string | Network mode: allow-all or block-all |
credentials | object | Sandbox credential bindings resolved by egress-broker (optional) |
egress | object | Egress policy rules (optional) |
Allow All Traffic#
Allow all outbound network access (default behavior).
go// Allow all traffic _, err = sandbox.UpdateNetworkPolicy(ctx, apispec.TplSandboxNetworkPolicy{ Mode: apispec.TplSandboxNetworkPolicyModeAllowAll, }) if err != nil { log.Fatal(err) } fmt.Println("Network policy updated: allow-all")
Block All Traffic#
Block all outbound network access.
go// Block all traffic _, err = sandbox.UpdateNetworkPolicy(ctx, apispec.TplSandboxNetworkPolicy{ Mode: apispec.TplSandboxNetworkPolicyModeBlockAll, }) if err != nil { log.Fatal(err) } fmt.Println("Network policy updated: block-all")
Allow Specific Domains#
Block all traffic except for specific allowed domains.
go// Block all except specific domains _, err = sandbox.UpdateNetworkPolicy(ctx, apispec.TplSandboxNetworkPolicy{ Mode: apispec.TplSandboxNetworkPolicyModeBlockAll, Egress: apispec.NewOptNetworkEgressPolicy(apispec.NetworkEgressPolicy{ AllowedDomains: []string{"github.com", "pypi.org", "api.openai.com"}, }), }) if err != nil { log.Fatal(err) } fmt.Println("Network policy updated: only github.com, pypi.org, api.openai.com allowed")
Block Specific Domains#
Allow all traffic except for specific blocked domains.
go// Block specific domains (allow all others) _, err = sandbox.UpdateNetworkPolicy(ctx, apispec.TplSandboxNetworkPolicy{ Mode: apispec.TplSandboxNetworkPolicyModeAllowAll, Egress: apispec.NewOptNetworkEgressPolicy(apispec.NetworkEgressPolicy{ DeniedDomains: []string{"facebook.com", "twitter.com"}, }), }) if err != nil { log.Fatal(err) } fmt.Println("Network policy updated: block facebook.com, twitter.com")
Set Network Policy at Creation#
Configure network policy when claiming a sandbox.
go// Claim sandbox with network policy sandbox, err = client.ClaimSandbox(ctx, "default", sandbox0.WithSandboxHardTTL(600), sandbox0.WithSandboxNetworkPolicy(apispec.TplSandboxNetworkPolicy{ Mode: apispec.TplSandboxNetworkPolicyModeAllowAll, }), ) if err != nil { log.Fatal(err) }
Test Network Connectivity#
Verify network policy by making requests from the sandbox.
go// Test network connectivity const shell = `/bin/curl -s -o /dev/null -w "%{http_code}\n" --max-time 3 https://github.com` resp, err := sandbox.Cmd(ctx, shell) if err != nil { log.Fatal(err) } fmt.Printf("GitHub response before blocking: %s\n", resp.OutputRaw) // Block all traffic _, err = sandbox.UpdateNetworkPolicy(ctx, apispec.TplSandboxNetworkPolicy{ Mode: apispec.TplSandboxNetworkPolicyModeBlockAll, }) if err != nil { log.Fatal(err) } // Test again (should fail) resp, err = sandbox.Cmd(ctx, shell) if err != nil { fmt.Println("Request blocked as expected") } fmt.Printf("GitHub response after blocking: %s\n", resp.OutputRaw)
Next Steps#
Port Exposure
Expose sandbox ports publicly
Webhooks
Receive event notifications
Volumes
Persistent storage for sandboxes