Manage Rancher users, global roles, tokens and settings #8

Merged
benvin merged 2 commits from benvin/users-roles-tokens-settings into main 2026-08-30 14:25:47 +10:00
Member

Why

The repo managed only the Keycloak(OIDC) auth provider and group role bindings; the rest of Rancher's config (users, custom global roles, API tokens, settings) was unmanaged clickops. server-url in particular was never asserted. This expands the same yaml-discovery convention the estate uses everywhere to four more rancher2 kinds.

How

  • config/config.hcl discovers users/, roles/, tokens/ and settings/ with the existing startswith-guarded comprehension; absent/empty dirs yield empty maps.
  • terragrunt.hcl passes the four new maps as inputs.
  • modules/rancher/variables.tf declares precise object schemas per kind (optional-with-default attributes, same style as keycloakoidc).
  • rancher2_user — filename is the username. password is required by the provider, so each entry carries a password_vault: {mount, path, key} ref and the module reads data.vault_kv_secret_v2. Optional global_role_bindings: [role] becomes rancher2_global_role_binding with user_id, keyed "<username>/<role>" so it never collides with the existing group-binding map.
  • rancher2_global_role — filename is the role name; new_user_default, inherited_cluster_roles, and rules (dynamic blocks: api_groups/resources/verbs/non_resource_urls/resource_names).
  • rancher2_tokendescription, ttl, renew, cluster_id.
  • rancher2_setting — filename is the setting name; seeds config/settings/server-url.yaml = https://rancher.k8s.syd1.au.unkin.net, matching the environment.
  • README documents each yaml schema.

Tokens caveat

Verified against the rancher2 14.1.1 provider schema (tofu providers schema -json): rancher2_token.user_id is computed, not settable — there is no user selector. Every token declared under config/tokens/ is minted for the identity the provider authenticates as (the admin token Terraform runs with), not for any user in config/users/. A token for another user must be created by that user. Token/secret values also land in Terraform state. This is called out in a comment on the resource and in the README.

Vault seeding

