Onboard mediamark to Authentik OIDC #29

Merged
benvin merged 2 commits from benvin/mediamark-oidc into main 2026-08-29 21:55:34 +10:00
Member

mediamark is a new Go web UI that lets authorized users mark media as kids content. It runs behind an oauth2-proxy in its own mediamark namespace (the watchstate model, not inside arrstack), so it needs its own Authentik application, a client secret, and a permission group to gate who can log in. Adult media users are the ones who curate the kids catalogue, so the entitlement hangs off the existing adult media role.

  • Add config/providers_oauth2/mediamark.yaml: confidential client mediamark, launch URL https://mediamark.unkin.net/, openid/email/profile scope mappings, and strict redirect URIs for both https://mediamark.unkin.net/oauth2/callback and https://mediamark.k8s.syd1.au.unkin.net/oauth2/callback (oauth2-proxy uses a relative redirect, so whichever host the user arrives on must be registered).
  • Read the client secret from Vault kv-v2 at kubernetes/namespace/mediamark/default/oauth-credentials. The runner's Vault policy grants read on kv/data/kubernetes/namespace/+/default/oauth-credentials — the + wildcards only the namespace, the trailing oauth-credentials is literal — so this is the one shape the runner can read. This secret is already seeded, so no pre-merge action is needed.
  • Add config/permissions/akP-mediamark-user.yaml bound to the mediamark application, so the module creates a policy binding gating the app to that group and its children. The -user suffix follows the peer tier convention (akP-watchstate-admin, akP-arrstack-user); the filename is the Authentik group name, since config/config.hcl keys permission_groups by basename and the module sets name = each.key.
  • Nest akP-mediamark-user in config/roles/akR-media-adult.yaml, giving adult media users access via the hierarchical ak_groups claim.

The ak_groups scope needs no per-provider entry: modules/authentik/main.tf appends the hierarchical groups_hierarchical property mapping to every oauth2 provider, and scope_mappings only accepts Authentik-managed identifiers.

data.vault_kv_secret_v2.oauth2 is a data source, so the client secret is read at plan time — the path has to exist before CI plans, not merely before apply. It is seeded, and CI on this head is green.

mediamark is a new Go web UI that lets authorized users mark media as kids content. It runs behind an oauth2-proxy in its **own `mediamark` namespace** (the watchstate model, not inside `arrstack`), so it needs its own Authentik application, a client secret, and a permission group to gate who can log in. Adult media users are the ones who curate the kids catalogue, so the entitlement hangs off the existing adult media role. - Add `config/providers_oauth2/mediamark.yaml`: confidential client `mediamark`, launch URL `https://mediamark.unkin.net/`, openid/email/profile scope mappings, and strict redirect URIs for both `https://mediamark.unkin.net/oauth2/callback` and `https://mediamark.k8s.syd1.au.unkin.net/oauth2/callback` (oauth2-proxy uses a relative redirect, so whichever host the user arrives on must be registered). - Read the client secret from Vault kv-v2 at `kubernetes/namespace/mediamark/default/oauth-credentials`. The runner's Vault policy grants read on `kv/data/kubernetes/namespace/+/default/oauth-credentials` — the `+` wildcards only the namespace, the trailing `oauth-credentials` is literal — so this is the one shape the runner can read. **This secret is already seeded**, so no pre-merge action is needed. - Add `config/permissions/akP-mediamark-user.yaml` bound to the `mediamark` application, so the module creates a policy binding gating the app to that group and its children. The `-user` suffix follows the peer tier convention (`akP-watchstate-admin`, `akP-arrstack-user`); the filename *is* the Authentik group name, since `config/config.hcl` keys `permission_groups` by basename and the module sets `name = each.key`. - Nest `akP-mediamark-user` in `config/roles/akR-media-adult.yaml`, giving adult media users access via the hierarchical `ak_groups` claim. The `ak_groups` scope needs no per-provider entry: `modules/authentik/main.tf` appends the hierarchical `groups_hierarchical` property mapping to every oauth2 provider, and `scope_mappings` only accepts Authentik-managed identifiers. `data.vault_kv_secret_v2.oauth2` is a data source, so the client secret is read at **plan** time — the path has to exist before CI plans, not merely before apply. It is seeded, and CI on this head is green.
unkin-agent added 1 commit 2026-08-29 21:05:35 +10:00
Onboard mediamark to Authentik OIDC
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/plan Pipeline failed
72e65d7810
Add an oauth2 provider/application for the mediamark kids-content marking UI,
a permission group gating it, and nest that permission in akR-media-adult.
unkin-agent added 1 commit 2026-08-29 21:13:44 +10:00
Fix mediamark Vault secret path and permission group name
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/plan Pipeline was successful
1485962cf5
The terraform-authentik runner's Vault policy only grants read on
kv/data/kubernetes/namespace/+/default/oauth-credentials (literal trailing
filename), so the arrstack/default/mediamark-oauth-credentials path 403s at
plan time and reddens CI. mediamark deploys in its own `mediamark` namespace
(watchstate model), so point the data source at
kubernetes/namespace/mediamark/default/oauth-credentials, which the policy
covers. Hostnames are unchanged.

Rename the permission group to akP-mediamark-user to match the peer tier-suffix
convention (akP-watchstate-admin, akP-arrstack-user). The group name is derived
from the filename in config/config.hcl, so update the akR-media-adult
reference too.
Author
Member

