How to Configure CubeEgress Domain Allowlists and Egress Security Policies in CubeSandbox

CubeSandbox implements a two-layer egress control system where CubeVS handles L3/L4 CIDR filtering and CubeEgress enforces L7 domain-based rules, requiring explicit deny_out rules when using domain allowlists to pass validation.

TencentCloud/CubeSandbox protects outbound traffic through a sophisticated architecture that separates IP-level filtering from application-layer policy enforcement. Configuring CubeEgress domain allowlists and egress security policies allows you to restrict sandbox environments to specific external APIs while safely injecting credentials and maintaining comprehensive audit trails.

Understanding the Two-Layer Egress Architecture

CubeSandbox employs a dual-layer approach to secure outbound connections:

  • Layer 3/4 (CubeVS): An eBPF-based data plane that manages raw IP routing, NAT, and CIDR-based egress filtering through allow_out and deny_out lists.
  • Layer 7 (CubeEgress): An OpenResty/NGINX proxy that intercepts HTTP/HTTPS traffic redirected from CubeVS, applies per-domain or per-path rules, injects credentials, and writes audit logs.

Configuring Domain Allowlists (allow_out)

The allow_out configuration accepts both IPv4 CIDR strings and DNS names (e.g., api.openai.com or *.example.com). When a rule's match contains an SNI, Host, or Scheme referencing a domain, CubeSandbox automatically adds that domain to allow_out, ensuring the underlying eBPF layer permits the traffic.

Validation Requirements

According to the validation logic in sdk/go/policy.go (lines 60-71), domain-based allowlists only validate when paired with a deny-all configuration. The validateAllowOutDomainsRequireDenyAll function enforces that either all other egress is denied (default "deny-all" mode) or 0.0.0.0/0 is explicitly placed in deny_out. If violated, the API returns a 400 error with the message defined in allowOutDomainRequiresDenyAll.

Defining Egress Security Policies (L7 Rules)

L7 rules consist of match conditions and actions defined in sdk/go/policy.go. The Match struct supports optional SNI, Host, Method, Path, and Scheme fields—all fields are AND-ed, while Method accepts multiple values (OR-ed). The Action struct specifies Allow (with optional Inject objects) or explicit denial (Allow: false), plus an optional Audit string for logging labels.

// Match holds rule match conditions.
type Match struct {
    SNI    string   `json:"sni,omitempty"`
    Host   string   `json:"host,omitempty"`
    Method []string `json:"method,omitempty"`
    Path   string   `json:"path,omitempty"`
    Scheme string   `json:"scheme,omitempty"`
}

// Inject injects a credential header on an allowed HTTPS request.
type Inject struct {
    Header string `json:"header"`
    Secret string `json:"secret"`
    Format string `json:"format,omitempty"`
}

// Action is a rule action.
type Action struct {
    Allow  bool     `json:"allow"`
    Inject []Inject `json:"inject,omitempty"`
    Audit  string   `json:"audit,omitempty"`
}

// Rule is one L7 egress rule.
type Rule struct {
    Name   string `json:"name"`
    Match  Match  `json:"match"`
    Action Action `json:"action"`
}

When Action.Allow is true, CubeEgress forwards the request and injects any headers defined in Inject. If false, it returns HTTP 403 before the request leaves the cluster.

Policy Propagation Flow

Configuration flows through the system as follows:

  1. Sandbox creation: The user passes a network block (allow_out, deny_out, rules) via the API or SDK.
  2. CubeMaster: Stores the configuration and pushes it to the network-agent.
  3. network-agent: Converts the high-level policy into a cubeegress.PolicyInput payload and calls PutPolicy. See the implementation in network-agent/internal/service/cubeegress_push.go (lines 13-73).
  4. CubeVS: Installs CIDR allow/deny lists. For every DNS query matching a domain in allow_out, CubeVS learns resolved IPs and adds them to the CIDR list automatically.
  5. CubeEgress: Receives the policy via its admin API, compiles the Lua rule set, and filters HTTP/HTTPS traffic redirected from CubeVS via the TPROXY iptables chain.

Implementation Examples

Go SDK Configuration

import (
    "github.com/TencentCloud/CubeSandbox/sdk/go"
)

