Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Security Model

Cloud SSH is suitable only for a trusted self-hosted deployment operated for one trusted team. The current local PTY runtime is intentionally not a hostile-user isolation boundary.

Trust Boundary

Every live room starts one process tree as the Unix account running cloud-ssh. All rooms share that account’s:

  • filesystem and file permissions;
  • process and signal permissions;
  • environment inherited from the service;
  • network namespace and reachable services;
  • host CPU, memory, and kernel.

Project roles control Cloud SSH operations such as attach, input, resize, control, event read, and snapshot read. They do not create operating-system isolation between room processes.

Do not use the local runtime for:

  • public signup;
  • mutually hostile users or teams;
  • untrusted code execution;
  • hosted multi-tenant workloads;
  • secrets that one room process must be unable to read from another room’s service account.

A container runtime with a separately reviewed image, filesystem, capability, resource, network, cleanup, and audit model is a release gate for those uses.

On Unix, runtime shutdown signals both the PTY foreground process group and the original session-leader process group, escalating from SIGTERM to SIGKILL. POSIX has no portable signal for every process group in a session, so a trusted program that creates a new session, daemonizes, or deliberately escapes those groups may survive. On Windows, the current fallback terminates only the direct child; it does not provide Job Object tree ownership. Deploy with a cgroup, container, or platform supervisor when complete descendant ownership is a hard requirement.

Identity Boundary

One process serves one configured tenant. Deployment owners have tenant-wide administrative power independent of project membership. Project collaboration uses developer and viewer roles. The database transactionally protects the last active owner.

Browser sessions use random opaque tokens. SQLite stores keyed token digests, not raw cookies. Session validity, idle expiry, absolute expiry, principal status, and revocation are checked against SQLite. State-changing browser requests require a separate CSRF token. WebSocket upgrades require the expected public origin. The CSRF-protected POST /api/auth/v1/session reads the current session and refreshes its idle activity only after session and CSRF validation; rejected requests have no activity side effect. It does not rotate the cookie on every read, so concurrent tabs cannot invalidate one another’s request token. A fresh opaque cookie is issued only when a session is created. The endpoint is not a cross-site navigable GET. Session revocation, expiry, principal disablement, and room-access loss still close an attachment fail-closed.

SSH authentication accepts active Cloud SSH public keys attached to active principals. A Cloud SSH principal is not an operating-system user. Before each state-changing room command, SSH and WebSocket revalidate credentials and room access before forwarding it to the actor. The command side effect and SSH PTY output delivery share an authorization gate with durable revocation and role changes, so either occurs entirely before or after the metadata commit. A mutation first validates its completed request without the gate, then acquires the write gate and repeats session, CSRF, and authorization validation before it commits and notifies attached rooms. This prevents rejected requests from starving active terminal authorization checks without weakening the ordering boundary. Idle and output-only connections also revalidate periodically. Revocation and role downgrade converge without waiting for reconnect.

OAuth/OIDC identities are keyed by stable provider subject. Provider secrets are resolved from operator environment references and are never returned by management APIs. Remote provider URLs require HTTPS and reject credentials, fragments, and private literal addresses except explicit localhost development fixtures.

Secrets

Keep these outside source control and image layers:

  • CLOUD_SSH_SESSION_HMAC_KEY;
  • OAuth and OIDC client secrets;
  • CLOUD_SSH_METRICS_BEARER_TOKEN;
  • the SSH private host key;
  • reverse-proxy TLS private keys;
  • SQLite backups containing identity and terminal history.

Use a secret manager or deployment-platform secret injection. Restrict the data volume, host key, environment source, and backup directory to the service and operators. Rotate provider and metrics secrets through their owning systems. Rotating the session HMAC key intentionally invalidates current browser sessions and outstanding login attempts.

Data Sensitivity

Terminal output and snapshots are durable plaintext BLOBs inside SQLite. They may contain command output, paths, source code, or application secrets. Audit events and identity metadata are in the same backup boundary. Encrypt disks and backups outside Cloud SSH, restrict backup access, and choose retention values that match the data classification.

Raw terminal input is not stored in room events and is excluded from Debug output. This reduces exposure of typed secrets, but a shell can echo input into terminal output, where it becomes durable history.

Network Deployment

Expose browser traffic through HTTPS. Keep SQLite local to the service host. Restrict SSH and metrics ports with network policy. Metrics require an independent bearer token by default; anonymous metrics are acceptable only on a protected scrape network.

Set CLOUD_SSH_TRUST_PROXY_HEADERS=true only behind a trusted proxy that replaces client-address headers. The production HTTP listener must remain behind that trusted reverse proxy, which enforces connection limits plus bounded header and request-body read timeouts before forwarding HTTP or WebSocket upgrades. Cloud SSH does not apply a blanket transport timeout because an established interactive WebSocket must remain long-lived. Ensure the configured public base URL exactly matches the externally visible browser origin. Preserve one stable SSH host key and publish its fingerprint through a trusted channel.

Logging And Metrics

Request logs omit query strings and include bounded request metadata only. Code and review gates prohibit logging terminal input, cookies, CSRF tokens, provider access or ID tokens, client secrets, session HMAC material, and SSH private keys. Configuration Debug output redacts secrets and URL credentials.

Metrics exclude tenant, project, room, principal, client, key fingerprint, and target selector labels. This prevents identity leakage and unbounded label cardinality. Error responses expose stable safe codes; internal database and provider details remain in controlled logs, not client bodies.

Failure Behavior

Bounds apply to live rooms, clients, actor mailboxes, terminal input, journal records, rate-limit keys, HTTP bodies, provider responses, and catch-up pages. Slow clients are detached.

SQLite durability failure is fail-closed. A terminal journal error stops input, marks every live generation degraded, terminates and waits for PTYs, and rejects new starts. A restart marks prior nonterminal generations abandoned rather than claiming process continuity. Graceful SIGINT/SIGTERM shutdown drains terminal reader tails and commits final history before the journal exits.

Disk exhaustion, database contention, and corrupt or unsupported snapshot data must be treated as operational incidents. Do not delete WAL files manually or edit generation/history rows. Follow Operations.

Security Review Checklist

The signed-off target review is published in the Security Review Report. Its acceptance applies only to the trusted self-hosted model above and is invalid for hostile-user execution.

Before exposing a deployment:

  1. Confirm every user and workload belongs to one trusted team.
  2. Run as a dedicated unprivileged service account or UID 10001 container user.
  3. Use HTTPS, a stable public origin, and a stable SSH host key.
  4. Store session, provider, metrics, host-key, database, and backup secrets with least privilege.
  5. Require metrics authorization or an isolated scrape network.
  6. Configure explicit room/client/rate-limit/retention bounds.
  7. Rehearse backup restoration and verify terminal segments and snapshots.
  8. Alert on readiness failure, journal commit failure, durable lag, queue growth, rate limiting, and unexpected generation abandonment.
  9. Run make production-check against the exact source revision.
  10. Reject the deployment if hostile-user or hosted multi-tenant isolation is a requirement.