| Surface | Exposure | Rationale |
|---|---|---|
| Coder dashboard/API | 443 (Caddy/Let's Encrypt), 80 (ACME only) | Coder handles authn; TLS everywhere |
| Control plane SSH | IAP tunnel only (gcloud compute ssh --tunnel-through-iap) |
no public 22 |
| Workspace VMs | no inbound rules at all | agent connects outbound; access via Coder relay (coder ssh/config-ssh) |
| Cloud SQL | private IP only, VPC peering | never on the public internet |
There is deliberately no public SSH anywhere. Workspace access rides Coder's authenticated WebSocket/WireGuard path.
| Principal | Roles | Why |
|---|---|---|
coder-control-plane SA |
compute.instanceAdmin.v1, iam.serviceAccountUser (on the workspace SA only), logging.logWriter, monitoring.metricWriter, secretAccessor (db-url secret only) |
runs the Terraform provisioner that creates/deletes workspace VMs |
coder-workspace SA |
logging.logWriter, monitoring.metricWriter |
workspaces need nothing else; instance-identity agent auth needs an attached SA but no roles |
| Terraform (you) | your user ADC | no long-lived bootstrap SA keys exist |
The previous iteration had these inverted (workspaces had compute.admin,
the control plane couldn't create VMs) — fixed.
- DB connection URL: Secret Manager only; fetched by the control plane VM
at boot via its SA. Never in Terraform-rendered metadata. (Terraform state
does contain the generated DB password — state is local and gitignored;
treat
terraform.tfstateas sensitive.) - Coder agent auth: Google instance identity — no token in VM metadata.
- Watchdog token: owner session token, regenerated by Coder on every
workspace start, written to
~/.config/agent-watchdog/envmode 0600 on the workspace's own disk. - Nothing in git:
.gitignoreblocks.env, tfvars, tfstate. CI secret scanning is moot — there are no committed credentials.
| Secret | Rotate by |
|---|---|
| DB password | terraform taint random_password.db_password && terraform apply, then reboot the control plane VM (it re-reads the secret at boot) |
| Coder session tokens | revoke in Coder UI (/settings/tokens); workspace watchdog tokens rotate themselves every workspace start |
| GitHub / Claude / Codex credentials | gh auth logout/login, claude /logout + re-login, codex logout/login inside the workspace |
Workspaces authenticate with gh auth login (HTTPS + gh credential helper
via gh auth setup-git). Credentials are stored once per workspace on its
persistent disk, scoped to your GitHub user. No deploy keys or shared
machine credentials exist. If/when this should be automatic for new
workspaces, switch to Coder's GitHub external-auth (OAuth app) — documented
as a TODO in the README.
Interactive logins (claude browser-code flow, codex login --device-auth)
stored by the CLIs in $HOME on the workspace disk. No API keys exist in
GCP or this repo. The disk is the trust boundary: deleting the workspace
deletes the credentials.
- Cloud SQL connection uses
sslmode=disable— traffic stays on the private VPC peering; enabling TLS adds cert management for little gain here. - Workspace VMs have ephemeral public IPs for egress (package installs, GitHub, agent APIs) instead of Cloud NAT (~$32/mo+). With zero inbound rules, exposure is limited to outbound-initiated traffic.
- Coder dashboard is on the public internet (it must be, for agents and for you). Single admin account; enable OIDC SSO later if desired.