func createSandbox() (*cubesandbox.Sandbox, error) {
    cfg := &cubesandbox.CubeNetworkConfig{
        // Allow only the OpenAI API domain; everything else is denied.
        AllowOut: []string{"api.openai.com"},
        DenyOut:  []string{"0.0.0.0/0"}, // required when domains are used
        Rules: []cubesandbox.Rule{
            {
                Name: "OpenAI credential injection",
                Match: cubesandbox.Match{
                    SNI: "api.openai.com",
                },
                Action: cubesandbox.Action{
                    Allow: true,
                    Inject: []cubesandbox.Inject{
                        {
                            Header: "Authorization",
                            Secret: "sk-xxxxxxxxxxxxxxxxxxxx",
                            // ${SECRET} will be substituted with the secret value.
                        },
                    },
                    Audit: "openai-call",
                },
            },
        },
    }

    // `Create` also accepts `AllowInternetAccess: false` to start from a deny‑all state.
    return client.Sandbox.Create(
        cubesandbox.SandboxCreateOpts{
            Network: cfg,
            AllowInternetAccess: false,
        })
}

The validation in policy.go guarantees this call succeeds; if DenyOut is omitted, the API returns a 400 error.

Python SDK Configuration

from cubesandbox import CubeSandbox

cs = CubeSandbox()

sandbox = cs.sandbox.create(
    allow_internet_access=False,
    network={
        "allow_out": ["api.openai.com"],
        "deny_out":  ["0.0.0.0/0"],
        "rules": [
            {
                "name": "OpenAI credential injection",
                "match": {"sni": "api.openai.com"},
                "action": {
                    "allow": True,
                    "inject": [{"header": "Authorization",
                                "secret": "sk-xxxxxxxxxxxxxxxxxxxx"}],
                    "audit": "openai-call"
                }
            }
        ],
    },
)

Retrieving Active Policies

// Assuming `client` is a configured network‑agent client.
pol, err := client.Egress.GetPolicy(context.Background(), sandboxIP)
if err != nil {
    log.Fatalf("failed to fetch policy: %v", err)
}
fmt.Printf("Current CubeEgress policy: %+v\n", pol)

The returned JSON mirrors the policy.go structure and represents the exact payload CubeEgress uses internally.

Safety Checks and Constraints

CubeSandbox enforces strict validation to prevent misconfigurations:

  • Domain-only allowlists require deny-all: Enforced by validateAllowOutDomainsRequireDenyAll to ensure domains are not accidentally whitelisted while other traffic is permitted.
  • Wildcard restrictions: Only the *. prefix is allowed (e.g., *.example.com). The isDomainAllowOutTarget function rejects any other * placement in the string.
  • Credential isolation: Secrets are stored only in CubeMaster and injected by CubeEgress just before the outbound request leaves the host, never exposing credentials to sandbox code.

Summary

  • CubeSandbox uses CubeVS for L3/L4 CIDR filtering and CubeEgress for L7 domain and path-based rules.
  • Domain allowlists (allow_out) require explicit deny_out: ["0.0.0.0/0"] to pass validation enforced in sdk/go/policy.go.
  • L7 rules support matching on SNI, Host, Method, Path, and Scheme, with actions for allowing, denying, or injecting credentials via the Inject struct.
  • Policies propagate from the SDK through CubeMaster to the network-agent, which pushes them to CubeEgress via the admin API defined in network-agent/internal/service/cubeegress_push.go.
  • Wildcard domains must use the *. prefix format, and credential injection occurs securely at the proxy layer without exposing secrets to sandbox code.

Frequently Asked Questions

What is the difference between CubeVS and CubeEgress?

CubeVS operates at the network layer (L3/L4) using eBPF to filter traffic based on IP CIDRs and manage NAT, while CubeEgress operates at the application layer (L7) as an OpenResty/NGINX proxy that inspects HTTP/HTTPS requests, enforces domain policies, injects headers, and generates audit logs. CubeVS handles the initial allow/deny decisions for IP traffic, while CubeEgress applies fine-grained rules to HTTP-specific attributes like SNI and Host headers.

Why do I need to set deny_out to 0.0.0.0/0 when using domain allowlists?

The validation logic in sdk/go/policy.go (lines 60-71) requires this configuration to prevent security gaps. Domain-based allowlists only make sense in a "deny-all" mode where all other traffic is explicitly blocked. Without 0.0.0.0/0 in deny_out, the API returns a 400 error with the message allowOutDomainRequiresDenyAll, ensuring that domain whitelisting does not accidentally coexist with permissive network access.

How does credential injection work in CubeEgress?

When a request matches a rule with Action.Allow: true and contains Inject objects, CubeEgress adds the specified headers to the outbound request just before it leaves the host. The secret values are stored in CubeMaster and never exposed to the sandbox code; only the proxy performs the injection using the format specified in the Inject struct's Header and Format fields.

Can I use wildcards in domain allowlists?

Yes, but only with the *. prefix format (e.g., *.example.com). The isDomainAllowOutTarget function validates that wildcards appear only at the beginning of the domain string. Any other placement of * in the string is rejected during validation, ensuring that wildcard matching remains predictable and secure.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →