-
Notifications
You must be signed in to change notification settings - Fork 9
docs(security): OIDC trusted publishing (v5.3.0), ops-table anchor fixes, badge guidance #637
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ab1d543
a2f41ac
676b176
e9b474c
92aa185
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,7 +44,8 @@ For remote Operations API commands, the CLI uses the first complete authenticati | |
| 4. Legacy `CLI_TARGET_USERNAME` and `CLI_TARGET_PASSWORD` environment variables | ||
| 5. `HARPER_CLI_OPERATION_TOKEN` and `HARPER_CLI_REFRESH_TOKEN` environment variables, or their legacy `CLI_TARGET_` equivalents — see [Token credentials for CI/CD](#token-credentials-for-cicd) | ||
| 6. A token saved by `harper login` | ||
| 7. `username=` and `password=` operation parameters (legacy fallback) | ||
| 7. A [workload identity token](#workload-identity-oidc) exchanged with the runtime's OIDC provider (v5.3.0) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we also update the recommendation table immediately below this list? It still tells every CI/CD pipeline to use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes — fixed in Worth saying how it got that way, since it is a process failure rather than a judgment one. I had made exactly this edit on the earlier branch. When the OIDC content was extracted onto this one I re-applied the precedence entry and the Workload identity section but not the table row, so the page ended up recommending a rotatable secret three paragraphs above telling readers they need no stored credential. Re-applying a section by hand loses the edits that were scattered outside it. 🤖 Addressed by Claude Code |
||
| 8. `username=` and `password=` operation parameters (legacy fallback) | ||
|
|
||
| :::tip | ||
| **Configure one credential style per context, not two.** Precedence exists to resolve a conflict, but it resolves it silently, and the ways this page describes for authentication to go wrong all need two styles live at once: a payload `username=`/`password=` pair takes over when a token stops resolving, a blank token variable hands the run to whatever saved login the machine has. Pick one and leave the others unset: | ||
|
|
@@ -62,7 +63,7 @@ Credentials are resolved as a pair and are never combined across sources. An inc | |
|
|
||
| Entry 5 is not a two-step fallback the way 3 and 4 are: whichever token namespace is merely **set** claims the choice, so a blank `HARPER_CLI_REFRESH_TOKEN` shadows a complete `CLI_TARGET_REFRESH_TOKEN` instead of deferring to it — see below. | ||
|
|
||
| Entries 5 and 6 authenticate with a bearer token and apply to **remote targets only**. A local operation goes over the domain socket, which the server already trusts — as, by default, it trusts any loopback address, since `authentication.authorizeLocal` defaults to `true`. Token environment variables are deliberately ignored on the socket path — a token minted for one instance would otherwise be attached to every local `harper` command in that shell and rejected. Note what that trust means on a self-hosted runner: an unset or blank `target` does not fail. The CLI falls back to the target saved by a previous `harper login` on that machine, and only if there is none does it go local — where it runs as superuser on the socket's ambient trust, with no credential checked at all. So a job that loses its `HARPER_CLI_TARGET` either deploys to whatever remote that runner last logged into, or to the runner's own node. Neither is an error, and the first is the wider blast radius. | ||
| Entries 5 through 7 authenticate with a bearer token and apply to **remote targets only**. A local operation goes over the domain socket, which the server already trusts — as, by default, it trusts any loopback address, since `authentication.authorizeLocal` defaults to `true`. Token environment variables are deliberately ignored on the socket path — a token minted for one instance would otherwise be attached to every local `harper` command in that shell and rejected. Note what that trust means on a self-hosted runner: an unset or blank `target` does not fail. The CLI falls back to the target saved by a previous `harper login` on that machine, and only if there is none does it go local — where it runs as superuser on the socket's ambient trust, with no credential checked at all. So a job that loses its `HARPER_CLI_TARGET` either deploys to whatever remote that runner last logged into, or to the runner's own node. Neither is an error, and the first is the wider blast radius. | ||
|
|
||
| Before v5.2.0, `username=` and `password=` operation parameters took precedence over environment variables and saved login tokens. This could authenticate an operation as the wrong user when those fields were part of the operation payload, such as the user being created by `add_user`. | ||
|
|
||
|
|
@@ -255,6 +256,47 @@ Create a **dedicated CI user** and run `harper login --for-ci` as that user. Tha | |
| There is no operation that revokes a refresh token directly. To invalidate one, either run `harper login --for-ci` again as that user — minting overwrites the stored hash, so the previous token stops working — or deactivate the user with `alter_user`. `harper logout` is not a revocation: it deletes your local copy and leaves the server-side hash valid. | ||
| ::: | ||
|
|
||
| ##### Workload identity (OIDC) | ||
|
|
||
| <VersionBadge version="v5.3.0" /> | ||
|
dawsontoth marked this conversation as resolved.
|
||
|
|
||
| On a runner that can prove its own identity, the CLI needs **no stored credential at all**. It asks the runtime for an identity token addressed to your instance and trades it for a one-hour operation token. Nothing durable is stored in your CI provider, and there is no 30-day token to rotate. | ||
|
|
||
| Configure the instance to trust the workflow once with [`add_oidc_trust`](../operations-api/operations.md#add_oidc_trust), then grant the token permission in the workflow. | ||
|
|
||
| The block below is **GitHub Actions** syntax — `permissions: id-token: write` is how Actions specifically opts a job into requesting an identity token. Other CI systems expose the same idea differently, and Harper currently detects GitHub Actions only; an unrecognized runtime is not an error, the CLI simply falls through to its other credential sources. | ||
|
|
||
| ```yaml | ||
| # .github/workflows/deploy.yml | ||
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| environment: production | ||
| steps: | ||
| - run: harper deploy by_ref=true restart=true replicated=true | ||
| env: | ||
| HARPER_CLI_TARGET: ${{ vars.HARPER_CLI_TARGET }} # a var, not a secret | ||
| ``` | ||
|
dawsontoth marked this conversation as resolved.
|
||
|
|
||
| `HARPER_CLI_TARGET` is the only variable the step needs, and it is not sensitive — hence `vars` rather than `secrets`. | ||
|
|
||
| **With a trust policy in place you need nothing else** — no `HARPER_CLI_REFRESH_TOKEN`, no password, no credentials on the command. That is the point of it: the workflow holds no Harper secret at all. | ||
|
|
||
| If your pipeline already sets a token or password, remove it. Leaving it in place is not harmful but it does keep winning — a configured credential outranks the exchange, deliberately, so that enabling this does not silently re-point an existing pipeline at a different user. See [Authentication Precedence](#authentication-precedence) if you need the full order for a mixed setup. | ||
|
|
||
| The exchange is attempted only when the runtime actually offers an identity. GitHub Actions sets `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` together on a job that declares `id-token: write`; without both, the CLI falls through to its other credential sources rather than reporting a failure. An unrecognized runtime falls through the same way. | ||
|
|
||
| On success the CLI prints, to stderr, which policy authenticated it and as whom: | ||
|
|
||
| ``` | ||
| Requesting a GitHub Actions identity token for https://my-instance.harperdb.io:9925/... | ||
| Authenticated as 'ci-deploy' via OIDC trust policy 'my-app-prod'. | ||
| ``` | ||
|
|
||
| If Harper rejects the token, the CLI reports it and carries on rather than aborting. On a workflow configured as above there is nothing left to try, so the operation itself fails with a 401 — but do not treat a rejected exchange as a guaranteed hard stop: if anything else on the runner can authenticate, the command proceeds under that identity instead. [Authentication Precedence](#authentication-precedence) covers those cases. | ||
|
|
||
| The server deliberately does not report which check failed — see [`exchange_oidc_token`](../operations-api/operations.md#exchange_oidc_token) — so diagnose with `list_oidc_trust` and the instance's `oidc-trust` log. | ||
|
|
||
| #### Method 3: Dedicated Authentication Parameters | ||
|
|
||
| <VersionBadge version="v5.2.0" /> | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.