> ## Documentation Index
> Fetch the complete documentation index at: https://docs.declaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Layer-4 network enforcement

> Verify that raw TCP connections to denied hosts fail at the kernel level — not just at the L7 proxy.

## Use case

Regression probe for strict egress firewall rules. Before the fix, a
sandbox with `NetworkPolicy(allow_out=["api.openai.com"],
deny_out=[ALL_TRAFFIC])` relied solely on the L7 proxy to block by TLS
SNI. A raw `socket.create_connection(("evil.com", 443))` completed a
TCP handshake against the local proxy listener (the packet was
redirected there regardless of destination), and the probe reported
`REACH` even though the L7 policy would drop the connection shortly
after.

After the fix, only packets bound for resolved allow-list IPs are
redirected. Everything else hits the default DROP and the sandbox's
`connect()` returns `ConnectionRefusedError` / `ETIMEDOUT` at the
kernel -- matching what the policy promises.

## What you'll learn

* How Declaw enforces network policy at both L4 (firewall) and L7
  (TLS SNI proxy)
* Testing raw TCP connectivity to denied hosts
* Verifying that allowed hosts still pass through

## Prerequisites

<Snippet file="snippets/env-setup.mdx" />

## Code walkthrough

The security policy allows only `api.openai.com` and denies everything
else:

```python theme={null}
from declaw import ALL_TRAFFIC, NetworkPolicy, Sandbox, SecurityPolicy

POLICY = SecurityPolicy(
    network=NetworkPolicy(
        allow_out=["api.openai.com"],
        deny_out=[ALL_TRAFFIC],
    ),
)
```

The probe script tests three paths: L4 raw TCP to a denied host, L7
HTTP to a denied host, and L4 raw TCP to the allowed host:

```python theme={null}
PROBE = """
import socket, urllib.request

def tcp(host, port, timeout=5):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(timeout)
    try:
        s.connect((host, port))
        return "REACH"
    except Exception as e:
        return f"BLOCKED: {type(e).__name__}: {e}"
    finally:
        s.close()

def l7(url, timeout=5):
    try:
        urllib.request.urlopen(url, timeout=timeout)
        return "REACH"
    except Exception as e:
        return f"BLOCKED: {type(e).__name__}: {str(e)[:80]}"

print("l4_evil_com:", tcp("evil.com", 443))
print("l4_cloudflare_dns:", tcp("1.1.1.1", 443))
print("l7_evil_com:", l7("https://evil.com"))
print("l4_allowed_openai:", tcp("api.openai.com", 443))
"""
```

Run the probe and inspect the results:

```python theme={null}
sbx = Sandbox.create(template="python", timeout=120, security=POLICY)
try:
    sbx.files.write("/tmp/script.py", PROBE)
    r = sbx.commands.run("python3 /tmp/script.py", timeout=60)
    print(r.stdout)
finally:
    sbx.kill()
```

## Expected output

```
l4_evil_com:       BLOCKED: ConnectionRefusedError: ...
l4_cloudflare_dns: BLOCKED: ConnectionRefusedError: ...
l7_evil_com:       BLOCKED: URLError: ...
l4_allowed_openai: REACH

VERDICT  : PASS
```

* **L4 denied** -- raw TCP to `evil.com` and `1.1.1.1` fails at the
  kernel with `ConnectionRefused` or `ETIMEDOUT`.
* **L7 denied** -- `urllib` to `evil.com` also fails (SNI rejected by
  the proxy as defense in depth).
* **L4 allowed** -- raw TCP to `api.openai.com` succeeds, proving the
  allow-list path is not over-restricted.

## Full source

See `cookbook/examples/network-l4-enforcement/main.py` in the repo.