data.vault_kv_secret_v2 resolves at plan time, so a config/users/*.yaml entry whose Vault secret does not exist yet fails the plan. This PR therefore ships zero user and token yamls — only the empty directories (.gitkeep) and the one server-url setting — so plan needs no new KV seed. Entries land in follow-ups once their secrets are seeded.

Validation

uvx pre-commit run --all-files clean (tofu fmt, tofu validate, tflint, terragrunt hcl fmt, yamllint). terragrunt render --json confirms discovery: settings = {server-url}, users/roles/tokens empty, existing inputs unchanged.

## Why The repo managed only the Keycloak(OIDC) auth provider and group role bindings; the rest of Rancher's config (users, custom global roles, API tokens, settings) was unmanaged clickops. `server-url` in particular was never asserted. This expands the same yaml-discovery convention the estate uses everywhere to four more rancher2 kinds. ## How - `config/config.hcl` discovers `users/`, `roles/`, `tokens/` and `settings/` with the existing `startswith`-guarded comprehension; absent/empty dirs yield empty maps. - `terragrunt.hcl` passes the four new maps as inputs. - `modules/rancher/variables.tf` declares precise object schemas per kind (optional-with-default attributes, same style as `keycloakoidc`). - `rancher2_user` — filename is the username. `password` is required by the provider, so each entry carries a `password_vault: {mount, path, key}` ref and the module reads `data.vault_kv_secret_v2`. Optional `global_role_bindings: [role]` becomes `rancher2_global_role_binding` with `user_id`, keyed `"<username>/<role>"` so it never collides with the existing group-binding map. - `rancher2_global_role` — filename is the role name; `new_user_default`, `inherited_cluster_roles`, and `rules` (dynamic blocks: `api_groups`/`resources`/`verbs`/`non_resource_urls`/`resource_names`). - `rancher2_token` — `description`, `ttl`, `renew`, `cluster_id`. - `rancher2_setting` — filename is the setting name; seeds `config/settings/server-url.yaml` = `https://rancher.k8s.syd1.au.unkin.net`, matching the environment. - README documents each yaml schema. ## Tokens caveat Verified against the rancher2 14.1.1 provider schema (`tofu providers schema -json`): **`rancher2_token.user_id` is computed, not settable** — there is no user selector. Every token declared under `config/tokens/` is minted for the identity the provider authenticates as (the admin token Terraform runs with), not for any user in `config/users/`. A token for another user must be created by that user. Token/secret values also land in Terraform state. This is called out in a comment on the resource and in the README. ## Vault seeding `data.vault_kv_secret_v2` resolves at **plan** time, so a `config/users/*.yaml` entry whose Vault secret does not exist yet fails the plan. This PR therefore ships **zero** user and token yamls — only the empty directories (`.gitkeep`) and the one `server-url` setting — so plan needs no new KV seed. Entries land in follow-ups once their secrets are seeded. ## Validation `uvx pre-commit run --all-files` clean (tofu fmt, tofu validate, tflint, terragrunt hcl fmt, yamllint). `terragrunt render --json` confirms discovery: `settings = {server-url}`, `users`/`roles`/`tokens` empty, existing inputs unchanged.
unkin-agent added 1 commit 2026-08-30 00:48:57 +10:00
Manage Rancher users, global roles, tokens and settings
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
c7ef079c88
Extend the yaml-discovery config to four more rancher2 kinds so Rancher
objects beyond the OIDC auth provider stop being unmanaged clickops.

- Discover config/users, config/roles, config/tokens and config/settings
  in config/config.hcl and pass them through terragrunt.hcl
- Add rancher2_user (password from Vault kv-v2) plus per-user
  rancher2_global_role_binding on "<username>/<role>" keys
- Add rancher2_global_role with dynamic rules blocks
- Add rancher2_token, documenting that the provider has no user selector
- Add rancher2_setting and seed config/settings/server-url.yaml
- Document every yaml schema in the README
unkin-agent added 1 commit 2026-08-30 00:59:04 +10:00
Validate user and role names instead of munging binding names
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
187e29d068
Rancher requires global role binding names to be RFC 1123 labels. The
per-user binding name lowercased the key and replaced only "/", so
usernames containing ".", "_" or "@" still produced an invalid name and
failed at apply, and hyphenated names could collide (foo-bar + baz vs
foo + bar-baz) into one object.

Add validation blocks on var.users requiring the username and every
referenced role name to be RFC 1123 labels, so non-compliant input fails
the plan with an actionable message. The binding name is then built from
the two parts directly. A precondition rejects the remaining hyphen
ambiguity at plan time rather than as a mid-apply conflict. Document the
constraint in the users schema section of the README. The group binding
path is unchanged.
Author
Member

Addressed both review findings on the per-user global role binding name (187e29d):

  • MEDIUM (unconstrained usernames): added validation blocks on var.users requiring the username and every entry in global_role_bindings to match ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$. Non-compliant input (., _, @, uppercase) now fails the plan with a message telling the operator to rename config/users/<username>.yaml, instead of silently munging and failing Rancher's RFC 1123 validation at apply.
  • LOW (non-injective transform): the name is now built from the two parts directly, akuser-${each.value.username}-${each.value.global_role_id} — no replace()/lower(). Since hyphens remain legal inside both parts, a lifecycle.precondition on the resource rejects the residual ambiguity (foo-bar + baz vs foo + bar-baz) at plan time rather than as a duplicate-object conflict mid-apply.

README users schema section documents the constraint. The group binding path is untouched. pre-commit run --all-files green; terragrunt render --json still shows an empty users map and otherwise unchanged inputs.

Addressed both review findings on the per-user global role binding name (187e29d): - MEDIUM (unconstrained usernames): added `validation` blocks on `var.users` requiring the username *and* every entry in `global_role_bindings` to match `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`. Non-compliant input (`.`, `_`, `@`, uppercase) now fails the plan with a message telling the operator to rename `config/users/<username>.yaml`, instead of silently munging and failing Rancher's RFC 1123 validation at apply. - LOW (non-injective transform): the name is now built from the two parts directly, `akuser-${each.value.username}-${each.value.global_role_id}` — no `replace()`/`lower()`. Since hyphens remain legal inside both parts, a `lifecycle.precondition` on the resource rejects the residual ambiguity (`foo-bar` + `baz` vs `foo` + `bar-baz`) at plan time rather than as a duplicate-object conflict mid-apply. README users schema section documents the constraint. The group binding path is untouched. `pre-commit run --all-files` green; `terragrunt render --json` still shows an empty users map and otherwise unchanged inputs.
benvin merged commit 2d5fff80dd into main 2026-08-30 14:25:47 +10:00
benvin deleted branch benvin/users-roles-tokens-settings 2026-08-30 14:25:48 +10:00
Sign in to join this conversation.