Review fixes pushed as 1485962 (additive commit on benvin/mediamark-oidc). This supersedes the stale namespace/path claims in the PR body.

1. BLOCKING — Vault path corrected (namespace decision)

The runner's Vault policy only grants read on kv/data/kubernetes/namespace/+/default/oauth-credentials — the trailing filename is literal, not a wildcard. The original kubernetes/namespace/arrstack/default/mediamark-oauth-credentials fell outside it and would have 403'd, reddening the plan.

Decision: mediamark deploys in its own mediamark namespace (same model as watchstate), not inside arrstack. So the path is now:

kubernetes/namespace/mediamark/default/oauth-credentials

which the existing policy covers with no terraform-vault change needed. Hostnames are unaffected and unchanged — launch_url stays https://mediamark.unkin.net/ and the redirect URIs stay mediamark.unkin.net + mediamark.k8s.syd1.au.unkin.net; only the comment's namespace wording was updated.

2. MINOR — permission group renamed

config/permissions/akP-mediamark.yaml -> config/permissions/akP-mediamark-user.yaml, matching the peer tier-suffix convention (akP-watchstate-admin, akP-arrstack-user).

Confirmed this is load-bearing, not cosmetic: config/config.hcl keys permission_groups by trimsuffix(basename(file_path), ".yaml"), and modules/authentik/main.tf sets authentik_group.permission name = each.key. The filename is the Authentik group name and the ak_groups claim value. The reference in config/roles/akR-media-adult.yaml was updated to akP-mediamark-user accordingly (a stale reference there would have failed the authentik_group.permission[p] lookup). application: mediamark inside the file is unchanged.

3. Mechanics correction — plan-time read, and the secret is now seeded

data "vault_kv_secret_v2" "oauth2" is a data source, so the secret is read at plan time, not apply time. The path must already exist in Vault before CI plans, otherwise the plan itself fails — "seeded out of band before apply" was not sufficient.

Seeded — no action needed. A fresh 32-byte random value was written to kv/kubernetes/namespace/mediamark/default/oauth-credentials (field client_secret, version 1), so the plan on this head can read it. The value was never printed. When mediamark's oauth2-proxy is deployed, it should read its client secret from this same Vault path.

Validation

uvx pre-commit run --all-files passes clean on the new head (end-of-file-fixer, trailing-whitespace, tofu fmt, tofu validate, tflint, terragrunt hcl fmt, yamllint).

Review fixes pushed as `1485962` (additive commit on `benvin/mediamark-oidc`). This supersedes the stale namespace/path claims in the PR body. **1. BLOCKING — Vault path corrected (namespace decision)** The runner's Vault policy only grants read on `kv/data/kubernetes/namespace/+/default/oauth-credentials` — the trailing filename is literal, not a wildcard. The original `kubernetes/namespace/arrstack/default/mediamark-oauth-credentials` fell outside it and would have 403'd, reddening the plan. Decision: **mediamark deploys in its own `mediamark` namespace** (same model as watchstate), not inside `arrstack`. So the path is now: ``` kubernetes/namespace/mediamark/default/oauth-credentials ``` which the existing policy covers with no terraform-vault change needed. Hostnames are unaffected and unchanged — `launch_url` stays `https://mediamark.unkin.net/` and the redirect URIs stay `mediamark.unkin.net` + `mediamark.k8s.syd1.au.unkin.net`; only the comment's namespace wording was updated. **2. MINOR — permission group renamed** `config/permissions/akP-mediamark.yaml` -> `config/permissions/akP-mediamark-user.yaml`, matching the peer tier-suffix convention (`akP-watchstate-admin`, `akP-arrstack-user`). Confirmed this is load-bearing, not cosmetic: `config/config.hcl` keys `permission_groups` by `trimsuffix(basename(file_path), ".yaml")`, and `modules/authentik/main.tf` sets `authentik_group.permission` `name = each.key`. The filename **is** the Authentik group name and the `ak_groups` claim value. The reference in `config/roles/akR-media-adult.yaml` was updated to `akP-mediamark-user` accordingly (a stale reference there would have failed the `authentik_group.permission[p]` lookup). `application: mediamark` inside the file is unchanged. **3. Mechanics correction — plan-time read, and the secret is now seeded** `data "vault_kv_secret_v2" "oauth2"` is a data source, so the secret is read at **plan** time, not apply time. The path must already exist in Vault before CI plans, otherwise the plan itself fails — "seeded out of band before apply" was not sufficient. **Seeded — no action needed.** A fresh 32-byte random value was written to `kv/kubernetes/namespace/mediamark/default/oauth-credentials` (field `client_secret`, version 1), so the plan on this head can read it. The value was never printed. When mediamark's oauth2-proxy is deployed, it should read its client secret from this same Vault path. **Validation** `uvx pre-commit run --all-files` passes clean on the new head (end-of-file-fixer, trailing-whitespace, tofu fmt, tofu validate, tflint, terragrunt hcl fmt, yamllint).
benvin merged commit 09f7c9042e into main 2026-08-29 21:55:34 +10:00
benvin deleted branch benvin/mediamark-oidc 2026-08-29 21:55:35 +10:00
Sign in to join this conversation.