Console Playground
← Blog

Zerotrust vs Semitrusted: Choosing Your Network Mode

When you execute code on unsandbox, you’re running inside an isolated LXC container. But isolation means different things depending on your use case. Sometimes you want maximum security with no network access. Sometimes you need to pip install a package or call an API.

That’s why unsandbox offers two network modes: zerotrust and semitrusted.

The Two Modes at a Glance

Mode Network Access Default Use Case
zerotrust None Yes Untrusted code, security-critical
semitrusted Via egress proxy No Package installation, API calls

Let’s dig into each.

Zerotrust: Complete Network Isolation

Zerotrust is the default mode. When you execute code without specifying a network mode, you get zerotrust.

# These are equivalent - both use zerotrust
un script.py
un -n zerotrust script.py

What Zerotrust Means

Your container has no network interfaces connected to the outside world. Period.

$ un -s bash 'curl https://google.com'
curl: (6) Could not resolve host: google.com

$ un -s bash 'ping 8.8.8.8'
ping: connect: Network is unreachable

DNS doesn’t resolve. TCP connections fail. UDP packets go nowhere. Your code runs in a network vacuum.

Why This Matters

Consider what happens when you run untrusted code:

  1. Malicious script tries to exfiltrate data - Can’t. No network.
  2. Cryptominer wants to phone home - Can’t. No network.
  3. Reverse shell attempts to connect out - Can’t. No network.
  4. Supply chain attack in a dependency - Can’t exfiltrate. No network.

Zerotrust isn’t about trusting the network. It’s about not needing to trust the code.

When to Use Zerotrust

  • Running user-submitted code - Students, code golf, online judges
  • Executing AI-generated code - LLM tool use where you can’t verify safety
  • Testing untrusted snippets - Code from the internet, Stack Overflow, forums
  • Security-sensitive operations - Processing secrets, cryptographic operations
  • Reproducible builds - No network means deterministic execution

The Trade-Off

Zerotrust containers can’t:

  • Fetch packages (pip install, npm install, etc.)
  • Make HTTP requests to external APIs
  • Download files from the internet
  • Resolve DNS queries
  • Connect to databases hosted elsewhere

If your code needs any of these, you need semitrusted.

Semitrusted: Internet Through a Proxy

Semitrusted mode gives your container internet access, but all traffic flows through our egress proxy.

# Enable network access
un -n semitrusted script.py

# With inline code
un -s python -n semitrusted 'import requests; print(requests.get("https://httpbin.org/ip").text)'

How It Works

Your container connects to the internet through a transparent proxy:

Container → Egress Proxy → Internet

The proxy:

  • Allows outbound HTTP/HTTPS (ports 80, 443)
  • Allows outbound DNS (port 53)
  • Blocks inbound connections - Nothing can connect TO your container
  • Logs traffic metadata - We can audit what domains are contacted
  • Rate limits egress - Prevents abuse

What You Can Do

# Install packages
$ un -s bash -n semitrusted 'pip install requests && python -c "import requests; print(requests.__version__)"'
2.31.0

# Call APIs
$ un -s python -n semitrusted 'import urllib.request; print(urllib.request.urlopen("https://api.github.com").read()[:100])'
b'{"current_user_url":"https://api.github.com/user",...

# Download files
$ un -s bash -n semitrusted 'curl -O https://example.com/data.json && ls -la data.json'
-rw-r--r-- 1 root root 1256 Jan 15 10:00 data.json

Why “Semitrusted”?

We call it semitrusted because you’re extending partial trust to the code:

  • You trust it won’t abuse network access - Spam, attacks, illegal content
  • You trust it won’t exfiltrate sensitive data - If you’re passing secrets
  • You accept visibility into what it does - Traffic is logged

The container still can’t:

  • Accept inbound connections (no reverse shells)
  • Access your local network
  • Bypass the proxy
  • Make unlimited requests (rate limits apply)

When to Use Semitrusted

  • Development with dependencies - Installing packages, testing integrations
  • API integrations - Code that needs to call external services
  • Web scraping - Fetching data from websites
  • Testing networking code - Verifying HTTP clients work
  • Long-running services - Apps that need to serve or consume APIs

CLI Examples

Basic Execution

# Zerotrust (default) - no network
un script.py

# Semitrusted - with network
un -n semitrusted script.py

Interactive Sessions

# Zerotrust session
un session

# Semitrusted session - install packages as needed
un session -n semitrusted

Services

Services can also specify network mode in their configuration:

# Create a service with network access
un service --name my-api --ports 8080 --network semitrusted --bootstrap "pip install flask && python app.py"

API Examples

Execute Endpoint

POST /execute
{
  "language": "python",
  "code": "import requests\nprint(requests.get('https://httpbin.org/ip').text)",
  "network": "semitrusted"
}

For zerotrust, omit the network field or set it to "zerotrust":

POST /execute
{
  "language": "python",
  "code": "print(2 + 2)"
}

Session Endpoint

POST /session
{
  "network": "semitrusted"
}

Security Comparison

Threat Zerotrust Semitrusted
Data exfiltration Impossible Possible (logged)
Cryptomining Can’t phone home Could attempt
Reverse shell Can’t connect out Blocked by proxy
Supply chain attack Can’t fetch Could fetch malicious packages
DDoS participation No network Rate limited
Port scanning No network Blocked by proxy

Making the Choice

Use Zerotrust When:

  1. You don’t control the code - User submissions, AI output, untrusted sources
  2. Security is paramount - Processing secrets, financial data, PII
  3. You need reproducibility - Same inputs = same outputs, no network variance
  4. The code is self-contained - No external dependencies or API calls needed

Use Semitrusted When:

  1. You need packages - pip install, npm install, cargo build
  2. You’re calling APIs - HTTP requests to external services
  3. You control the code - Your own scripts, trusted sources
  4. You’re developing/testing - Interactive development with network

The Middle Ground

Often the best approach is layered:

# Step 1: Install dependencies in a semitrusted session
un session -n semitrusted
> pip install numpy pandas requests
> # save snapshot

# Step 2: Run untrusted code from that snapshot in zerotrust
un session --snapshot my-deps-snapshot
> # Now you have packages but no network

Or use snapshots:

# Create a base image with all dependencies
un session -n semitrusted --name setup
> pip install -r requirements.txt
> exit

# Snapshot it
un session --snapshot setup

# Run zerotrust sessions from that snapshot
un session --restore my-snapshot
# Has packages, no network access

FAQ

Q: Can I switch modes mid-session? A: No. Network mode is set when the container starts. Create a new session with the desired mode.

Q: Does semitrusted cost more? A: No. Both modes are included in all plans.

Q: Can I whitelist specific domains? A: Not currently. Semitrusted allows all outbound HTTP/HTTPS.

Q: Is zerotrust really zero network? A: Yes. No loopback exceptions, no local network, no DNS. The container has no route to anywhere.

Q: What about localhost connections? A: Localhost within the container works (127.0.0.1). But the container can’t reach your host machine’s localhost.

Q: Can I run a web server in zerotrust? A: You can run it, but nothing external can connect to it. Use services with semitrusted for web apps.

Conclusion

Zerotrust and semitrusted represent a fundamental trade-off in sandbox design:

  • Zerotrust: Maximum security, minimum capability
  • Semitrusted: Network access, reduced isolation

There’s no wrong choice - only the right choice for your use case. Run untrusted code in zerotrust. Develop and test in semitrusted. Use snapshots to bridge the gap.

The network mode is just a flag. The security model is what matters.

# Maximum security
un script.py

# When you need the network
un -n semitrusted script.py

Choose wisely. Execute safely.