Identity and access

How people sign in to Nistor on Google Cloud through Identity-Aware Proxy, how the first administrator is set, and how permissions and service accounts work.

In 1.0, Nistor signs people in with Google identities only. Identity-Aware Proxy (IAP) sits in front of the web shell, and Nistor's own permissions decide what each signed-in person may do.

How a request is authenticated#

  • IAP is on the frontend service only. The frontend module turns on Cloud Run's built-in IAP integration (iap_enabled = true). IAP signs the person in before the request reaches the web shell, and adds their identity to every request.
  • The backend has no IAP of its own. The web shell's server forwards /api/* to the backend, together with the identity headers that IAP added. Only the service accounts that the root trusts can call the backend: Cloud Run's run.invoker permission is the gate.
  • Which identity the backend believes. The backend prefers the signed IAP assertion (X-Goog-IAP-JWT-Assertion), checked against the frontend's IAP audience (PROXY_IAP_AUDIENCE). While the root's input trust_iap_header_fallback is true (the default), a missing or unverifiable assertion falls back to the plain e-mail header that IAP sets. Set it to false once the backend logs show that every browser call carries a verified assertion. After that, a browser call without one is refused with 401.

Give people access#

Two things must be true for a person to reach Nistor:

  1. IAP is turned on for the frontend service. The root's terraform apply does that.
  2. The person, or a group they belong to, holds IAP-secured Web App User (roles/iap.httpsResourceAccessor) on the frontend service. Without it, IAP answers 403 before the web shell loads.

The root does not make the second grant. The account that runs terraform apply usually lacks the right to set IAP policy, so an owner or an IAP administrator makes the grant once:

gcloud iap web add-iam-policy-binding \
  --resource-type=cloud-run \
  --service=<frontend service name> \
  --region=<region> \
  --project=<project> \
  --member='group:nistor-users@example.com' \
  --role='roles/iap.httpsResourceAccessor'

To check who has access, and that IAP is on (the command fails when IAP is off):

gcloud iap web get-iam-policy --resource-type=cloud-run \
  --service=<frontend service name> --region=<region> --project=<project>

Also check that nothing bypasses IAP. The frontend's run.invoker policy must not list allUsers or allAuthenticatedUsers:

gcloud run services get-iam-policy <frontend service name> --region=<region> --project=<project>

IAP's own service agent (service-<project number>@gcp-sa-iap.iam.gserviceaccount.com) needs roles/run.invoker on the frontend to forward signed-in requests. The frontend module can manage that binding (manage_run_invoker_iam, off by default).

Who becomes a Nistor user#

  • The first administrator. The root's bootstrap_admin_email is created as an administrator when the backend starts. A backend running on Cloud Run refuses to start without it, in every environment. Pass it at plan time (TF_VAR_bootstrap_admin_email); do not commit it in a tfvars file.
  • Everyone else. The first time someone signs in, the backend registers them with no permissions, provided their e-mail domain is in auto_register_email_domains (a comma-separated list). In test and prod, the backend refuses to start when this list is empty. An empty list would register anyone IAP lets through.
  • An administrator then gives each user permissions. Permissions are codes such as catalog:view or finops:view. Role templates are named sets of permissions that you apply to a user in one step. The API is under /api/v1/iam: GET /permissions, GET /role-templates, GET /users, PUT /users/{email}/permissions and POST /users/{email}/apply-template/{template_id}.

In the web shell, the same is under Platform → IAM Admin: + Add User registers someone ahead of their first sign-in, and Edit access on a user's row applies a role or single permissions (Customize permissions), saved with Save changes. See First sign-in.

Service accounts#

  • Each role runs as its own service account: role_service_accounts names one per role (backend, scheduler, jobs, compiler, input_logs, runner, dq_rebuild, migrate, frontend, and mcp and workspace_events when those roles are on). The root wires the trust between roles call by call. There is no shared account.
  • The install pipeline's account (ci_cd_service_account_email) can read each role's /health/installed and report a deployment, and nothing more. It is not an administrator.
  • Acting for a user. The backend's own service account may call the backend on behalf of the person whose request started the work, with the X-Nistor-Acting-For header. The AI assistant's tool calls work this way. Only the account named in the backend's SELF_SERVICE_ACCOUNT setting may send that header. When it is empty, no call can act for a user.

Edit this page on GitLab

↑ ↓ to moveEnter to openEsc to close