No description
  • Go 84.4%
  • Python 10.5%
  • Makefile 3.8%
  • Go Template 0.7%
  • Dockerfile 0.4%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Morten Olsen 736d799928
All checks were successful
ci/woodpecker/push/quality Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
test: simplify data volume lifecycle assertions
2026-10-30 21:04:24 +01:00
.claude init 2025-12-22 22:41:01 +01:00
.devcontainer init 2025-12-22 22:41:01 +01:00
.github/workflows fix(ci): confirm publication visibility and pin Kustomize 2026-10-29 21:32:04 +00:00
.woodpecker feat: reconcile GitOps groups and fail-closed application access 2026-10-29 18:59:44 +00:00
api/v1alpha1 feat: support persistent Authentik data volumes 2026-10-30 20:56:59 +01:00
charts/authentik-operator feat: support persistent Authentik data volumes 2026-10-30 20:56:59 +01:00
cmd feat: add AuthentikProxyProvider for forward auth on the embedded outpost 2026-10-30 00:21:15 +01:00
config feat: support persistent Authentik data volumes 2026-10-30 20:56:59 +01:00
docs fix: never adopt proxy providers by name and reject duplicate hosts 2026-10-30 00:30:19 +01:00
hack init 2025-12-22 22:41:01 +01:00
internal test: simplify data volume lifecycle assertions 2026-10-30 21:04:24 +01:00
scripts fix(ci): confirm publication visibility and pin Kustomize 2026-10-29 21:32:04 +00:00
test test: use admitted provider status field in recovery fixture 2026-10-29 19:57:01 +00:00
.dockerignore init 2025-12-22 22:41:01 +01:00
.gitignore fix(ci): require Forgejo-owned immutable release tags 2026-10-29 18:21:17 +00:00
.golangci.yml init 2025-12-22 22:41:01 +01:00
Dockerfile fix: build with Go 1.25 2026-09-22 10:27:21 +02:00
GLOSSARY.md feat: reconcile GitOps groups and fail-closed application access 2026-10-29 18:59:44 +00:00
go.mod feat: configure oauth provider grant types and signing key 2026-09-22 09:08:22 +02:00
go.sum fix: build with Go 1.25 2026-09-22 10:27:21 +02:00
Makefile test: reuse the initialized defaulted Authentik server 2026-10-24 19:48:13 +00:00
mise.toml fix(ci): pin Python for release safety checks 2026-10-29 19:03:14 +00:00
PROJECT feat: add AuthentikProxyProvider for forward auth on the embedded outpost 2026-10-30 00:21:15 +01:00
README.md feat: support persistent Authentik data volumes 2026-10-30 20:56:59 +01:00
renovate.json fix: retire obsolete Authentik Redis in operator 0.1.4 (#3) 2026-10-26 23:42:39 +01:00
Taskfile.yml refactor: isolate guarded access transitions and enforce lint 2026-10-29 19:13:49 +00:00
TODO.md use https for external url instead of http 2026-01-01 21:02:41 +01:00

operator-authentik

// TODO(user): Add simple overview of use/purpose

Description

// TODO(user): An in-depth paragraph about your project and overview of use

Authentik server image default

New AuthentikServer resources that omit spec.image use ghcr.io/goauthentik/server:2026.8.5 for both server and worker. This is the current patch in the supported 2026.8 release series as of 2026-10-24; see the upstream release notes. The registry does not publish a latest tag.

Keep the default on a fixed supported release. Update it through a reviewed change after checking upstream release/support notes and registry availability; regenerate config/crd/bases, copy the server CRD into the Helm chart, and run make test, make test-e2e and chart validation. The fallback, API marker, packaged CRDs and E2E expectations must agree. Repository Renovate runs weekly on Sunday at 03:15 UTC and tracks the operator's image defaults; core's Renovate updates explicit workload image pins separately. An explicit spec.image is preserved, including an older version or latest; changing the default does not rewrite existing pins. Operators should pin existing stateful installations explicitly and approve upgrades separately. The homelab is pinned to 2026.8.5; removing its obsolete Redis workload does not migrate the database or change its credentials.

Supported Authentik versions and dependencies

Use Authentik 2025.10 or newer; the deployed supported release series is 2026.8.x. Authentik removed Redis in 2025.10 and now uses PostgreSQL for caching, tasks and sessions. The operator creates no Redis workload or Redis environment settings. On reconciliation it deletes an obsolete <server-name>-redis Deployment and Service only when controlled by that AuthentikServer UID; unowned resources and resources controlled by another server are left untouched. PostgreSQL configuration and bootstrap credentials are preserved.

Server startup health checks

The server's HTTP startup probe checks /-/health/live/ on port 9000 every ten seconds, with a five-second request timeout and 60 failures allowed. Kubernetes holds liveness and readiness probes until startup succeeds, giving the server up to ten minutes to start while PostgreSQL and the migration worker recover after a reboot. After startup, the existing live and ready endpoints continue to check the running server. Readiness still controls whether it receives traffic.

This startup budget prevents premature liveness restarts; database contention must also be fixed in the postgres operator. It does not make a permanent lock deadlock safe.

Persistent local files

Set spec.dataVolumeClaimName to the name of an existing PersistentVolumeClaim in the AuthentikServer namespace to enable persistent local files, including background image uploads. Both server and worker mount that same claim read-write as volume data at /data; their existing UID, GID and filesystem group are 1000. The claim name must be nonempty when supplied. Omitting the field leaves both workloads without a data volume.

spec:
  host: auth.example.com
  dataVolumeClaimName: authentik-data

Create and protect the claim separately before configuring it. The operator does not create, adopt, resize or delete PVCs, including when this field is removed or the AuthentikServer is deleted. Back up the stored files independently of the PostgreSQL database. The claim must support simultaneous access by server and worker: a ReadWriteOnce local-path claim works when both run on the same node; multi-node deployments need storage and access modes that support their placement. An unavailable or unschedulable claim prevents the affected pods from starting.

Getting Started

Release version and approval

Main and branch pushes validate without publishing. An approved version tag pushed to Forgejo is mirrored outward and triggers image, release-asset and Helm publication. Publishers stamp the chart version and appVersion from that tag; they refuse reused versions. Follow Operator releases for the read-only preflight and approval of the exact version, commit and effects. Publication alone does not update the homelab: once the artifacts exist, change core's pinned operator version and verify the live objects and original symptom.

API-client connection lifetime

Controllers construct API clients on each reconciliation. Clients keep their own URL and authorization headers while sharing the HTTP connection pool for their TLS mode. The explicit skip-verification mode uses a process-wide pool with at most ten idle connections in total, two per host and a 30-second idle timeout. Verifying clients continue using the standard HTTP client. Creating a fresh transport per reconciliation leaks idle connections and their goroutines, including when the URL uses internal HTTP; garbage collection alone does not close them. HOMEIT-490 reproduced 100 retained connections after 100 clients.

Run the connection-reuse, credential-isolation and TLS-isolation regressions with:

go test ./internal/authentik -run 'TestFreshClientsReuseConnections|TestClientTLSModesRemainIsolated'

After deployment, measure memory across startup with the full client/group/proxy backlog and repeated reconciliation. The homelab's 256Mi operator limit is an OOM mitigation, not proof of a bounded live working set or final resource sizing.

Prerequisites

  • go version v1.24.6+
  • docker version 17.03+.
  • kubectl version v1.11.3+.
  • Access to a Kubernetes v1.11.3+ cluster.

To Deploy on the cluster

Build and push your image to the location specified by IMG:

make docker-build docker-push IMG=<some-registry>/operator-authentik:tag

NOTE: This image ought to be published in the personal registry you specified. And it is required to have access to pull the image from the working environment. Make sure you have the proper permission to the registry if the above commands don’t work.

Install the CRDs into the cluster:

make install

Deploy the Manager to the cluster with the image specified by IMG:

make deploy IMG=<some-registry>/operator-authentik:tag

NOTE: If you encounter RBAC errors, you may need to grant yourself cluster-admin privileges or be logged in as admin.

Create instances of your solution You can apply the samples (examples) from the config/sample:

kubectl apply -k config/samples/

NOTE: Ensure that the samples has default values to test it out.

To Uninstall

Delete the instances (CRs) from the cluster:

kubectl delete -k config/samples/

Delete the APIs(CRDs) from the cluster:

make uninstall

UnDeploy the controller from the cluster:

make undeploy

Project Distribution

Following the options to release and provide this solution to the users.

By providing a bundle with all YAML files

  1. Build the installer for the image built and published in the registry:
make build-installer IMG=<some-registry>/operator-authentik:tag

NOTE: The makefile target mentioned above generates an 'install.yaml' file in the dist directory. This file contains all the resources built with Kustomize, which are necessary to install this project without its dependencies.

  1. Using the installer

Users can just run 'kubectl apply -f ' to install the project, i.e.:

kubectl apply -f https://raw.githubusercontent.com/<org>/operator-authentik/<tag or branch>/dist/install.yaml

By providing a Helm Chart

  1. Build the chart using the optional helm plugin
kubebuilder edit --plugins=helm/v2-alpha
  1. See that a chart was generated under 'dist/chart', and users can obtain this solution from there.

NOTE: If you change the project, you need to update the Helm Chart using the same command above to sync the latest changes. Furthermore, if you create webhooks, you need to use the above command with the '--force' flag and manually ensure that any custom configuration previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml' is manually re-applied afterwards.

Contributing

// TODO(user): Add detailed information on how you would like others to contribute to this project

NOTE: Run make help for more information on all potential make targets

More information can be found via the Kubebuilder Documentation

License

Copyright 2025.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

End-to-end test fixtures

make test-e2e runs the operator in an isolated Kind cluster. The server fixture omits spec.image, checks admission and both deployment images are 2026.8.5, then waits for both deployments and status.ready=true. The client test reuses that initialized server and its bootstrap secret. A second server against the same test database would generate credentials that differ from its first-boot initialization. Explicit image preservation is tested separately in the controller regression tests.

The suite has a thirty-minute Go timeout so image builds, startup waits and failure diagnostics fit inside the overall deadline. Individual readiness waits remain bounded. Fixtures also check the public HTTPS URL and startup probe, and delete clients and servers before undeploying the controller so their finalizers can finish. Namespace cleanup has a bounded wait. Runtime credentials and data are not used by these isolated tests.

GitOps groups and access

Operator 0.2.0 adds AuthentikGroup and AuthentikClient.allowedGroups. See ownership and cutover rules before upgrading. User provisioning is outside this feature.

apiVersion: authentik.homelab.mortenolsen.pro/v1alpha1
kind: AuthentikGroup
metadata:
  name: app-access
  namespace: prod
spec:
  serverRef: {name: authentik, namespace: shared}
  members:
    - id: 123
      uuid: 11111111-2222-4333-8444-555555555555

Reference it from a client with allowedGroups: [{name: app-access, namespace: prod}]. An empty list denies access. Once a client has opted in, removing the list also denies access. The group owns its complete direct membership; UI edits are drift. Remote renames are repaired using the stable group ID and ownership UID, including recovery when status is lost. Ambiguous ownership blocks writes. Deletion revokes and reads back managed fields before releasing its finalizer, retaining the remote identity. Backing groups never grant Authentik superuser or role privileges, and the operator has no special admin behavior. Inspect user IDs/UUIDs through the authenticated Authentik API without logging credentials; never identify members by display name.

Local and Woodpecker PR validation uses mise install && task ci:quality (build, make test including envtest, make lint, generated CRD/RBAC drift and Helm rendering). The service-image delivery shape and existing GitHub mirror release workflow are preserved: merging main validates without publishing. An explicitly approved Forgejo version tag publishes the reviewed chart/image version. The new quality workflow uses the existing ci/mise image and no secrets; it does not publish or change release mechanics.

Release ownership

See Operator releases for approved Forgejo tags, publication checks, version immutability and recovery. Main merges validate without publishing.

The PR runner obtains Python 3.13.16 through mise for the release-safety regression suite; it does not assume python3 is installed in the ci/mise base image.

The isolated Kind fixture provisions an empty managed group before its client and revokes that group before deleting the server. The PR quality task compiles this fixture; full Kind execution uses the existing mirrored E2E Tests workflow. Docker and Kind are not present in the Mission Control task container, so a local compile is not reported as a full isolated runtime acceptance run.

Forward-auth proxy providers

Operator 0.3.0 adds AuthentikProxyProvider for applications that cannot speak OIDC and are protected by a reverse proxy using Authentik forward auth (for example Istio ext_authz). See the design and ownership rules.

apiVersion: authentik.homelab.mortenolsen.pro/v1alpha1
kind: AuthentikProxyProvider
metadata:
  name: whoami
  namespace: prod
spec:
  serverRef: {name: authentik, namespace: shared}
  name: whoami
  externalHost: https://whoami.example.com
  accessTokenValidity: hours=8
  allowedGroups: [{name: admin, namespace: shared}]

The operator creates the forward_single provider and its application with the same guarded access as AuthentikClient, then serves it from the embedded outpost, whose authentik_host it sets to https://<server host>. Omitting allowedGroups denies everyone. The reverse proxy must also route /outpost.goauthentik.io/ on the protected host to the Authentik server for the login callback.