# Networking and security

Sandboxes are built to be secure-by-default, meaning that a default Sandbox has
no ability to accept incoming network connections or access your Modal resources.

## Networking

Since Sandboxes may run untrusted code, they have options to restrict their network access.
To block all network access, set `block_network=True` on [`Sandbox.create`](/docs/reference/modal.Sandbox#create).

For more fine-grained networking control, a Sandbox's outbound network access
can be restricted using the `outbound_cidr_allowlist` parameter. This parameter takes a
list of CIDR ranges that the Sandbox is allowed to access, blocking all other
outbound traffic.

### Connecting to Sandboxes with HTTP and WebSockets

You can make authenticated HTTP and WebSocket requests to a Sandbox by generating
Sandbox Connect Tokens. They work like this:

```python notest
# Start a Sandbox with a server running on port 8080.
sb = modal.Sandbox.create(
    "bash", "-c", "python3 -m http.server 8080",
    app=my_app,
)

# Create a connect token, optionally including arbitrary user metadata.
creds = sb.create_connect_token(user_metadata={"user_id": "foo"})

# Make an HTTP request, passing the token in the Authorization header.
requests.get(creds.url, headers={"Authorization": f"Bearer {creds.token}"})

# You can also put the token in a `_modal_connect_token` query param.
url = f"{creds.url}/?_modal_connect_token={creds.token}"
ws_url = url.replace("https://", "wss://")
with websockets.connect(ws_url) as socket:
    socket.send("Hello world!")

sb.detach()
```

The server running on port 8080 in the container will receive an authenticated
request with an unspoofable `X-Verified-User-Data` header whose value is the
JSON-serialized Python dict that was passed as `user_metadata` to the
`create_connect_token()` function. This can be used by the application to
determine access control, for example.

There are a few things to remember with Sandbox Connect Tokens:

1. The server inside the container must be listening on port 8080.
2. The token may be sent in an `Authorization` header, in a `_modal_connect_token`
   query param, or in a `_modal_connect_token` cookie.
3. If `_modal_connect_token` is set as a query param, the resulting response will
   include a `Set-Cookie` header that sets it as a cookie.
4. The `user_metadata` must be JSON-serializable and must be less than 512
   characters after serialization.

### Forwarding ports

While it is recommended to use [Sandbox Connect Tokens](#connecting-to-sandboxes-with-http-and-websockets)
for HTTP requests and WebSocket connections to the container, you can also expose
raw TCP ports to the internet. This is useful if, for example, you want to run a
server inside the Sandbox that expects a raw TCP connection and handles
authentication itself.

Use the `encrypted_ports` and `unencrypted_ports` parameters of `Sandbox.create`
to specify which ports to forward. You can then access the public URL of a tunnel
using the [`Sandbox.tunnels`](/docs/reference/modal.Sandbox#tunnels) method:

```python notest
import requests
import time

sb = modal.Sandbox.create(
    "python",
    "-m",
    "http.server",
    "12345",
    encrypted_ports=[12345],
    app=my_app,
)

tunnel = sb.tunnels()[12345]

time.sleep(1)  # Wait for server to start.

print(f"Connecting to {tunnel.url}...")
print(requests.get(tunnel.url, timeout=5).text)

sb.detach()
```

It is also possible to create an encrypted port that uses `HTTP/2` rather than `HTTP/1.1` with the `h2_ports` option. This will return
a URL that you can make H2 (HTTP/2 + TLS) requests to. If you want to run an `HTTP/2` server inside a sandbox, this feature may be useful.
Here is an example:

```python notest
import time

port = 4359
sb = modal.Sandbox.create(
    app=my_app,
    image=my_image,
    h2_ports = [port],
)
p = sb.exec("python", "my_http2_server.py")

tunnel = sb.tunnels()[port]
time.sleep(1)
print(f"Tunnel URL: {tunnel.url}")

sb.detach()
```

For more details on how tunnels work, see the [tunnels guide](/docs/guide/tunnels).

### Custom domains

<Callout variant="gated-feature">

Custom domains for Sandbox tunnels are available on the <a href="/pricing">Team and Enterprise plans</a>. Visit <a href="/settings/plans">workspace settings</a> to upgrade.

</Callout>

<Callout variant="beta">

The infrastructure is production-grade, but onboarding requires a manual setup step.

</Callout>

By default, Sandbox tunnels are served from subdomains of `w.modal.host`.
In some cases, it's necessary to have a tunnel served through a custom domain
for security reasons. This is possible with manual setup.

Note that tunnel custom domains are distinct from other custom domains in Modal.
Other custom domains use `CNAME` forwarding. For tunnels, we need to use an
`NS` record to delegate the domain to Modal's nameservers.

**1. Delegate a (sub)domain to Modal's nameservers.**

Add `NS` records to your DNS zone pointing to Modal's nameservers. For example,
to use `sandbox.example.com`, add the following records in your DNS provider's
control panel:

| Name                  | Type | Value                |
| --------------------- | ---- | -------------------- |
| `sandbox.example.com` | NS   | `w-ns-a.modal.host.` |
| `sandbox.example.com` | NS   | `w-ns-b.modal.host.` |
| `sandbox.example.com` | NS   | `w-ns-c.modal.host.` |
| `sandbox.example.com` | NS   | `w-ns-d.modal.host.` |

You can delegate any subdomain depth you like (e.g. `tunnels.a.b.c.example.com`).

**2. Ask Modal to set up the domain.**

Reach out to us on Slack and provide the domain name. We'll enable it for your
workspace.

**3. Pass `custom_domain` to `Sandbox.create`.**

```python notest
import modal

app = modal.App.lookup("my-app", create_if_missing=True)
sb = modal.Sandbox.create(
    "python", "-m", "http.server", "8080",
    encrypted_ports=[8080],
    custom_domain="sandbox.example.com",
    app=app,
)

tunnel = sb.tunnels()[8080]
print(tunnel.url)  # https://[...].sandbox.example.com
```

Modal will provision a TLS certificate automatically. Sandbox Connect Tokens generated
for this sandbox will also use the custom domain.

## Security model

Sandboxes are built on top of [gVisor](https://gvisor.dev/), a container runtime
by Google that provides strong isolation properties. gVisor has custom logic to
prevent Sandboxes from making malicious system calls, giving you stronger isolation
than most other container runtimes.

Additionally, Sandboxes are not authorized to access other resources in your Modal
workspace the way that Modal Functions are [by default](/docs/guide/restricted-access).
As a result, the blast radius of any malicious code will be limited to the Sandbox
container itself.
