> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/openshell/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/openshell/_mcp/server.

# OpenShell Security Best Practices for Controls, Risks, and Configuration Guidance

> A guide to every configurable security control in OpenShell: defaults, what you can change, and the risks of each choice.

OpenShell enforces sandbox security across four layers: network, filesystem, process, and inference.
This page documents every configurable control, its default, what it protects, and the risk of relaxing it.

For the full policy YAML schema, refer to the [Policy Schema](/reference/policy-schema).
For the architecture of each enforcement layer, refer to [How OpenShell Works](/about/how-it-works).

If you use [NemoClaw](https://github.com/NVIDIA/NemoClaw), its [Security Best Practices](https://docs.nvidia.com/nemoclaw/latest/security/best-practices.html) guide covers additional entrypoint-level controls, policy presets, provider trust tiers, and posture profiles specific to the NemoClaw blueprint.

## Enforcement Layers

OpenShell applies security controls at two enforcement points.
OpenShell locks static controls at sandbox creation and requires destroying and recreating the sandbox to change them.
You can update dynamic controls on a running sandbox with `openshell policy update` or `openshell policy set`.

| Layer      | What it protects                                                | Enforcement point                                | Changeable at runtime                                                                        |
| ---------- | --------------------------------------------------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| Network    | Unauthorized outbound connections and data exfiltration.        | CONNECT proxy + OPA policy engine                | Yes. Use `openshell policy update`, `openshell policy set`, or operator approval in the TUI. |
| Filesystem | System binary tampering, credential theft, config manipulation. | Landlock LSM (kernel level)                      | No. Requires sandbox re-creation.                                                            |
| Process    | Privilege escalation, fork bombs, dangerous syscalls.           | Seccomp BPF + privilege drop (`setuid`/`setgid`) | No. Requires sandbox re-creation.                                                            |
| Inference  | Credential exposure, unauthorized model access.                 | Proxy intercept of `inference.local`             | Yes. Use `openshell inference set`.                                                          |

## Network Controls

The CONNECT proxy and OPA policy engine enforce all network controls at the gateway level.

### Deny-by-Default Egress

Every outbound connection from the sandbox goes through the CONNECT proxy.
The proxy evaluates each connection against the OPA policy engine.
If no `network_policies` entry matches the destination host, port, and calling binary, the proxy denies the connection.

| Aspect              | Detail                                                                                                                                                                                                           |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | All egress denied. Only endpoints listed in `network_policies` can receive traffic.                                                                                                                              |
| What you can change | Add entries to `network_policies` in the policy YAML. Apply statically at creation (`--policy`) or dynamically (`openshell policy update` for incremental changes, `openshell policy set` for full replacement). |
| Risk if relaxed     | Each allowed endpoint is a potential data exfiltration path. The agent can send workspace content, credentials, or conversation history to any reachable host.                                                   |
| Recommendation      | Add only endpoints the agent needs for its task. Start with a minimal policy and use denied-request logs (`openshell logs <name> --source sandbox`) to identify missing endpoints.                               |

### Network Namespace Isolation

The sandbox runs in a dedicated Linux network namespace with a veth pair.
All traffic routes through the host-side veth IP (`10.200.0.1`) where the proxy listens.
Even if a process ignores proxy environment variables, it can only reach the proxy.

| Aspect              | Detail                                                                                                                   |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Default             | Always active. The sandbox cannot bypass the proxy at the network level.                                                 |
| What you can change | This is not a user-facing knob. OpenShell always enforces it in proxy mode.                                              |
| Risk if bypassed    | Without network namespace isolation, a process could connect directly to the internet, bypassing all policy enforcement. |
| Recommendation      | No action needed. OpenShell enforces this automatically.                                                                 |

### User Namespace Isolation

Kubernetes user namespaces (`hostUsers: false`) map container UID 0 to an unprivileged host UID.
Capabilities like `CAP_SYS_ADMIN` become namespaced. They grant power over container-local resources only, not the host.
This provides defense-in-depth: even if a container escape vulnerability exists, the attacker lands as an unprivileged host user.

| Aspect                   | Detail                                                                                                                                                                                                           |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default                  | Disabled. Set `server.enableUserNamespaces: true` in Helm values or `enable_user_namespaces = true` in the gateway config to enable cluster-wide.                                                                |
| What you can change      | Enable cluster-wide through Helm or gateway config. Override per-sandbox through the `user_namespaces` field on `SandboxTemplate` in the API.                                                                    |
| Prerequisites            | Kubernetes 1.33+ with user namespace support available (beta through 1.35, GA in 1.36+), a container runtime that supports user namespaces (containerd 2.0+, CRI-O 1.25+), and Linux 5.12+ for ID-mapped mounts. |
| Risk if enabled with GPU | NVIDIA device plugin compatibility with user namespaces is unverified. OpenShell logs a warning when both GPU and user namespaces are active on the same sandbox.                                                |
| Recommendation           | Enable on non-GPU clusters running Kubernetes with user namespace support available (1.33+ beta, 1.36+ GA) for stronger host isolation. Test GPU workloads separately before enabling on GPU clusters.           |

### Binary Identity Binding

The proxy identifies which binary initiated each connection by reading `/proc/<pid>/exe` (the kernel-trusted executable path).
It walks the process tree for ancestor binaries and parses `/proc/<pid>/cmdline` for script interpreters.
The proxy SHA256-hashes each binary on first use (trust-on-first-use). If someone replaces a binary mid-session, the hash mismatch triggers an immediate deny.

| Aspect              | Detail                                                                                                                                                                                                   |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | Every `network_policies` entry requires a `binaries` list. Only listed binaries can reach the associated endpoints. Binary paths support glob patterns (`*` for one path component, `**` for recursive). |
| What you can change | Add binaries to an endpoint entry. Use glob patterns for directory-scoped access (for example, `/sandbox/.vscode-server/**`).                                                                            |
| Risk if relaxed     | Broad glob patterns (like `/**`) allow any binary to reach the endpoint, defeating the purpose of binary-scoped enforcement.                                                                             |
| Recommendation      | Scope binaries to the specific executables that need each endpoint. Use narrow globs when the exact path varies (for example, across Python virtual environments).                                       |

### L4-Only vs L7 Inspection

The `protocol` field on an endpoint controls whether the proxy inspects individual HTTP requests inside the tunnel.

| Aspect              | Detail                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | Endpoints without a `protocol` field use L4-only enforcement: the proxy checks host, port, and binary, then relays the TCP stream without inspecting payloads.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| What you can change | Add `protocol: rest` to enable per-request HTTP method/path inspection, `protocol: websocket` to inspect RFC 6455 upgrade handshakes and client text messages, or `protocol: graphql` to inspect GraphQL-over-HTTP operation type, operation name, and root fields. WebSocket endpoints can also use GraphQL operation rules for GraphQL-over-WebSocket messages. Pair inspected protocols with `rules` or access presets (`full`, `read-only`, `read-write`). REST endpoints that need credential placeholders in supported text request bodies can set `request_body_credential_rewrite: true`.                                                                                                                                                                                                                                                         |
| Risk if relaxed     | L4-only endpoints allow the agent to send any data through the tunnel after the initial connection is permitted. The proxy cannot see HTTP methods, paths, or GraphQL operations. Adding `access: full` with L7 inspection enables observability but permits all inspected actions.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| Recommendation      | Use `protocol: rest` with specific `rules` for APIs where intent is encoded in method and path. Add `request_body_credential_rewrite: true` only for REST APIs that require OpenShell-managed credentials in UTF-8 JSON, form, or text request bodies. Use `protocol: graphql` for GraphQL-over-HTTP APIs where destructive operations are body-encoded. Use `protocol: websocket` for RFC 6455 endpoints, with explicit `GET` and `WEBSOCKET_TEXT` rules for raw text protocols or explicit GraphQL operation rules for GraphQL-over-WebSocket. Prefer `access: read-only` or explicit allowlists, and deny hash-only persisted queries unless you maintain a trusted registry. Omit `protocol` for non-HTTP protocols. For WebSocket endpoints that must carry placeholder credentials in client text frames, add `websocket_credential_rewrite: true`. |

### Enforcement Mode (`audit` vs `enforce`)

When L7 inspection is active, the `enforcement` field controls whether the proxy blocks or logs rule violations.

| Aspect              | Detail                                                                                                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | `audit`. The proxy logs violations but forwards traffic.                                                                                                                        |
| What you can change | Set `enforcement: enforce` to block requests that do not match any `rules` entry. Denied requests receive a `403 Forbidden` response with a JSON body describing the violation. |
| Risk if relaxed     | `audit` mode provides visibility but does not prevent unauthorized actions. An agent can still perform write or delete operations on an API even if the rules would deny them.  |
| Recommendation      | Start with `audit` to understand traffic patterns and verify that rules are correct. Switch to `enforce` after you validate that the rules match the intended access pattern.   |

### TLS Handling

The proxy auto-detects TLS on every tunnel by peeking the first bytes.
When a TLS ClientHello is detected, the proxy terminates TLS transparently using a per-sandbox ephemeral CA.
This enables credential injection and L7 inspection without explicit configuration.

| Aspect              | Detail                                                                                                                                                                                                                                 |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | Auto-detect and terminate. OpenShell generates the sandbox CA at startup and injects it into the process trust stores (`NODE_EXTRA_CA_CERTS`, `DENO_CERT`, `SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`). |
| What you can change | Set `tls: skip` on an endpoint to disable TLS detection and termination for that endpoint. Use this for client-certificate mTLS to upstream or non-standard binary protocols.                                                          |
| Risk if relaxed     | `tls: skip` disables credential injection and L7 inspection for that endpoint. The proxy relays encrypted traffic without seeing the contents.                                                                                         |
| Recommendation      | Use auto-detect (the default) for most endpoints. Use `tls: skip` only when the upstream requires the client's own TLS certificate (mTLS) or uses a non-HTTP protocol.                                                                 |

### SSRF Protection

After OPA policy allows a connection, the proxy resolves DNS and rejects undeclared internal destinations.

| Aspect              | Detail                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Default             | The proxy blocks private IPs for undeclared, wildcard, hostless, and policy-advisor-proposed endpoints. Exact hostnames declared in user policy may resolve to private RFC 1918 addresses. Loopback (`127.0.0.0/8`), link-local (`169.254.0.0/16`), and unspecified (`0.0.0.0`) addresses are always blocked and cannot be overridden with `allowed_ips`.                                                                                                                                                                                                                                                                                                                      |
| What you can change | Declare a known internal service as an exact `host` endpoint, or add `allowed_ips` (CIDR notation) to an endpoint to permit specific private IP ranges for wildcard or hostless policies. On Linux, the proxy consults the sandbox's `/etc/hosts` before DNS, so Kubernetes `hostAliases` can make LAN-only hostnames resolvable. Policies with `allowed_ips` entries that overlap loopback, link-local, or unspecified addresses fail to load with a clear validation error.                                                                                                                                                                                                  |
| Risk if relaxed     | Without SSRF protection, a misconfigured policy could allow the agent to reach cloud metadata services (`169.254.169.254`), internal databases, or other infrastructure endpoints through DNS rebinding.                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| Recommendation      | Prefer exact hostname endpoints for stable internal services. Use `allowed_ips` when you need hostless or wildcard authorization, and scope the CIDR as narrowly as possible (for example, `10.0.5.20/32` for a single host). Loopback, link-local, and unspecified addresses are always blocked regardless of `allowed_ips`. `hostAliases` change resolution, not authorization: `host: searxng.local` with `/etc/hosts` mapping to `192.168.1.105` is trusted only when that exact hostname is declared by user policy. The policy advisor does not propose rules for always-blocked destinations and still requires a separate `allowed_ips` edit for private-IP endpoints. |

### Operator Approval

When the agent requests an endpoint not in the policy, OpenShell blocks it and surfaces the request in the TUI for operator review.
The system merges approved endpoints into the sandbox's policy as a new durable revision.

| Aspect              | Detail                                                                                                                                                                                                             |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Default             | Enabled. The proxy blocks unlisted endpoints and requires approval.                                                                                                                                                |
| What you can change | Approved endpoints persist across sandbox restarts within the same sandbox instance. They reset when the sandbox is destroyed and recreated.                                                                       |
| Risk if relaxed     | Approving an endpoint permanently widens the running sandbox's policy. Review each request before approving.                                                                                                       |
| Recommendation      | Use operator approval for exploratory work. For recurring endpoints, add them to the policy YAML with appropriate binary and path restrictions. To reset all approved endpoints, destroy and recreate the sandbox. |

## Filesystem Controls

Landlock LSM restricts which paths the sandbox process can read or write at the kernel level.

### Landlock LSM

Landlock enforces filesystem access at the kernel level.
Paths listed in `read_only` receive read-only access.
Paths listed in `read_write` receive full access.
All other paths are inaccessible.

Landlock setup runs in two phases. The parent supervisor probes the kernel ABI and opens the configured path file descriptors before forking. The child then applies the ruleset with `restrict_self()` after privilege drop. At startup, OpenShell emits the selected ABI version and the applied read-only and read-write rule counts so you can confirm what the kernel accepted.

| Aspect              | Detail                                                                                                                                                                                                                                                                                         |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | `compatibility: best_effort`. Uses the highest kernel ABI available. Missing paths are skipped. If the kernel does not support Landlock or any configured path cannot be opened, the sandbox continues without those restrictions and emits a High-severity OCSF `DetectionFinding`.           |
| What you can change | Set `compatibility: hard_requirement` to abort sandbox startup if Landlock is unavailable or any configured path cannot be opened.                                                                                                                                                             |
| Risk if relaxed     | On kernels without Landlock (pre-5.13), or when all paths fail to open, the sandbox runs without kernel-level filesystem restrictions. The agent can access any file the process user can access.                                                                                              |
| Recommendation      | Use `best_effort` for development. Use `hard_requirement` in environments where any gap in filesystem isolation is unacceptable. Treat High-severity Landlock findings as a signal to investigate the host kernel or the image. Run on Ubuntu 22.04+ or any kernel 5.13+ for Landlock support. |

### Read-Only vs Read-Write Paths

The policy separates filesystem paths into read-only and read-write groups.

| Aspect              | Detail                                                                                                                                                                 |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | System paths (`/usr`, `/lib`, `/etc`, `/var/log`) are read-only. Working paths (`/sandbox`, `/tmp`) are read-write. `/app` is conditionally included if it exists.     |
| What you can change | Add or remove paths in `filesystem_policy.read_only` and `filesystem_policy.read_write`.                                                                               |
| Risk if relaxed     | Making system paths writable lets the agent replace binaries, modify TLS trust stores, or change DNS resolution. Validation rejects broad read-write paths (like `/`). |
| Recommendation      | Keep system paths read-only. If the agent needs additional writable space, add a specific subdirectory.                                                                |

### Path Validation

OpenShell validates policies before they take effect.

| Constraint                                                          | Behavior                          |
| ------------------------------------------------------------------- | --------------------------------- |
| Paths must be absolute (start with `/`).                            | Rejected with `INVALID_ARGUMENT`. |
| Paths must not contain `..` traversal.                              | Rejected with `INVALID_ARGUMENT`. |
| Read-write paths must not be overly broad (for example, `/` alone). | Rejected with `INVALID_ARGUMENT`. |
| Each path must not exceed 4096 characters.                          | Rejected with `INVALID_ARGUMENT`. |
| Combined `read_only` + `read_write` paths must not exceed 256.      | Rejected with `INVALID_ARGUMENT`. |

## Process Controls

The sandbox supervisor drops privileges, applies seccomp filters, and enforces process-level restrictions during startup.

### Privilege Drop

The sandbox process runs as a non-root user after explicit privilege dropping.

| Aspect              | Detail                                                                                                                                                                                                         |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | `run_as_user: sandbox`, `run_as_group: sandbox`. The supervisor calls `setuid()`/`setgid()` with post-condition verification, disables core dumps with `RLIMIT_CORE=0`, and on Linux sets `PR_SET_DUMPABLE=0`. |
| What you can change | Set `run_as_user` and `run_as_group` in the `process` section. Validation rejects root (`root` or `0`).                                                                                                        |
| Risk if relaxed     | Running as a higher-privilege user increases the impact of container escape vulnerabilities.                                                                                                                   |
| Recommendation      | Keep the `sandbox` user. Do not attempt to set root.                                                                                                                                                           |

### Seccomp Filters

OpenShell applies seccomp in two phases. A narrow supervisor-startup prelude runs before CLI parsing and async runtime initialization, then the child process receives the broader runtime seccomp filter after privilege drop.

| Aspect                               | Detail                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Startup prelude                      | After privileged bootstrap helpers complete, the supervisor sets `PR_SET_NO_NEW_PRIVS` and synchronizes a seccomp filter across all runtime threads that blocks `mount`, the new mount API syscalls, `pivot_root`, `umount2`, `bpf`, `perf_event_open`, `userfaultfd`, module-loading syscalls, and kexec. This closes the long-lived privileged remount and kernel-surface window while leaving required setup syscalls such as `setns` available. |
| Socket domains                       | The filter allows `AF_INET` and `AF_INET6` (for proxy communication) and blocks `AF_PACKET`, `AF_BLUETOOTH`, and `AF_VSOCK` with `EPERM`. `AF_NETLINK` is partially allowed: only `NETLINK_ROUTE` (protocol 0) is permitted so that `getifaddrs(3)` works; all other netlink protocols are blocked. Write operations via `NETLINK_ROUTE` still require `CAP_NET_ADMIN`, which the sandbox does not grant.                                           |
| Runtime unconditional syscall blocks | `memfd_create`, `ptrace`, `bpf`, `process_vm_readv`, `process_vm_writev`, `pidfd_open`, `pidfd_getfd`, `pidfd_send_signal`, `io_uring_setup`, `mount`, `fsopen`, `fsconfig`, `fsmount`, `fspick`, `move_mount`, `open_tree`, `setns`, `umount2`, `pivot_root`, `userfaultfd`, `perf_event_open`.                                                                                                                                                    |
| Conditional syscall blocks           | `execveat` with `AT_EMPTY_PATH`, `unshare` and `clone` with `CLONE_NEWUSER`, and `seccomp(SECCOMP_SET_MODE_FILTER)` are denied with `EPERM`.                                                                                                                                                                                                                                                                                                        |
| What you can change                  | This is not a user-facing knob. OpenShell enforces it automatically.                                                                                                                                                                                                                                                                                                                                                                                |
| Risk if relaxed                      | The blocked syscalls support container escape (`mount`, `pivot_root`, `move_mount`, namespace creation), cross-process observation (`ptrace`, `process_vm_readv`, `pidfd_*`), raw kernel bypass (`bpf`, `io_uring_setup`, `perf_event_open`), and filter evasion (`seccomp`, `userfaultfd`).                                                                                                                                                        |
| Recommendation                       | No action needed. OpenShell enforces this automatically.                                                                                                                                                                                                                                                                                                                                                                                            |

### Enforcement Application Order

The sandbox supervisor applies enforcement in a specific order during process startup.
This ordering is intentional: named network-namespace setup still relies on privileged helpers, and privilege dropping still needs `/etc/group` and `/etc/passwd`, which Landlock subsequently restricts.

1. Privileged supervisor bootstrap helpers, including network-namespace setup and optional `nft` probes.
2. Supervisor startup prelude seccomp (`PR_SET_NO_NEW_PRIVS` plus the early syscall denylist) synchronized across runtime threads.
3. Network namespace entry (`setns`) in child `pre_exec`.
4. Privilege drop (`initgroups` + `setgid` + `setuid`).
5. Core-dump hardening (`RLIMIT_CORE=0`, plus `PR_SET_DUMPABLE=0` on Linux).
6. Landlock filesystem restrictions.
7. Runtime seccomp socket domain and syscall filters.

## Inference Controls

OpenShell routes all inference traffic through the gateway to isolate provider credentials from the sandbox.

### Routed Inference through `inference.local`

The proxy intercepts HTTPS CONNECT requests to `inference.local` and routes matching inference API requests through the sandbox-local router.
The agent never receives the provider API key.

| Aspect               | Detail                                                                                                                                                                                                                                                                                                                                            |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Default              | Always active. The proxy handles `inference.local` before OPA policy evaluation. The gateway injects credentials on the host side.                                                                                                                                                                                                                |
| Keep-alive isolation | If a sandbox reuses a keep-alive connection that previously carried a routed inference request for a subsequent non-inference request, the proxy denies the non-inference request with `connection not allowed by policy` and closes the connection. This prevents agents from reusing an inference-authorized connection for other destinations. |
| What you can change  | Configure inference routes with `openshell inference set`.                                                                                                                                                                                                                                                                                        |
| Risk if bypassed     | If an inference provider's host is added directly to `network_policies`, the agent could reach it with a stolen or hardcoded key, bypassing credential isolation.                                                                                                                                                                                 |
| Recommendation       | Do not add inference provider hosts to `network_policies`. Use OpenShell inference routing instead.                                                                                                                                                                                                                                               |

## Gateway Security

The gateway secures communication between the CLI, sandbox workloads, and external clients with mutual TLS and token-based authentication.

### mTLS

Gateway transport uses TLS, with client certificate checks available where the deployment provides a client CA. Local single-user Docker, Podman, and VM gateways can use the verified client certificate as user authentication. Kubernetes deployments use the certificate bundle for transport and sandbox supervisor connectivity only; configure OIDC or a trusted access proxy for user authentication.

| Aspect              | Detail                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Default             | Local TLS bundles enable mTLS user authentication for single-user local gateways. Helm deployments generate mTLS certificates for transport, while sandbox supervisors authenticate API calls with gateway-minted sandbox JWTs. TLS-enabled loopback gateways also accept plaintext HTTP for sandbox service hostnames by default.                                                                                                                                 |
| What you can change | Configure OIDC or a trusted access proxy for multi-user gateways, set `OPENSHELL_ENABLE_MTLS_AUTH=true` for local single-user gateways, enable `server.auth.allowUnauthenticatedUsers=true` only for trusted local Kubernetes development or a fully trusted proxy, disable TLS only for trusted reverse-proxy setups, or disable loopback service HTTP with `--enable-loopback-service-http=false`.                                                               |
| Risk if relaxed     | Disabling TLS removes transport-level protection entirely. Allowing unauthenticated users removes the gateway user-auth boundary and must not be exposed to shared or public networks. Treating transport certificates as shared user identity in Kubernetes would collapse user and sandbox trust boundaries. Loopback service HTTP is local-only and rejects cross-origin browser requests, but any local process can still reach exposed service URLs directly. |
| Recommendation      | Use local mTLS user authentication only for single-user Docker, Podman, and VM gateways. Use OIDC or a trusted access proxy for Kubernetes and shared deployments.                                                                                                                                                                                                                                                                                                 |

### SSH Tunnel Authentication

SSH connections to sandboxes travel through the gateway over the sandbox supervisor's authenticated control path. Each SSH connect call also carries a short-lived session token scoped to a specific sandbox. The sandbox never exposes an SSH port on the network. Its SSH daemon listens on a local Unix socket that only the sandbox's own supervisor process can reach.

| Aspect              | Detail                                                                                                                                          |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Default             | Session tokens expire after 24 hours. Concurrent connections are limited to 10 per token and 20 per sandbox.                                    |
| What you can change | Configure `ssh_session_ttl_secs`. Set to 0 for no expiry.                                                                                       |
| Risk if relaxed     | Longer TTLs or no expiry increase the window for stolen token reuse. Higher connection limits increase the blast radius of a compromised token. |
| Recommendation      | Keep the 24-hour default. Monitor connection counts through the TUI.                                                                            |

## Common Mistakes

The following patterns weaken security without providing meaningful benefit.

| Mistake                                                           | Why it matters                                                                                                                                                                                                                         | What to do instead                                                                                      |
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Omitting an inspected protocol on REST or WebSocket API endpoints | Without `protocol: rest` or `protocol: websocket`, the proxy uses L4-only enforcement. It allows the TCP stream through after checking host, port, and binary, but cannot inspect individual HTTP requests or WebSocket text messages. | Add `protocol: rest` or `protocol: websocket` with specific `rules` to enable method and path control.  |
| Using `access: full` when finer rules would suffice               | `access: full` with `protocol: rest` or `protocol: websocket` enables inspection but allows all methods and paths for that protocol.                                                                                                   | Use `access: read-only` or explicit `rules` to restrict what the agent can do at the L7 level.          |
| Adding endpoints permanently when operator approval would suffice | Adding endpoints to the policy YAML makes them permanently reachable across all instances.                                                                                                                                             | Use operator approval. Approved endpoints persist within the sandbox instance but reset on re-creation. |
| Using broad binary globs                                          | A glob like `/**` allows any binary to reach the endpoint, defeating binary-scoped enforcement.                                                                                                                                        | Scope globs to specific directories (for example, `/sandbox/.vscode-server/**`).                        |
| Skipping TLS termination on HTTPS APIs                            | Setting `tls: skip` disables credential injection and L7 inspection.                                                                                                                                                                   | Use the default auto-detect behavior unless the upstream requires client-certificate mTLS.              |
| Setting `enforcement: enforce` before auditing                    | Jumping to `enforce` without first running in `audit` mode risks breaking the agent's workflow.                                                                                                                                        | Start with `audit`, review the logs, and switch to `enforce` after you validate the rules.              |

## Related Topics

* [Policies](/sandboxes/policies) for applying and iterating on sandbox policies.
* [Policy Schema](/reference/policy-schema) for the full field-by-field YAML reference.
* [Default Policy](/reference/default-policy) for the built-in default policy breakdown.
* [Gateway Auth](/reference/gateway-auth) for gateway authentication details.
* [How OpenShell Works](/about/how-it-works) for the system architecture.
* NemoClaw [Security Best Practices](https://docs.nvidia.com/nemoclaw/latest/security/best-practices.html) for entrypoint-level controls (capability drops, PATH hardening, build toolchain removal), policy presets, provider trust tiers, and posture profiles.