VPS access model (brioso-vps, decided 2026-10-13, BRI-433)

VPS access model (brioso-vps, decided 2026-10-13, BRI-433)

Who may log in to the new box, as which account, and how the root surface is limited. Read this before touching accounts, sudoers, keys or the Tailscale ACL.

Accounts

Account Used by Auth Privilege
deploy GitHub Actions (pyinfra converge, activate, smoke, health) ed25519 key VPS_PYINFRA_SSH_KEY, public half in deploy/ssh/ci-authorized-keys.pub, installed with restrict NOPASSWD: ALL (a converge needs arbitrary root). Only when ci_full_sudo is set
brioso-admin-<name> one human each personal key, Tailscale full sudo, never used by automation, so logs stay separable
diag teammates and cloud agents (tag:agent) Tailscale SSH journald read, status of the four web slot units, restart of the two slots of its env. No shell as root
web-services app and service units none (no login) rootless, owns /opt/brioso

Decisions

  • The legacy deploy.yml (deploy-vps.sh) is not used on the new box. deploy is elevated instead of adding a new CI account. The narrow script grant stays on the old box until it is retired, so the elevation is opt in: VPS_CI_FULL_SUDO=1 (inventory ci_full_sudo), set as a repo variable together with VPS_SSH_USER=deploy at the flip.
  • Human admin and automation never share an account. CI actions show up as deploy in the journal and auth.log.
  • Other tenants on this VPS run rootless (a dedicated user, podman or a quadlet) unless explicitly flagged. If a second, unrelated tenant arrives, the isolation decision (separate VM or container per tenant) is made then. On a shared host, root is root.
  • Cheap hardening in place: restrict on the CI key (no forwarding, no pty), tag:ci limited by ACL to ports 22, 443, 6006 (Phoenix), 41230 (turbo), the key only in GitHub environment secrets (beta, production), sudo logged by the journal. Use nono kernel sandboxing for units that do not need broad access (litestream, cloudflared already do).
  • The CI Tailscale identity is an OAuth client that mints tag:ci nodes in the usebrioso team tailnet. Tailscale SSH (tailscale set --ssh) is only needed for diag and agents. Turn it on only after the ssh ACL section has rules for admins (brioso-admin-*, check), deploy for tag:ci and diag for tag:agent (accept), or tailnet SSH for admins breaks.

Tailscale ACL checklist (usebrioso tailnet)

  1. tagOwners: tag:brioso-vps-ovh, tag:ci, tag:agent.
  2. grants: members to the box on 22 and 3900 and 443, tag:ci to 22, 443, 6006, 41230, tag:agent to 22 only.
  3. ssh: members to the box as brioso-admin-* (check), tag:ci as deploy (accept), tag:agent and members as diag (accept).
  4. OAuth client with auth_keys write for tag:ci (GitHub), a separate client or ephemeral key for tag:agent.

Flip checklist for access

  1. GitHub environments beta and production: TS_OAUTH_CLIENT_ID, TS_OAUTH_SECRET, VPS_TAILSCALE_HOST, VPS_PYINFRA_SSH_KEY (private half of the CI key).
  2. Repo variables: VPS_SSH_USER=deploy, VPS_CI_FULL_SUDO=1.
  3. Converge users with VPS_CI_FULL_SUDO=1 before the first CI run (done on the new box 2026-10-13).
  4. VPS_DEPLOY_SSH_KEY and the legacy deploy.yml stay pointed at the old box and are retired with it.