Skip to content

deploy:setup-ci: create the cluster's trust policy and deploy role once #145

Description

@dawsontoth

Part of #143.

Problem

The workflow in #144 authenticates with GitHub's OIDC token. The cluster accepts that token only under a trust policy a super user created, and nothing in create-harper creates one. The policy has to match exactly. When it doesn't, the exchange in CI is refused with a 401 that, on purpose, doesn't say why.

What a GitHub Actions policy needs (add_oidc_trust, 5.3.0):

  • issuer: https://token.actions.githubusercontent.com.
  • audience: the target exactly as the CLI requests its token, with an explicit port and a trailing slash. add_oidc_trust refuses one without them.
  • claims: repository_id, which survives a rename; workflow_ref (<owner>/<repo>/.github/workflows/deploy.yaml@refs/heads/main), which pins both the workflow and the branch; and environment: production.
  • user: the user the run acts as. operations: what its token may run.

Proposal

A script shipped in every template as npm run deploy:setup-ci, run by a super user after harper login. It is safe to run again, and should be rerun whenever what the workflow runs changes. It:

  1. Reads the GitHub repository from git remote get-url origin, or asks.
  2. Looks up the repository's numeric id with gh api repos/<owner>/<repo> or the public API, and asks if neither answers.
  3. Creates or updates a role with { "super_user": false, "operations": ["deploy_component", "get_job"] } and no table permissions. get_job is for the workflow's wait on the rolling job. The documentation's example CI role leaves it out, and an allowlist refuses whatever it doesn't list.
  4. Creates a user in that role if there isn't one, with a random password that is never printed or stored. Nothing signs in with it.
  5. Calls add_oidc_trust with the values above, operations scoped to the same list, and a description naming the repository and workflow. Running it again replaces the policy.
  6. Reads the policy back with list_oidc_trust, and stops on an invalid_reason.
  7. Sets the HARPER_CLI_TARGET repository variable with gh variable set when gh is signed in, and otherwise prints the command.
  8. Prints how to revoke. harper drop_oidc_trust id=<id> stops new runs. A token already issued lasts out its hour, so in an incident also run harper alter_user username=<user> active=false.

Write it in Node, not shell, because templates run on Windows too. Derive the role, user and policy names from the project, so several projects can share a cluster.

The scaffolder can't run the script, because the GitHub repository rarely exists yet. After the cluster URL and harper login prompts (lib/steps/getEnvVars.js), the closing message lists the next steps instead: push to GitHub, then run npm run deploy:setup-ci.

What the role does not limit

deploy_component runs whatever main holds on the cluster, as the server. The role stops a token from doing what the workflow doesn't do, but it doesn't make deploying less powerful. Deploys are really guarded by who can merge to main. The README should tell people to protect main and, if they want, to require a reviewer on the production environment. HarperFast/harper#3046 will let the role be limited to this one component.

Related

Done when

On a fresh repository and a 5.3+ cluster, running the script and then pushing to main produces a green deploy. Running the script again changes nothing. A policy that can't match, such as one with the wrong audience, is reported by the script and not by a 401 in CI.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Fields

Priority

P1

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions