diff --git a/containers/authentik/authentik-django.nix b/containers/authentik/authentik-django.nix index 58bb5bf..eb07abd 100644 --- a/containers/authentik/authentik-django.nix +++ b/containers/authentik/authentik-django.nix @@ -9,13 +9,20 @@ # # autoPatchelfHook restores RPATHs that were stripped in the FOD. # +# Optional input: webui derivation. When provided, resolves @webui@ store +# path placeholders in Python source. When null (default), leaves placeholders +# for isolated testing. +# # Output: # $out/bin/python3.14 venv python (symlink to nix python314) # $out/lib/python3.14/site-packages/ all Python packages # $out/lifecycle/ lifecycle scripts (symlink) # $out/blueprints/ YAML blueprints # $out/manage.py Django management script -{ pkgs ? import { }, sources ? import ./sources.nix { inherit pkgs; } }: +{ pkgs ? import { } +, sources ? import ./sources.nix { inherit pkgs; } +, webui ? null +}: let python-deps = import ./python-deps.nix { inherit pkgs sources; }; @@ -28,6 +35,9 @@ let hash = "sha256-Q6SJed0K6eIrqQ9mNAD4RGx+YCJvnI5E+0KGp5fBtTU="; }; + # When webui is provided, resolve paths directly; otherwise use placeholder + webuiPath = if webui != null then "${webui}" else "@webui@"; + sp = "$out/lib/python3.14/site-packages"; in @@ -122,9 +132,9 @@ pkgs.stdenv.mkDerivation { # Web asset paths: placeholder @webui@ for Go server card to resolve substituteInPlace ${sp}/authentik/stages/email/utils.py \ --replace-fail 'Path("web/icons/icon_left_brand.png")' \ - 'Path("@webui@/icons/icon_left_brand.png")' \ + 'Path("${webuiPath}/icons/icon_left_brand.png")' \ --replace-fail 'Path("web/dist/assets/icons/icon_left_brand.png")' \ - 'Path("@webui@/dist/assets/icons/icon_left_brand.png")' + 'Path("${webuiPath}/dist/assets/icons/icon_left_brand.png")' # Lifecycle bash script: use Nix store bash (no /usr/bin/env in containers) substituteInPlace ${sp}/lifecycle/ak \ diff --git a/containers/authentik/default.nix b/containers/authentik/default.nix index 01cc2f1..d441405 100644 --- a/containers/authentik/default.nix +++ b/containers/authentik/default.nix @@ -1,19 +1,39 @@ -# Nix-built Authentik identity provider -# Uses nixpkgs authentik package (ak entrypoint wrapping Go server + Python worker) -# Built with dockerTools.buildLayeredImage for efficient layer caching +# Nix-built Authentik identity provider (from source) +# +# Assembles four component derivations into a container image: +# 1. webui — Lit frontend (esbuild + rollup) +# 2. authentik-django — Python backend + lifecycle scripts +# 3. authentik-server — Go HTTP server binary +# 4. ak wrapper — sets PATH/VIRTUAL_ENV, delegates to lifecycle/ak +# +# Built with dockerTools.buildLayeredImage for efficient layer caching. { pkgs ? import { } }: let - # Wrapper entrypoint that sets up /blueprints symlinks before running ak. - # buildLayeredImage's extraCommands can't access store paths from contents (they're - # in separate layers), so we create the symlinks at container start instead. + sources = import ./sources.nix { inherit pkgs; }; + webui = import ./webui.nix { inherit pkgs sources; }; + authentik-django = import ./authentik-django.nix { inherit pkgs sources webui; }; + authentik-server = import ./authentik-server.nix { inherit pkgs sources authentik-django webui; }; + + # Wrapper that provides bin/ak with the correct runtime environment. + # lifecycle/ak dispatches: "server" → Go binary, "worker"/"migrate"/etc → Python. + ak = pkgs.writeShellScriptBin "ak" '' + export PYTHONDONTWRITEBYTECODE=1 + export PATH="${authentik-server}/bin:${authentik-django}/bin:$PATH" + export VIRTUAL_ENV="${authentik-django}" + cd "${authentik-django}" + exec "${authentik-django}/lifecycle/ak" "$@" + ''; + + # Container entrypoint: symlink built-in blueprints then run ak. + # buildLayeredImage's extraCommands can't access store paths from contents + # (they're in separate layers), so we create the symlinks at container start. entrypoint = pkgs.writeShellScript "authentik-entrypoint" '' - # Link built-in blueprint dirs from the Nix store into /blueprints - for item in /nix/store/*authentik-django*/blueprints/*/; do + for item in ${authentik-django}/blueprints/*/; do name=$(basename "$item") [ ! -e "/blueprints/$name" ] && ln -s "$item" "/blueprints/$name" 2>/dev/null || true done - exec ${pkgs.authentik}/bin/ak "$@" + exec ${ak}/bin/ak "$@" ''; in @@ -22,7 +42,9 @@ pkgs.dockerTools.buildLayeredImage { tag = "latest"; contents = [ - pkgs.authentik + ak + authentik-django + authentik-server pkgs.bashInteractive pkgs.coreutils pkgs.cacert @@ -30,9 +52,8 @@ pkgs.dockerTools.buildLayeredImage { ]; # Create /blueprints as world-writable so user 65534 can create symlinks at runtime. - # The nixpkgs authentik-django package hardcodes blueprints_dir to its Nix store path, - # making custom blueprints mounted at /blueprints/custom invisible. The entrypoint - # wrapper populates this directory with symlinks to built-in blueprints on each start. + # authentik-django hardcodes blueprints_dir to $out/blueprints; the AUTHENTIK_BLUEPRINTS_DIR + # env var overrides it to /blueprints, where custom blueprints are mounted by k8s ConfigMap. extraCommands = '' mkdir -p blueprints chmod 777 blueprints diff --git a/containers/authentik/test-build.nix b/containers/authentik/test-build.nix index ee9711a..57b7569 100644 --- a/containers/authentik/test-build.nix +++ b/containers/authentik/test-build.nix @@ -9,16 +9,36 @@ # nix-build test-build.nix -A authentik-server --extra-experimental-features 'nix-command flakes' # nix-build test-build.nix -A webui-deps --extra-experimental-features 'nix-command flakes' # nix-build test-build.nix -A webui --extra-experimental-features 'nix-command flakes' +# nix-build test-build.nix -A assembled --extra-experimental-features 'nix-command flakes' let pkgs = (builtins.getFlake "nixpkgs").legacyPackages.x86_64-linux; sources = import ./sources.nix { inherit pkgs; }; + + # Individual components (isolated, no cross-wiring) + _webui = import ./webui.nix { inherit pkgs sources; }; + + # Fully wired assembly (webui → authentik-django → authentik-server) + _authentik-django-assembled = import ./authentik-django.nix { inherit pkgs sources; webui = _webui; }; + _authentik-server-assembled = import ./authentik-server.nix { + inherit pkgs sources; + authentik-django = _authentik-django-assembled; + webui = _webui; + }; in { + # Individual component builds (for debugging in isolation) python-deps = import ./python-deps.nix { inherit pkgs sources; }; authentik-django = import ./authentik-django.nix { inherit pkgs sources; }; client-go = import ./client-go.nix { inherit pkgs sources; }; client-ts = import ./client-ts.nix { inherit pkgs sources; }; authentik-server = import ./authentik-server.nix { inherit pkgs sources; }; webui-deps = import ./webui-deps.nix { inherit pkgs sources; }; - webui = import ./webui.nix { inherit pkgs sources; }; + webui = _webui; + + # Fully assembled stack — tests that all components wire together + assembled = pkgs.linkFarm "authentik-assembled-${sources.version}" [ + { name = "authentik-django"; path = _authentik-django-assembled; } + { name = "authentik-server"; path = _authentik-server-assembled; } + { name = "webui"; path = _webui; } + ]; }