fix(authentik): embedded outpost config in the blueprint, refresh every minute #1431

Closed
claude-bot wants to merge 2 commits from fix/gate-embedded-refresh-interval into main
Member

The embedded outpost's config is Authentik's own config and does not depend on authentik-gate-sync, so it moves out of the controller into the k8s-outposts blueprint.

  • Blueprint: a state: present entry for authentik Embedded Outpost with config only, no providers. An update of an existing object is partial (importer partial=True unless MUST_CREATED), so the gates the controller attached stay. config is replaced whole, so the entry mirrors the live row with one change, refresh_interval: minutes=1. A replica whose outpost websocket is down picks up gate changes only on that interval, because the reconnect backoff never resets (goauthentik/authentik#26463). kubernetes_namespace stays default: the outpost has the Local Kubernetes Cluster connection.
  • Controller: embedded_config/ensure_embedded_config, AK_BROWSER_URL and the AUTHENTIK_HOST_BROWSER env are removed. The controller only attaches providers.

Rollout: the blueprint can land before the new image. The old controller only rewrites the three host keys, which carry the same values, and merges the rest, so the two do not fight.

Tests: 33 pass.

🤖 Generated with Claude Code

The embedded outpost's config is Authentik's own config and does not depend on authentik-gate-sync, so it moves out of the controller into the k8s-outposts blueprint. - **Blueprint:** a `state: present` entry for `authentik Embedded Outpost` with `config` only, no `providers`. An update of an existing object is partial (importer `partial=True` unless MUST_CREATED), so the gates the controller attached stay. `config` is replaced whole, so the entry mirrors the live row with one change, `refresh_interval: minutes=1`. A replica whose outpost websocket is down picks up gate changes only on that interval, because the reconnect backoff never resets (goauthentik/authentik#26463). `kubernetes_namespace` stays `default`: the outpost has the Local Kubernetes Cluster connection. - **Controller:** `embedded_config`/`ensure_embedded_config`, `AK_BROWSER_URL` and the `AUTHENTIK_HOST_BROWSER` env are removed. The controller only attaches providers. Rollout: the blueprint can land before the new image. The old controller only rewrites the three host keys, which carry the same values, and merges the rest, so the two do not fight. Tests: 33 pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-10-23 08:19:56 +00:00
fix(authentik-gate-sync): refresh the embedded outpost every minute
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
95f3ff86b9
Each authentik-server replica's embedded outpost loses its websocket whenever gunicorn recycles the worker holding it (AUTHENTIK_WEB__MAX_REQUESTS). The reconnect backoff never resets (goauthentik/authentik#26463), so a replica then misses gate changes for up to 5 minutes and catches up only on the refresh interval. A 1-minute interval bounds that gap; worker recycling stays as is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review-bot left a comment
Member

🤖 Automated review — model claude-sonnet-5-5, commit 95f3ff86b9124ed8a7d31c4546586d0e87e8e1fd.

Automated review by the review-bot: it approves only when it found nothing red or yellow, and a human still merges. Push new commits to get a fresh review.


Summary

This PR adds "refresh_interval": "minutes=1" to the config that embedded_config() writes to the embedded outpost. The goal is to cap how long a replica with a dead outpost websocket misses gate changes. The change is small and fits the existing ensure_embedded_config flow. Whether the setting does what the PR claims depends on Authentik and outpost behaviour that I could not check from this repo.

⚪ Unverified

  • Key name and effect: I could not confirm that refresh_interval is an outpost config key the embedded outpost reads, or that it governs the polling that covers a missing websocket. The repo has no other reference to it. The PR's evidence is log observation plus upstream issue goauthentik/authentik#26463, which I can't read. If the embedded outpost ignores the key, the PR does nothing, and ensure_embedded_config still reports a successful update. Please verify on the live instance. Check that gate changes show up within about a minute on a replica with a dropped websocket, or find the key in the deployed Authentik version's source or docs.
  • Value format: I did not check that "minutes=1" is the format the deployed Authentik version parses for this field. Existing "hours=24" and "days=30" values in provider_body use the same style, but that is not proof.
  • Load claim: The PR says the cost is "one provider read per replica per minute". I could not check this.

🟡 Suggestions

  • ci/images/authentik-gate-sync/controller.py:57: REFRESH_INTERVAL is hardcoded, while the neighbouring tunables (INTERVAL, AK_BROWSER_URL) read from environment variables. Making it an env var is optional.
  • ci/images/authentik-gate-sync/controller.py:221-222: The controller now overwrites any refresh_interval set in Authentik's UI or a blueprint. The test deliberately covers this (it passes "minutes=5" and expects "minutes=1"). Say in the docstring that the key is controller-owned, as with authentik_host.

Tests

The updated test_embedded_config_keeps_other_keys covers the pure function: the key is set, an existing value is overridden, and other keys are kept. It cannot show that Authentik honours the setting. I read only the diff of test_controller.py, so I did not check whether the fake Authentik's outpost PATCH handling needs the new key. The PR states 35 tests pass.

Recommendation

Looks reasonable — no blocking issues; comments above are optional. The only open point is confirming on the live instance that the embedded outpost honours the key.

🤖 **Automated review** — model `claude-sonnet-5-5`, commit `95f3ff86b9124ed8a7d31c4546586d0e87e8e1fd`. > Automated review by the `review-bot`: it approves only when it found nothing red or yellow, and a human still merges. Push new commits to get a fresh review. --- ## Summary This PR adds `"refresh_interval": "minutes=1"` to the config that `embedded_config()` writes to the embedded outpost. The goal is to cap how long a replica with a dead outpost websocket misses gate changes. The change is small and fits the existing `ensure_embedded_config` flow. Whether the setting does what the PR claims depends on Authentik and outpost behaviour that I could not check from this repo. ## ⚪ Unverified - **Key name and effect:** I could not confirm that `refresh_interval` is an outpost config key the embedded outpost reads, or that it governs the polling that covers a missing websocket. The repo has no other reference to it. The PR's evidence is log observation plus upstream issue goauthentik/authentik#26463, which I can't read. If the embedded outpost ignores the key, the PR does nothing, and `ensure_embedded_config` still reports a successful update. Please verify on the live instance. Check that gate changes show up within about a minute on a replica with a dropped websocket, or find the key in the deployed Authentik version's source or docs. - **Value format:** I did not check that `"minutes=1"` is the format the deployed Authentik version parses for this field. Existing `"hours=24"` and `"days=30"` values in `provider_body` use the same style, but that is not proof. - **Load claim:** The PR says the cost is "one provider read per replica per minute". I could not check this. ## 🟡 Suggestions - `ci/images/authentik-gate-sync/controller.py:57`: `REFRESH_INTERVAL` is hardcoded, while the neighbouring tunables (`INTERVAL`, `AK_BROWSER_URL`) read from environment variables. Making it an env var is optional. - `ci/images/authentik-gate-sync/controller.py:221-222`: The controller now overwrites any `refresh_interval` set in Authentik's UI or a blueprint. The test deliberately covers this (it passes `"minutes=5"` and expects `"minutes=1"`). Say in the docstring that the key is controller-owned, as with `authentik_host`. ## Tests The updated `test_embedded_config_keeps_other_keys` covers the pure function: the key is set, an existing value is overridden, and other keys are kept. It cannot show that Authentik honours the setting. I read only the diff of `test_controller.py`, so I did not check whether the fake Authentik's outpost PATCH handling needs the new key. The PR states 35 tests pass. ## Recommendation Looks reasonable — no blocking issues; comments above are optional. The only open point is confirming on the live instance that the embedded outpost honours the key. <!-- pr-review:sha=95f3ff86b9124ed8a7d31c4546586d0e87e8e1fd -->
claude-bot added 1 commit 2026-10-23 17:00:27 +00:00
fix(authentik): embedded outpost config lives in the blueprint
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
58196acebc
The embedded outpost's config is Authentik's own and does not depend on
authentik-gate-sync. A blueprint update of an existing outpost is partial
(importer: partial=True unless MUST_CREATED), so the entry sets config
without providers and the gates the controller attached stay.

config is replaced whole; the entry mirrors the live row
(authentik_outposts_outpost, 2026-10-23) with refresh_interval
minutes=1. kubernetes_namespace stays default: the outpost has the
Local Kubernetes Cluster connection, and the authentik namespace would
let the worker create an ak-outpost Service for it.

The controller drops embedded_config/ensure_embedded_config and only
attaches providers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-bot changed title from fix(authentik-gate-sync): refresh the embedded outpost every minute to fix(authentik): embedded outpost config in the blueprint, refresh every minute 2026-10-23 17:00:32 +00:00
review-bot left a comment
Member

🤖 Automated review — model claude-opus-5-5, commit 58196acebc0c319f36efc7e20485f0635fb0c1b1.

Automated review by the review-bot: it approves only when it found nothing red or yellow, and a human still merges. Push new commits to get a fresh review.


Summary

Re-review against my last review at 95f3ff86. The one new commit, 58196ace, takes out the controller change I reviewed (embedded_config/ensure_embedded_config, AK_BROWSER_URL, the AUTHENTIK_HOST_BROWSER env) and puts the embedded outpost's config into the k8s-outposts blueprint instead, with refresh_interval: "minutes=1". My earlier review had no 🔴. Both earlier 🟡 points are moot because the code they were about is gone: the hardcoded REFRESH_INTERVAL and the docstring about the controller owning the key.

The PR is mergeable. Its base is the current main tip (1bd31119). The blueprint was last touched on main by 772311c, and controller.py by 69988458 (#1430). Neither conflicts with this PR or supersedes it. The PR body links no issues; goauthentik/authentik#26463 is an upstream issue I can't read. The open question is whether Authentik accepts an outpost entry with no providers/type. This repo's own comments say it doesn't.

⚪ Unverified

  • Will the importer accept an outpost entry without providers and type? (kubernetes/apps/authentik/blueprints-configmap.yaml:380-409)
    • The PR says an update of an existing object is partial (partial=True unless MUST_CREATED). I can't check Authentik's importer from this repo, and the repo says the opposite in two places:
      • The comment on the LDAP Outpost entry: "type and providers are required fields per the current outpost serializer" (blueprints-configmap.yaml:711-713).
      • Commit 772311c from earlier today: "an entry must carry providers".
    • If those comments are right, this entry fails validation. If Authentik validates the whole blueprint before applying it (I didn't verify that either), the entire k8s-outposts blueprint would stop applying. That includes the LDAPS CertificateKeyPair rotation from !Env (:429-436) and the LDAP/Radius outpost entries.
    • After rollout, check the k8s-outposts blueprint instance status in Authentik (successful, not error), and confirm the gates are still listed in the embedded outpost's providers.
  • Does refresh_interval affect the embedded outpost's polling? This is still open from last time.
    • The value format is now better supported: the LDAP and Radius outposts in the same blueprint use refresh_interval: "minutes=5" (:730, :764).
    • I still can't confirm that the embedded outpost reads the key or that it bounds the dead-websocket gap.
  • The listed config is the live row. config is replaced whole, so any live key missing from the list is dropped. I can't see the live row.
    • The Local Radius entry carries kubernetes_ingress_path_type, kubernetes_httproute_annotations and kubernetes_httproute_parent_refs (:775-777). The new entry doesn't (:396-409).
    • The commit says the list mirrors authentik_outposts_outpost as of 2026-10-23, so this is probably fine.

🟡 Suggestions

  • blueprints-configmap.yaml:381-382: Match the outpost on managed: goauthentik.io/outposts/embedded instead of name: authentik Embedded Outpost.
    • The controller deliberately matches on managed, on the client side too, so a renamed or second outpost can't be picked up (controller.py, embedded_outpost(), ending at line 340).
    • With state: present and a name that doesn't match, the importer would try to create an outpost from this entry, not update the real one.

Tests

  • Deleting test_embedded_config_keeps_other_keys and test_embedded_config matches the removed code. The controller has no remaining references to AK_BROWSER_URL or ensure_embedded_config.
  • Nothing in the repo tests the blueprint, so the partial-update behaviour can only be checked live (see Unverified).
  • The rollout claim holds by reading the code. The old controller (still on main) merges {**config, authentik_host, authentik_host_insecure: False, authentik_host_browser}, and the blueprint uses the same values. The controller keeps refresh_interval, so the two don't fight.

Recommendation

Looks reasonable, no blocking issues. Before relying on this, check that the k8s-outposts blueprint still applies with the provider-less outpost entry. Matching on managed is optional.

🤖 **Automated review** — model `claude-opus-5-5`, commit `58196acebc0c319f36efc7e20485f0635fb0c1b1`. > Automated review by the `review-bot`: it approves only when it found nothing red or yellow, and a human still merges. Push new commits to get a fresh review. --- ## Summary Re-review against my last review at `95f3ff86`. The one new commit, `58196ace`, takes out the controller change I reviewed (`embedded_config`/`ensure_embedded_config`, `AK_BROWSER_URL`, the `AUTHENTIK_HOST_BROWSER` env) and puts the embedded outpost's config into the `k8s-outposts` blueprint instead, with `refresh_interval: "minutes=1"`. My earlier review had no 🔴. Both earlier 🟡 points are moot because the code they were about is gone: the hardcoded `REFRESH_INTERVAL` and the docstring about the controller owning the key. The PR is `mergeable`. Its base is the current `main` tip (`1bd31119`). The blueprint was last touched on `main` by `772311c`, and `controller.py` by `69988458` (#1430). Neither conflicts with this PR or supersedes it. The PR body links no issues; goauthentik/authentik#26463 is an upstream issue I can't read. The open question is whether Authentik accepts an outpost entry with no `providers`/`type`. This repo's own comments say it doesn't. ## ⚪ Unverified - **Will the importer accept an outpost entry without `providers` and `type`?** (`kubernetes/apps/authentik/blueprints-configmap.yaml:380-409`) - The PR says an update of an existing object is partial (`partial=True` unless MUST_CREATED). I can't check Authentik's importer from this repo, and the repo says the opposite in two places: - The comment on the LDAP Outpost entry: "`type` and `providers` are required fields per the current outpost serializer" (`blueprints-configmap.yaml:711-713`). - Commit `772311c` from earlier today: "an entry must carry providers". - If those comments are right, this entry fails validation. If Authentik validates the whole blueprint before applying it (I didn't verify that either), the entire `k8s-outposts` blueprint would stop applying. That includes the LDAPS `CertificateKeyPair` rotation from `!Env` (`:429-436`) and the LDAP/Radius outpost entries. - After rollout, check the `k8s-outposts` blueprint instance status in Authentik (successful, not error), and confirm the gates are still listed in the embedded outpost's `providers`. - **Does `refresh_interval` affect the embedded outpost's polling?** This is still open from last time. - The value format is now better supported: the LDAP and Radius outposts in the same blueprint use `refresh_interval: "minutes=5"` (`:730`, `:764`). - I still can't confirm that the embedded outpost reads the key or that it bounds the dead-websocket gap. - **The listed `config` is the live row.** `config` is replaced whole, so any live key missing from the list is dropped. I can't see the live row. - The Local Radius entry carries `kubernetes_ingress_path_type`, `kubernetes_httproute_annotations` and `kubernetes_httproute_parent_refs` (`:775-777`). The new entry doesn't (`:396-409`). - The commit says the list mirrors `authentik_outposts_outpost` as of 2026-10-23, so this is probably fine. ## 🟡 Suggestions - `blueprints-configmap.yaml:381-382`: Match the outpost on `managed: goauthentik.io/outposts/embedded` instead of `name: authentik Embedded Outpost`. - The controller deliberately matches on `managed`, on the client side too, so a renamed or second outpost can't be picked up (`controller.py`, `embedded_outpost()`, ending at line 340). - With `state: present` and a name that doesn't match, the importer would try to *create* an outpost from this entry, not update the real one. ## Tests - Deleting `test_embedded_config_keeps_other_keys` and `test_embedded_config` matches the removed code. The controller has no remaining references to `AK_BROWSER_URL` or `ensure_embedded_config`. - Nothing in the repo tests the blueprint, so the partial-update behaviour can only be checked live (see Unverified). - The rollout claim holds by reading the code. The old controller (still on `main`) merges `{**config, authentik_host, authentik_host_insecure: False, authentik_host_browser}`, and the blueprint uses the same values. The controller keeps `refresh_interval`, so the two don't fight. ## Recommendation Looks reasonable, no blocking issues. Before relying on this, check that the `k8s-outposts` blueprint still applies with the provider-less outpost entry. Matching on `managed` is optional. <!-- pr-review:sha=58196acebc0c319f36efc7e20485f0635fb0c1b1 -->
k.renner closed this pull request 2026-10-26 06:39:33 +00:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.