Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions deploy/docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#
# Quick start:
#
# 1. Start the gateway:
# 1. Start the gateway (init first generates JWT keys in /var/lib/openshell/tls):
# docker compose up -d
#
# 2. Register the gateway with the CLI (one-time):
Expand Down Expand Up @@ -49,19 +49,16 @@
# bind-mount source when sandbox containers are created. Named volumes
# cannot be used here because Docker resolves bind-mount sources against the
# host filesystem, not the container filesystem.
#
# Linux note:
# host.docker.internal and host.openshell.internal are not automatically
# added on Linux Docker. Add the following under the gateway service:
# extra_hosts:
# - "host.docker.internal:host-gateway"
# - "host.openshell.internal:host-gateway"

services:
gateway:
image: ghcr.io/nvidia/openshell/gateway:${IMAGE_TAG:-latest}
image: &gateway-image ghcr.io/nvidia/openshell/gateway:${IMAGE_TAG:-latest}
restart: unless-stopped

depends_on:
init:
condition: service_completed_successfully

# Clear the default CMD so gateway.toml owns all settings (see note above).
command: []

Expand Down Expand Up @@ -96,7 +93,8 @@ services:
# (e.g. /var/lib/openshell/gateway): the path must match exactly on
# both the host and inside the container, and a single gateway per host
# is the expected topology.
- type: bind
- &openshell-data
type: bind
source: /var/lib/openshell
target: /var/lib/openshell
bind:
Expand Down Expand Up @@ -126,3 +124,14 @@ services:
# bind-mounted directory so its path is resolvable by the host Docker daemon.
XDG_DATA_HOME: /var/lib/openshell
HOME: /var/lib/openshell

# One-shot: generates the sandbox JWT keys (kept if present).
init:
image: *gateway-image
user: "0"
restart: "no"
command: ["generate-certs", "--output-dir", "/var/lib/openshell/tls"]
environment:
HOME: /var/lib/openshell
volumes:
- *openshell-data
26 changes: 14 additions & 12 deletions deploy/docker/gateway.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,8 @@
# clearing the CMD first.
#
# grpc_endpoint note:
# host.docker.internal is automatically resolvable from containers on
# Docker Desktop (Windows / macOS). On Linux, add extra_hosts to the
# gateway service:
# extra_hosts:
# - "host.docker.internal:host-gateway"
# - "host.openshell.internal:host-gateway"
# Omitted on purpose. The supervisor uses host networking and reaches the
# published port at http://127.0.0.1:8080 (keep OPENSHELL_PORT at 8080).

[openshell]
version = 2
Expand All @@ -33,6 +29,18 @@ log_level = "info"
compute_driver = "docker"
disable_tls = true

# No TLS and loopback only: user calls are unauthenticated.
# Supervisors still authenticate with the JWT keys below.
[openshell.gateway.auth]
allow_unauthenticated_users = true

# Written by the compose init service.
[openshell.gateway.gateway_jwt]
signing_key_path = "/var/lib/openshell/tls/jwt/signing.pem"
public_key_path = "/var/lib/openshell/tls/jwt/public.pem"
kid_path = "/var/lib/openshell/tls/jwt/kid"
gateway_id = "openshell-docker"

[openshell.drivers.docker]
# Default image pulled for `openshell sandbox create` without --from.
default_image = "nvcr.io/nvidia/base/ubuntu:24.04"
Expand All @@ -44,12 +52,6 @@ supervisor_image = "ghcr.io/nvidia/openshell/supervisor:latest"
image_pull_policy = "if_not_present"
# Value assigned to the openshell.sandbox_namespace label on sandbox containers.
sandbox_label = "openshell"
# Address sandbox containers use to call back to the gateway.
# The Docker driver replaces the host with host.openshell.internal and the
# port with the gateway's own bind port (8080). Only the scheme survives.
# The gateway must be published on port 8080 on the Docker host so that
# host.openshell.internal:8080 resolves to the gateway container.
grpc_endpoint = "http://host.openshell.internal:8080"
# Explicit supervisor-compatible Docker default. Set RuntimeDefault or
# Localhost/<profile> only when the daemon host has AppArmor available.
app_armor_profile = "Unconfined"
28 changes: 27 additions & 1 deletion docs/how-it-works/gateways/container-deployment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ the repository contains a production-ready Compose setup with full inline docume

| File | Purpose |
|---|---|
| `docker-compose.yml` | Gateway service, volumes, and environment variables |
| `docker-compose.yml` | Gateway and init services, volumes, and environment variables |
| `gateway.toml` | TOML configuration mounted into the container |

Clone or copy those files, then start the gateway:
Expand All @@ -165,6 +165,8 @@ Clone or copy those files, then start the gateway:
docker compose -f deploy/docker/docker-compose.yml up -d
```

The `init` service runs first and writes the sandbox JWT signing keys to `/var/lib/openshell/tls` on the host. The Docker driver requires them, and existing keys are kept. Do not commit them. This setup disables TLS and user authentication and publishes the port on loopback only.

Register the gateway with the CLI. If registering from the same machine:

```shell
Expand All @@ -178,6 +180,30 @@ machine's LAN address:
openshell gateway add http://HOST_IP:8080 --remote --name remote
```

### Enable mTLS

The `init` service already writes the server, client, and CA certificates to `/var/lib/openshell/tls`, so enabling mTLS only needs configuration changes:

1. In `gateway.toml`, remove `disable_tls = true` and the `[openshell.gateway.auth]` table.
2. In `docker-compose.yml`, add `OPENSHELL_LOCAL_TLS_DIR: /var/lib/openshell/tls` under the `gateway` service `environment`. The gateway loads its server certificate, client CA, and sandbox client bundle from that directory, and turns on mTLS user authentication.
3. Apply the change:

```shell
docker compose -f deploy/docker/docker-compose.yml up -d
```

Then copy the client bundle to `~/.config/openshell/gateways/local/mtls/`, where the CLI loads it (refer to [Gateway Authentication](/how-it-works/gateways/authentication#mtls)). The files are owned by root, so copy them with `sudo` and hand them to your user. If you registered the plaintext gateway as `local`, run `openshell gateway remove local` first.

```shell
mtls=~/.config/openshell/gateways/local/mtls
mkdir -p "$mtls"
sudo cp /var/lib/openshell/tls/ca.crt \
/var/lib/openshell/tls/client/tls.crt \
/var/lib/openshell/tls/client/tls.key "$mtls"/
sudo chown "$USER" "$mtls"/*
openshell gateway add https://127.0.0.1:8080 --local --name local
```

## Using Podman

Replace `docker` with `podman` in the commands above. Mount the Podman socket instead of the Docker socket and set the driver to `podman`:
Expand Down
Loading