Skip to content

Runbook: Provisioning Backstage Access to ArgoCD

Status: Active — implemented for the 7kgroup canary (hub ArgoCD)

Purpose

Provision a dedicated, least-privilege ArgoCD account for the Backstage portal (roadie argo-cd plugin), so the portal reads Applications without the admin password. This is a one-time, hub-level step against the shared management-plane ArgoCD (configured in 7k-inari-fleet). Every client's Backstage instance reuses this account — repeat the steps with a different account name only if per-client isolation is required.

The account is token-only (apiKey, no interactive login) and scoped to role:backstage = applications, get, */* (read-only: no create/update/delete, sync, repositories, clusters, or accounts).

Prerequisites

  • Write access to github.com/7k-group/7k-inari-fleet.
  • kubectl access to the management-plane cluster (the argocd namespace).
  • ArgoCD admin credentials — only to mint the account token; discarded after.
  • Vault write access (backstage/argocd).

Background: where ArgoCD config lives

The hub ArgoCD is Helm argo-cd (chart 9.5.14), self-managed by the argocd Application with two sources:

  • chart argo-cd, values from $values/fleet/bootstrap/values.yaml
  • fleet/bootstrap (applied as manifests; extras/argocd-cm.yaml holds account config)

Therefore:

  • argocd-cm account config → edit fleet/bootstrap/extras/argocd-cm.yaml
  • argocd-rbac-cm policy → edit fleet/bootstrap/values.yaml (configs.rbac)

Both are reconciled by ArgoCD self-heal — edit the repo, never patch live (a live edit is reverted within seconds).

Steps

1. Declare the account (fleet repo)

In fleet/bootstrap/extras/argocd-cm.yaml, under data:, add:

accounts.backstage: apiKey
accounts.backstage.enabled: "true"

2. Add least-privilege RBAC (fleet repo)

In fleet/bootstrap/values.yaml, append to configs.rbac.policy.csv:

g, backstage, role:backstage
p, role:backstage, applications, get, */*, allow

Commit both files. The self-managing argocd app syncs within a few minutes, or force it:

kubectl -n argocd annotate application argocd argocd.argoproj.io/refresh=hard --overwrite

3. Verify the account + role landed

kubectl -n argocd get cm argocd-cm -o jsonpath='{.data.accounts\.backstage}'
kubectl -n argocd get cm argocd-rbac-cm -o jsonpath='{.data.policy\.csv}' | grep backstage

4. Mint the account token

Exec into argocd-server, authenticate as admin, and generate a token. The command below sources the admin password from the backstage-env-vars secret; if provisioning before Backstage exists, use argocd-initial-admin-secret ({.data.password}) instead.

ADMIN_PW_B64=$(kubectl -n backstage get secret backstage-env-vars -o jsonpath='{.data.ARGOCD_PASSWORD}')
kubectl -n argocd exec deploy/argocd-server -- sh -c \
  "PW=\$(printf '%s' $ADMIN_PW_B64 | base64 -d); \
   argocd login localhost:8080 --username admin --password \"\$PW\" --plaintext >/dev/null 2>&1 && \
   argocd account generate-token --account backstage --id backstage-portal"

The token is a JWT (sub: backstage:apiKey, jti: backstage-portal) with no expiry. It is revocable via its jti (see Rotation below).

5. Store the token in Vault

vault kv put backstage/argocd token="<jwt>"

This replaces the legacy admin password key — the portal no longer needs it.

6. Point the Backstage addon at the token

Already done in the golden template (clients/_templates/cnoe/backstage/):

  • manifests/external-secrets.yaml maps Vault backstage/argocd:tokenARGOCD_TOKEN
  • values.yaml sets argocd.appLocatorMethods[].instances[].token: ${ARGOCD_TOKEN}

Sync the backstage-<client> app; the ExternalSecret pulls the token and the pod restarts with it.

7. Verify

Token-level (run inside argocd-server, --auth-token <jwt>):

argocd account can-i get applications 'default/test'    # -> yes
argocd account can-i create applications 'default/test' # -> no
argocd app list                                          # -> lists applications

can-i needs a project/app object (e.g. default/test). Testing with a bare * false-negatives because the policy object */* requires a /.

Portal-level: the backstage pod logs show Plugin initialization complete ... 'argocd' with no 401/403, and ArgoCD data loads on catalog entities.

Rotation / revocation

  • Rotate: generate a new token with a fresh id (--id backstage-portal-<date>), update Vault, let ESO refresh (or restart the backstage pod), then delete the old id.
  • Revoke: argocd account delete-token --account backstage --id backstage-portal

TODO

  • Automate account + token provisioning (currently a manual admin step).
  • Decide whether each client gets a dedicated ArgoCD account (isolation) or all share the single hub backstage account.