diff --git a/ci/images/authentik-gate-sync/controller.py b/ci/images/authentik-gate-sync/controller.py index feddb0b7..f81e50a0 100644 --- a/ci/images/authentik-gate-sync/controller.py +++ b/ci/images/authentik-gate-sync/controller.py @@ -45,7 +45,6 @@ INGRESS_NS = os.environ.get("INGRESS_NAMESPACE", "ingress") DEFAULT_AUTHZ_FLOW = os.environ.get("DEFAULT_AUTHORIZATION_FLOW", "default-provider-authorization-implicit-consent") INVALIDATION_FLOW = os.environ.get("INVALIDATION_FLOW", "default-provider-invalidation-flow") COOKIE_DOMAIN = os.environ.get("COOKIE_DOMAIN", "renner.uno") -AK_BROWSER_URL = os.environ.get("AUTHENTIK_HOST_BROWSER", "https://auth.renner.uno/") INTERVAL = int(os.environ.get("INTERVAL_SECONDS", "60")) HTTP_TIMEOUT = 20 @@ -209,14 +208,6 @@ def resolve_backend(gate, service): return sp["port"], selector, target -def embedded_config(config): - """The embedded outpost's config with the host the gates depend on. The - embedded outpost builds the sign-in redirect from authentik_host, so it is - the public URL, which is also Authentik's default for it.""" - return {**(config or {}), "authentik_host": AK_BROWSER_URL, "authentik_host_insecure": False, - "authentik_host_browser": AK_BROWSER_URL} - - def provider_body(gate, flows): return { "name": gate["name"], @@ -349,14 +340,6 @@ def embedded_outpost(ak): return found[0] -def ensure_embedded_config(ak): - outpost = embedded_outpost(ak) - want = embedded_config(outpost.get("config")) - if outpost.get("config") != want: - ak.req("PATCH", f"outposts/instances/{outpost['pk']}/", {"config": want}) - LOG.info("embedded outpost: updated config") - - def ensure_gate(ak, gate, flows): name = gate["name"] if gate["group"] and ak.one("core/groups/", name=gate["group"]) is None: @@ -485,7 +468,6 @@ def reconcile(): sources = [i for i in k8s.get("/apis/networking.k8s.io/v1/ingresses").get("items", []) if is_gated(i)] shared = shared_hosts(sources) flows = {f["slug"]: f["pk"] for f in ak.list("flows/instances/")} - ensure_embedded_config(ak) desired = set() # (kind, ns, name) of every Kubernetes object some gate wants gates = set() # gate names whose Authentik side should exist diff --git a/ci/images/authentik-gate-sync/test_controller.py b/ci/images/authentik-gate-sync/test_controller.py index 038db4f6..3c70a520 100644 --- a/ci/images/authentik-gate-sync/test_controller.py +++ b/ci/images/authentik-gate-sync/test_controller.py @@ -166,11 +166,6 @@ class Objects(unittest.TestCase): def test_drift(self): self.assertEqual(c.drift({"a": 1, "b": 2}, {"a": 1, "b": 3}), {"b": 3}) - def test_embedded_config_keeps_other_keys(self): - cfg = c.embedded_config({"log_level": "debug", "authentik_host": c.AK_URL}) - self.assertEqual(cfg, {"log_level": "debug", "authentik_host": c.AK_BROWSER_URL, - "authentik_host_insecure": False, "authentik_host_browser": c.AK_BROWSER_URL}) - class FakeAuthentik: """In-memory stand-in for the endpoints the controller uses; records calls.""" @@ -299,15 +294,6 @@ class Reconcile(unittest.TestCase): c.embedded_outpost(self.ak) self.assertEqual(self.ak.get(f"outposts/instances/{ldap['pk']}/")["providers"], [7]) - def test_embedded_config(self): - c.ensure_embedded_config(self.ak) - cfg = self.ak.get(f"outposts/instances/{self.embedded['pk']}/")["config"] - self.assertEqual((cfg["log_level"], cfg["authentik_host"], cfg["authentik_host_browser"]), - ("info", c.AK_BROWSER_URL, c.AK_BROWSER_URL)) - before = len(self.ak.calls) - c.ensure_embedded_config(self.ak) - self.assertEqual(self.ak.calls[before:], []) - def test_provider_moves_from_proxy_mode(self): self.ak.add("providers/proxy/", {"name": self.g["name"], "mode": "proxy", "internal_host": "http://llama-server.ai.svc.cluster.local:8080", diff --git a/kubernetes/apps/authentik/blueprints-configmap.yaml b/kubernetes/apps/authentik/blueprints-configmap.yaml index 54b3a8a0..0ec6ba93 100644 --- a/kubernetes/apps/authentik/blueprints-configmap.yaml +++ b/kubernetes/apps/authentik/blueprints-configmap.yaml @@ -374,10 +374,39 @@ data: name: lidarr-gate state: absent - # The embedded outpost is not declared here: authentik-gate-sync owns its - # providers list (every gate) and the hosts in its config, and a - # blueprint entry must carry `providers`, which would detach the gates - # on every apply. + # No `providers`: authentik-gate-sync attaches the gates, and an update + # of an existing outpost is partial. `config` is replaced whole, so it + # carries every key, kubernetes_namespace included. + - model: authentik_outposts.outpost + identifiers: + name: authentik Embedded Outpost + state: present + attrs: + config: + log_level: info + # The embedded outpost builds the sign-in redirect from + # authentik_host, so it is the public URL. + authentik_host: "https://auth.renner.uno/" + authentik_host_insecure: false + authentik_host_browser: "https://auth.renner.uno/" + # A replica whose outpost websocket is down picks up gate changes + # only on this interval; the reconnect backoff never resets + # (goauthentik/authentik#26463). + refresh_interval: "minutes=1" + object_naming_template: "ak-outpost-%(name)s" + container_image: null + docker_labels: null + docker_network: ladmin_default + docker_map_ports: true + kubernetes_namespace: default + kubernetes_replicas: 1 + kubernetes_service_type: ClusterIP + kubernetes_disabled_components: [] + kubernetes_image_pull_secrets: [] + kubernetes_ingress_annotations: {} + kubernetes_ingress_class_name: null + kubernetes_ingress_secret_name: "authentik-outpost-tls" + kubernetes_json_patches: null # Shared LDAPS cert for the LDAP Outpost. Authentik's data model # owns the cert per-LDAPProvider (no outpost-level cert field), so diff --git a/kubernetes/apps/authentik/gate-sync.yaml b/kubernetes/apps/authentik/gate-sync.yaml index ab6dc4e6..731466a6 100644 --- a/kubernetes/apps/authentik/gate-sync.yaml +++ b/kubernetes/apps/authentik/gate-sync.yaml @@ -114,8 +114,6 @@ spec: env: - name: AK_URL value: http://authentik-server.authentik.svc.cluster.local - - name: AUTHENTIK_HOST_BROWSER - value: https://auth.renner.uno/ - name: DEFAULT_AUTHORIZATION_FLOW value: default-provider-authorization-implicit-consent - name: INTERVAL_SECONDS