treewide: nixfmt

This commit is contained in:
WilliButz 2025-02-02 14:16:38 +01:00
parent d653af66b3
commit dbfc2207df
No known key found for this signature in database
GPG key ID: AB05DF703EB9DC70
14 changed files with 688 additions and 564 deletions

View file

@ -1,7 +1,8 @@
{ authentik-src {
, authentik-version authentik-src,
, buildNapalmPackage authentik-version,
, nodejs_22 buildNapalmPackage,
nodejs_22,
}: }:
buildNapalmPackage "${authentik-src}/website" { buildNapalmPackage "${authentik-src}/website" {
@ -52,8 +53,7 @@ buildNapalmPackage "${authentik-src}/website" {
let let
files = builtins.readDir ./docs-extra-package-locks; files = builtins.readDir ./docs-extra-package-locks;
in in
builtins.concatMap (f: builtins.concatMap (
if files.${f} == "regular" f: if files.${f} == "regular" then [ (./docs-extra-package-locks + "/${f}") ] else [ ]
then [ (./docs-extra-package-locks + "/${f}") ] else []
) (builtins.attrNames files); ) (builtins.attrNames files);
} }

View file

@ -1,8 +1,9 @@
{ authentik-src {
, authentik-version authentik-src,
, authentikComponents authentik-version,
, buildNapalmPackage authentikComponents,
, nodejs_22 buildNapalmPackage,
nodejs_22,
}: }:
buildNapalmPackage "${authentik-src}/web" rec { buildNapalmPackage "${authentik-src}/web" rec {
version = authentik-version; # 0.0.0 specified upstream in package.json version = authentik-version; # 0.0.0 specified upstream in package.json

View file

@ -1,9 +1,10 @@
{ authentik-src {
, authentik-version authentik-src,
, authentikComponents authentik-version,
, buildGo123Module authentikComponents,
, lib buildGo123Module,
, makeWrapper lib,
makeWrapper,
}: }:
buildGo123Module { buildGo123Module {
@ -16,15 +17,15 @@ buildGo123Module {
''; '';
src = lib.cleanSourceWith { src = lib.cleanSourceWith {
src = authentik-src; src = authentik-src;
filter = (path: _: filter = (
path: _:
(builtins.any (x: x) ( (builtins.any (x: x) (
(map (infix: lib.hasInfix infix path) [ (map (infix: lib.hasInfix infix path) [
"/authentik" "/authentik"
"/cmd" "/cmd"
"/internal" "/internal"
]) ])
++ ++ (map (suffix: lib.hasSuffix suffix path) [
(map (suffix: lib.hasSuffix suffix path) [
"/web" "/web"
"/web/static.go" "/web/static.go"
"/web/robots.txt" "/web/robots.txt"

View file

@ -1,16 +1,19 @@
{ authentik-src {
, authentikComponents authentik-src,
, makeWrapper authentikComponents,
, runCommandLocal makeWrapper,
runCommandLocal,
}: }:
runCommandLocal "authentik-manage" { runCommandLocal "authentik-manage"
nativeBuildInputs = [ makeWrapper ]; {
} '' nativeBuildInputs = [ makeWrapper ];
mkdir -vp $out/bin }
cp -v ${authentik-src}/manage.py $out/bin/manage.py ''
mkdir -vp $out/bin
cp -v ${authentik-src}/manage.py $out/bin/manage.py
wrapProgram $out/bin/manage.py \ wrapProgram $out/bin/manage.py \
--prefix PATH : ${authentikComponents.pythonEnv}/bin \ --prefix PATH : ${authentikComponents.pythonEnv}/bin \
--prefix PYTHONPATH : ${authentikComponents.staticWorkdirDeps} --prefix PYTHONPATH : ${authentikComponents.staticWorkdirDeps}
'' ''

View file

@ -1,21 +1,24 @@
{ authentik-src {
, authentikComponents authentik-src,
, makeWrapper authentikComponents,
, runCommandLocal makeWrapper,
runCommandLocal,
}: }:
runCommandLocal "authentik-migrate.py" { runCommandLocal "authentik-migrate.py"
nativeBuildInputs = [ makeWrapper ]; {
} '' nativeBuildInputs = [ makeWrapper ];
mkdir -vp $out/bin }
cp ${authentik-src}/lifecycle/migrate.py $out/bin/migrate.py ''
chmod +w $out/bin/migrate.py mkdir -vp $out/bin
patchShebangs $out/bin/migrate.py cp ${authentik-src}/lifecycle/migrate.py $out/bin/migrate.py
substituteInPlace $out/bin/migrate.py \ chmod +w $out/bin/migrate.py
--replace \ patchShebangs $out/bin/migrate.py
'migration_path in Path(__file__).parent.absolute().glob("system_migrations/*.py")' \ substituteInPlace $out/bin/migrate.py \
'migration_path in Path("${authentikComponents.staticWorkdirDeps}/lifecycle").glob("system_migrations/*.py")' --replace \
wrapProgram $out/bin/migrate.py \ 'migration_path in Path(__file__).parent.absolute().glob("system_migrations/*.py")' \
--prefix PATH : ${authentikComponents.pythonEnv}/bin \ 'migration_path in Path("${authentikComponents.staticWorkdirDeps}/lifecycle").glob("system_migrations/*.py")'
--prefix PYTHONPATH : ${authentikComponents.staticWorkdirDeps} wrapProgram $out/bin/migrate.py \
'' --prefix PATH : ${authentikComponents.pythonEnv}/bin \
--prefix PYTHONPATH : ${authentikComponents.staticWorkdirDeps}
''

View file

@ -1,9 +1,10 @@
{ authentik-src {
, authentikPoetryOverrides authentik-src,
, defaultPoetryOverrides authentikPoetryOverrides,
, lib defaultPoetryOverrides,
, mkPoetryEnv lib,
, python312 mkPoetryEnv,
python312,
}: }:
mkPoetryEnv { mkPoetryEnv {
@ -12,13 +13,13 @@ mkPoetryEnv {
overrides = [ overrides = [
defaultPoetryOverrides defaultPoetryOverrides
] ++ authentikPoetryOverrides; ] ++ authentikPoetryOverrides;
groups = ["main"]; groups = [ "main" ];
checkGroups = []; checkGroups = [ ];
# workaround to remove dev-dependencies for the current combination of legacy # workaround to remove dev-dependencies for the current combination of legacy
# used by authentik and poetry2nix's behavior # used by authentik and poetry2nix's behavior
pyproject = builtins.toFile "patched-pyproject.toml" (lib.replaceStrings pyproject = builtins.toFile "patched-pyproject.toml" (
["tool.poetry.dev-dependencies"] lib.replaceStrings [ "tool.poetry.dev-dependencies" ] [ "tool.poetry.group.dev.dependencies" ] (
["tool.poetry.group.dev.dependencies"] builtins.readFile "${authentik-src}/pyproject.toml"
(builtins.readFile "${authentik-src}/pyproject.toml") )
); );
} }

View file

@ -1,7 +1,8 @@
{ authentik-src {
, authentikComponents authentik-src,
, linkFarm authentikComponents,
, applyPatches linkFarm,
applyPatches,
}: }:
let let
patched-src = applyPatches { patched-src = applyPatches {
@ -14,11 +15,32 @@ let
}; };
in in
linkFarm "authentik-static-workdir-deps" [ linkFarm "authentik-static-workdir-deps" [
{ name = "authentik"; path = "${patched-src}/authentik"; } {
{ name = "locale"; path = "${authentik-src}/locale"; } name = "authentik";
{ name = "blueprints"; path = "${authentik-src}/blueprints"; } path = "${patched-src}/authentik";
{ name = "internal"; path = "${authentik-src}/internal"; } }
{ name = "lifecycle"; path = "${patched-src}/lifecycle"; } {
{ name = "schemas"; path = "${authentik-src}/schemas"; } name = "locale";
{ name = "web"; path = authentikComponents.frontend; } path = "${authentik-src}/locale";
}
{
name = "blueprints";
path = "${authentik-src}/blueprints";
}
{
name = "internal";
path = "${authentik-src}/internal";
}
{
name = "lifecycle";
path = "${patched-src}/lifecycle";
}
{
name = "schemas";
path = "${authentik-src}/schemas";
}
{
name = "web";
path = authentikComponents.frontend;
}
] ]

View file

@ -1,10 +1,9 @@
(import (import (
( let
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in lock = builtins.fromJSON (builtins.readFile ./flake.lock);
fetchTarball { in
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; fetchTarball {
sha256 = lock.nodes.flake-compat.locked.narHash; url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
} sha256 = lock.nodes.flake-compat.locked.narHash;
) }
{ src = ./.; } ) { src = ./.; }).defaultNix
).defaultNix

221
flake.nix
View file

@ -30,101 +30,150 @@
flake-utils.follows = "flake-utils"; flake-utils.follows = "flake-utils";
}; };
}; };
authentik-src = { # change version string in outputs as well when updating authentik-src = {
# change version string in outputs as well when updating
url = "github:goauthentik/authentik/version/2024.12.3"; url = "github:goauthentik/authentik/version/2024.12.3";
flake = false; flake = false;
}; };
}; };
outputs = inputs@{ outputs =
self, inputs@{
nixpkgs, self,
flake-parts, nixpkgs,
poetry2nix, flake-parts,
napalm, poetry2nix,
authentik-src, napalm,
... authentik-src,
}: ...
}:
flake-parts.lib.mkFlake flake-parts.lib.mkFlake { inherit inputs; } (
{ inherit inputs; } {
({ inputs, lib, withSystem, ... }: inputs,
let lib,
authentik-version = "2024.12.3"; # to pass to the drvs of some components withSystem,
in { ...
systems = import inputs.systems; }:
flake = { self, ... }: { let
nixosModules.default = { pkgs, ... }: { authentik-version = "2024.12.3"; # to pass to the drvs of some components
imports = [ ./module.nix ]; in
services.authentik.authentikComponents = pkgs.lib.mkDefault (withSystem pkgs.stdenv.hostPlatform.system ( {
{ config, ... }: systems = import inputs.systems;
{ inherit (config.packages) manage staticWorkdirDeps migrate pythonEnv frontend gopkgs docs; } flake =
)); { self, ... }:
}; {
nixosModules.default =
{ pkgs, ... }:
{
imports = [ ./module.nix ];
services.authentik.authentikComponents = pkgs.lib.mkDefault (
withSystem pkgs.stdenv.hostPlatform.system (
{ config, ... }:
{
inherit (config.packages)
manage
staticWorkdirDeps
migrate
pythonEnv
frontend
gopkgs
docs
;
}
)
);
};
# returns a scope which includes the attrset `authentikComponents` # returns a scope which includes the attrset `authentikComponents`
# #
# the returned scope may be overridden using its `overrideScope` function to # the returned scope may be overridden using its `overrideScope` function to
# create a new scope with patched versions of individual authentik components # create a new scope with patched versions of individual authentik components
# #
# see ./tests/override-scope.nix for a usage example # see ./tests/override-scope.nix for a usage example
lib.mkAuthentikScope = let authentik-version' = authentik-version; in { lib.mkAuthentikScope =
pkgs, let
system ? pkgs.stdenv.hostPlatform.system, authentik-version' = authentik-version;
authentik-version ? authentik-version', in
mkPoetryEnv ? (import inputs.poetry2nix { inherit pkgs; }).mkPoetryEnv, {
defaultPoetryOverrides ? (import inputs.poetry2nix { inherit pkgs; }).defaultPoetryOverrides, pkgs,
authentikPoetryOverrides ? import ./poetry2nix-python-overrides.nix pkgs, system ? pkgs.stdenv.hostPlatform.system,
buildNapalmPackage ? napalm.legacyPackages.${system}.buildPackage authentik-version ? authentik-version',
}: mkPoetryEnv ? (import inputs.poetry2nix { inherit pkgs; }).mkPoetryEnv,
import ./components { defaultPoetryOverrides ? (import inputs.poetry2nix { inherit pkgs; }).defaultPoetryOverrides,
inherit pkgs authentik-src authentik-version mkPoetryEnv defaultPoetryOverrides authentikPoetryOverrides buildNapalmPackage; authentikPoetryOverrides ? import ./poetry2nix-python-overrides.nix pkgs,
buildNapalmPackage ? napalm.legacyPackages.${system}.buildPackage,
}:
import ./components {
inherit
pkgs
authentik-src
authentik-version
mkPoetryEnv
defaultPoetryOverrides
authentikPoetryOverrides
buildNapalmPackage
;
};
}; };
}; perSystem =
perSystem = { pkgs, system, self', ... }: let {
inherit (self.lib.mkAuthentikScope { inherit pkgs; }) authentikComponents; pkgs,
in { system,
packages = { self',
inherit (authentikComponents) ...
docs }:
frontend let
pythonEnv inherit (self.lib.mkAuthentikScope { inherit pkgs; }) authentikComponents;
gopkgs in
staticWorkdirDeps {
migrate packages = {
manage; inherit (authentikComponents)
docs
frontend
pythonEnv
gopkgs
staticWorkdirDeps
migrate
manage
;
terraform-provider-authentik = inputs.nixpkgs.legacyPackages.${system}.buildGo123Module rec { terraform-provider-authentik = inputs.nixpkgs.legacyPackages.${system}.buildGo123Module rec {
pname = "terraform-provider-authentik"; pname = "terraform-provider-authentik";
version = "2024.10.1"; version = "2024.10.1";
src = pkgs.fetchFromGitHub { src = pkgs.fetchFromGitHub {
owner = "goauthentik"; owner = "goauthentik";
repo = pname; repo = pname;
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-vU1VHDlxwi5YblVBa3lE1BLuk1Qr3AMSmHY9adkLQZU="; sha256 = "sha256-vU1VHDlxwi5YblVBa3lE1BLuk1Qr3AMSmHY9adkLQZU=";
};
doCheck = false; # tests are run against authentik -> vm test
vendorHash = "sha256-MtTfEOev6NrZyly5VrTVhoIJZsQX/4xyZcQul5fVO50=";
postInstall = ''
path="$out/libexec/terraform-providers/registry.terraform.io/goauthentik/authentik/${version}/''${GOOS}_''${GOARCH}/"
mkdir -p "$path"
mv $out/bin/${pname} $path/${pname}_v${version}
rmdir $out/bin
'';
};
};
checks = {
default = self.checks.${system}.vmtest;
vmtest = (
import tests/minimal-vmtest.nix {
inherit pkgs authentik-version;
inherit (self) nixosModules;
}
);
override-scope = (
import tests/override-scope.nix {
inherit pkgs authentik-version;
inherit (self) nixosModules;
inherit (self.lib) mkAuthentikScope;
}
);
}; };
doCheck = false; # tests are run against authentik -> vm test
vendorHash = "sha256-MtTfEOev6NrZyly5VrTVhoIJZsQX/4xyZcQul5fVO50=";
postInstall = ''
path="$out/libexec/terraform-providers/registry.terraform.io/goauthentik/authentik/${version}/''${GOOS}_''${GOARCH}/"
mkdir -p "$path"
mv $out/bin/${pname} $path/${pname}_v${version}
rmdir $out/bin
'';
}; };
}; }
checks = { );
default = self.checks.${system}.vmtest;
vmtest = (import tests/minimal-vmtest.nix {
inherit pkgs authentik-version;
inherit (self) nixosModules;
});
override-scope = (import tests/override-scope.nix {
inherit pkgs authentik-version;
inherit (self) nixosModules;
inherit (self.lib) mkAuthentikScope;
});
};
};
});
} }

View file

@ -1,41 +1,49 @@
{ config {
, lib config,
, pkgs lib,
, ... pkgs,
...
}: }:
let let
inherit (lib) inherit (lib)
types; types
;
inherit (lib.attrsets) inherit (lib.attrsets)
attrNames attrNames
getAttrs getAttrs
mapAttrsToList; mapAttrsToList
;
inherit (lib.lists) inherit (lib.lists)
flatten flatten
toList; toList
;
inherit (lib.modules) inherit (lib.modules)
mkDefault mkDefault
mkIf mkIf
mkMerge mkMerge
mkOverride; mkOverride
;
inherit (lib.options) inherit (lib.options)
mkEnableOption mkEnableOption
mkOption; mkOption
;
inherit (lib.strings) inherit (lib.strings)
concatStringsSep concatStringsSep
optionalString optionalString
versionOlder; versionOlder
;
inherit (lib.trivial) inherit (lib.trivial)
boolToString boolToString
isBool; isBool
;
settingsFormat = pkgs.formats.yaml {}; settingsFormat = pkgs.formats.yaml { };
in in
{ {
options.services = { options.services = {
@ -50,7 +58,7 @@ in
settings = mkOption { settings = mkOption {
type = types.submodule { type = types.submodule {
freeformType = settingsFormat.type; freeformType = settingsFormat.type;
options = {}; options = { };
}; };
}; };
@ -141,211 +149,233 @@ in
config = mkMerge [ config = mkMerge [
# authentik server # authentik server
(mkIf config.services.authentik.enable (let (mkIf config.services.authentik.enable (
cfg = config.services.authentik; let
cfg = config.services.authentik;
# https://goauthentik.io/docs/installation/docker-compose#startup # https://goauthentik.io/docs/installation/docker-compose#startup
tz = "UTC"; tz = "UTC";
# Passed to each service and to the `ak` wrapper using `systemd-run(1)` # Passed to each service and to the `ak` wrapper using `systemd-run(1)`
serviceDefaults = { serviceDefaults = {
DynamicUser = true; DynamicUser = true;
User = "authentik"; User = "authentik";
EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
}; };
akOptions = flatten (mapAttrsToList akOptions = flatten (
# Map defaults for each authentik service (listed above) to command line parameters for mapAttrsToList
# `systemd-run(1)` in order to spin up an environment with correct (dynamic) user, # Map defaults for each authentik service (listed above) to command line parameters for
# state directory and environment to run `ak` inside. # `systemd-run(1)` in order to spin up an environment with correct (dynamic) user,
(k: vs: map # state directory and environment to run `ak` inside.
(v: "--property ${k}=${if isBool v then boolToString v else toString v}") (k: vs: map (v: "--property ${k}=${if isBool v then boolToString v else toString v}") (toList vs))
(toList vs)) # Read serviceDefaults from `authentik.service`. That way, module system primitives (mk*)
# Read serviceDefaults from `authentik.service`. That way, module system primitives (mk*) # can be used inside `serviceDefaults` and it doesn't need to be evaluated here again.
# can be used inside `serviceDefaults` and it doesn't need to be evaluated here again. (
(getAttrs (attrNames serviceDefaults) config.systemd.services.authentik.serviceConfig // { getAttrs (attrNames serviceDefaults) config.systemd.services.authentik.serviceConfig
StateDirectory = "authentik"; // {
})); StateDirectory = "authentik";
in }
{ )
services = { );
authentik.settings = { in
blueprints_dir = mkDefault "${cfg.authentikComponents.staticWorkdirDeps}/blueprints"; {
template_dir = mkDefault "${cfg.authentikComponents.staticWorkdirDeps}/templates"; services = {
postgresql = mkIf cfg.createDatabase { authentik.settings = {
user = mkDefault "authentik"; blueprints_dir = mkDefault "${cfg.authentikComponents.staticWorkdirDeps}/blueprints";
name = mkDefault "authentik"; template_dir = mkDefault "${cfg.authentikComponents.staticWorkdirDeps}/templates";
host = mkDefault ""; postgresql = mkIf cfg.createDatabase {
user = mkDefault "authentik";
name = mkDefault "authentik";
host = mkDefault "";
};
cert_discovery_dir = mkIf (cfg.nginx.enable && cfg.nginx.enableACME) "env://CREDENTIALS_DIRECTORY";
storage.media = {
backend = mkDefault "file";
file = mkDefault {
path = "/var/lib/authentik/media";
};
};
media.enable_upload = mkDefault true;
}; };
cert_discovery_dir = mkIf (cfg.nginx.enable && cfg.nginx.enableACME) "env://CREDENTIALS_DIRECTORY"; redis.servers.authentik = {
storage.media = { enable = true;
backend = mkDefault "file"; port = 6379;
file = mkDefault { };
path = "/var/lib/authentik/media"; postgresql = mkIf cfg.createDatabase {
enable = true;
ensureDatabases = [ "authentik" ];
ensureUsers = [
{
name = "authentik";
ensureDBOwnership = true;
}
];
};
};
environment.systemPackages = [
(pkgs.writeShellScriptBin "ak" ''
exec ${config.systemd.package}/bin/systemd-run --pty --collect \
${concatStringsSep " \\\n" akOptions} \
--working-directory /var/lib/authentik \
-- ${cfg.authentikComponents.manage}/bin/manage.py "$@"
'')
];
environment.etc."authentik/config.yml".source =
settingsFormat.generate "authentik.yml" cfg.settings;
systemd.services = {
authentik-migrate = {
requiredBy = [ "authentik.service" ];
requires = lib.optionals cfg.createDatabase [ "postgresql.service" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ] ++ lib.optionals cfg.createDatabase [ "postgresql.service" ];
before = [ "authentik.service" ];
restartTriggers = [ config.environment.etc."authentik/config.yml".source ];
environment.TZ = tz;
serviceConfig = mkMerge [
serviceDefaults
{
Type = "oneshot";
RemainAfterExit = true;
RuntimeDirectory = "authentik-migrate";
WorkingDirectory = "%t/authentik-migrate";
ExecStartPre = [
# needs access to "authentik/sources/schemas"
"${pkgs.coreutils}/bin/ln -svf ${cfg.authentikComponents.staticWorkdirDeps}/authentik"
];
ExecStart = "${cfg.authentikComponents.migrate}/bin/migrate.py";
Restart = "on-failure";
RestartSec = "1s";
inherit (config.systemd.services.authentik.serviceConfig) StateDirectory;
}
];
};
authentik-worker = {
requiredBy = [ "authentik.service" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
before = [ "authentik.service" ];
restartTriggers = [ config.environment.etc."authentik/config.yml".source ];
preStart = ''
ln -svf ${config.services.authentik.authentikComponents.staticWorkdirDeps}/* /run/authentik/
'';
environment.TZ = tz;
serviceConfig = mkMerge [
serviceDefaults
{
RuntimeDirectory = "authentik";
WorkingDirectory = "%t/authentik";
ExecStart = "${cfg.authentikComponents.manage}/bin/manage.py worker";
Restart = "on-failure";
RestartSec = "1s";
LoadCredential = mkIf (cfg.nginx.enable && cfg.nginx.enableACME) [
"${cfg.nginx.host}.pem:${config.security.acme.certs.${cfg.nginx.host}.directory}/fullchain.pem"
"${cfg.nginx.host}.key:${config.security.acme.certs.${cfg.nginx.host}.directory}/key.pem"
];
# needs access to $StateDirectory/media/public
inherit (config.systemd.services.authentik.serviceConfig) StateDirectory;
}
];
};
authentik = {
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [
"network-online.target"
"redis-authentik.service"
] ++ (lib.optionals cfg.createDatabase [ "postgresql.service" ]);
restartTriggers = [ config.environment.etc."authentik/config.yml".source ];
preStart = ''
ln -svf ${cfg.authentikComponents.staticWorkdirDeps}/* /var/lib/authentik/
${optionalString (cfg.settings.storage.media.backend == "file") ''
mkdir -p ${cfg.settings.storage.media.file.path}
''}
'';
environment.TZ = tz;
serviceConfig = mkMerge [
serviceDefaults
{
StateDirectory = "authentik";
UMask = "0027";
# TODO /run might be sufficient
WorkingDirectory = "%S/authentik";
ExecStart = "${cfg.authentikComponents.gopkgs}/bin/server";
Restart = "on-failure";
RestartSec = "1s";
}
];
};
};
services.nginx = mkIf cfg.nginx.enable {
enable = true;
recommendedTlsSettings = true;
recommendedProxySettings = true;
virtualHosts.${cfg.nginx.host} = {
inherit (cfg.nginx) enableACME;
forceSSL = cfg.nginx.enableACME;
locations."/" = {
proxyWebsockets = true;
proxyPass = "https://localhost:9443";
}; };
}; };
media.enable_upload = mkDefault true;
}; };
redis.servers.authentik = { }
enable = true; ))
port = 6379;
};
postgresql = mkIf cfg.createDatabase {
enable = true;
ensureDatabases = [ "authentik" ];
ensureUsers = [
{ name = "authentik"; ensureDBOwnership = true; }
];
};
};
environment.systemPackages = [ # LDAP outpost
(pkgs.writeShellScriptBin "ak" '' (mkIf config.services.authentik-ldap.enable (
exec ${config.systemd.package}/bin/systemd-run --pty --collect \ let
${concatStringsSep " \\\n" akOptions} \ cfg = config.services.authentik-ldap;
--working-directory /var/lib/authentik \ in
-- ${cfg.authentikComponents.manage}/bin/manage.py "$@" {
'') systemd.services.authentik-ldap = {
];
environment.etc."authentik/config.yml".source = settingsFormat.generate "authentik.yml" cfg.settings;
systemd.services = {
authentik-migrate = {
requiredBy = [ "authentik.service" ];
requires = lib.optionals cfg.createDatabase [ "postgresql.service" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ] ++ lib.optionals cfg.createDatabase [ "postgresql.service" ];
before = [ "authentik.service" ];
restartTriggers = [ config.environment.etc."authentik/config.yml".source ];
environment.TZ = tz;
serviceConfig = mkMerge [ serviceDefaults {
Type = "oneshot";
RemainAfterExit = true;
RuntimeDirectory = "authentik-migrate";
WorkingDirectory = "%t/authentik-migrate";
ExecStartPre = [
# needs access to "authentik/sources/schemas"
"${pkgs.coreutils}/bin/ln -svf ${cfg.authentikComponents.staticWorkdirDeps}/authentik"
];
ExecStart = "${cfg.authentikComponents.migrate}/bin/migrate.py";
Restart = "on-failure";
RestartSec = "1s";
inherit (config.systemd.services.authentik.serviceConfig) StateDirectory;
} ];
};
authentik-worker = {
requiredBy = [ "authentik.service" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
before = [ "authentik.service" ];
restartTriggers = [ config.environment.etc."authentik/config.yml".source ];
preStart = ''
ln -svf ${config.services.authentik.authentikComponents.staticWorkdirDeps}/* /run/authentik/
'';
environment.TZ = tz;
serviceConfig = mkMerge [ serviceDefaults {
RuntimeDirectory = "authentik";
WorkingDirectory = "%t/authentik";
ExecStart = "${cfg.authentikComponents.manage}/bin/manage.py worker";
Restart = "on-failure";
RestartSec = "1s";
LoadCredential = mkIf (cfg.nginx.enable && cfg.nginx.enableACME) [
"${cfg.nginx.host}.pem:${config.security.acme.certs.${cfg.nginx.host}.directory}/fullchain.pem"
"${cfg.nginx.host}.key:${config.security.acme.certs.${cfg.nginx.host}.directory}/key.pem"
];
# needs access to $StateDirectory/media/public
inherit (config.systemd.services.authentik.serviceConfig) StateDirectory;
} ];
};
authentik = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ]; wants = [ "network-online.target" ];
after = [ after = [
"network-online.target" "network-online.target"
"redis-authentik.service" "authentik.service"
] ++ (lib.optionals cfg.createDatabase [ "postgresql.service" ]); ];
restartTriggers = [ config.environment.etc."authentik/config.yml".source ]; serviceConfig = {
preStart = '' RuntimeDirectory = "authentik-ldap";
ln -svf ${cfg.authentikComponents.staticWorkdirDeps}/* /var/lib/authentik/
${optionalString (cfg.settings.storage.media.backend == "file") ''
mkdir -p ${cfg.settings.storage.media.file.path}
''}
'';
environment.TZ = tz;
serviceConfig = mkMerge [ serviceDefaults {
StateDirectory = "authentik";
UMask = "0027"; UMask = "0027";
# TODO /run might be sufficient WorkingDirectory = "%t/authentik-ldap";
WorkingDirectory = "%S/authentik"; DynamicUser = true;
ExecStart = "${cfg.authentikComponents.gopkgs}/bin/server"; ExecStart = "${config.services.authentik.authentikComponents.gopkgs}/bin/ldap";
EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "1s";
} ];
};
};
services.nginx = mkIf cfg.nginx.enable {
enable = true;
recommendedTlsSettings = true;
recommendedProxySettings = true;
virtualHosts.${cfg.nginx.host} = {
inherit (cfg.nginx) enableACME;
forceSSL = cfg.nginx.enableACME;
locations."/" = {
proxyWebsockets = true;
proxyPass = "https://localhost:9443";
}; };
}; };
}; }
})) ))
# LDAP outpost
(mkIf config.services.authentik-ldap.enable (let
cfg = config.services.authentik-ldap;
in
{
systemd.services.authentik-ldap = {
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [
"network-online.target"
"authentik.service"
];
serviceConfig = {
RuntimeDirectory = "authentik-ldap";
UMask = "0027";
WorkingDirectory = "%t/authentik-ldap";
DynamicUser = true;
ExecStart = "${config.services.authentik.authentikComponents.gopkgs}/bin/ldap";
EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
Restart = "on-failure";
};
};
}))
# RADIUS outpost # RADIUS outpost
(mkIf config.services.authentik-radius.enable (let (mkIf config.services.authentik-radius.enable (
cfg = config.services.authentik-radius; let
in cfg = config.services.authentik-radius;
{ in
systemd.services.authentik-radius = { {
wantedBy = [ "multi-user.target" ]; systemd.services.authentik-radius = {
wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ];
after = [ wants = [ "network-online.target" ];
"network-online.target" after = [
"authentik.service" "network-online.target"
]; "authentik.service"
serviceConfig = { ];
RuntimeDirectory = "authentik-radius"; serviceConfig = {
UMask = "0027"; RuntimeDirectory = "authentik-radius";
WorkingDirectory = "%t/authentik-radius"; UMask = "0027";
DynamicUser = true; WorkingDirectory = "%t/authentik-radius";
ExecStart = "${config.services.authentik.authentikComponents.gopkgs}/bin/radius"; DynamicUser = true;
EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; ExecStart = "${config.services.authentik.authentikComponents.gopkgs}/bin/radius";
Restart = "on-failure"; EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
Restart = "on-failure";
};
}; };
}; }
})) ))
# This is an attempt to solve a rather ugly problem that was # This is an attempt to solve a rather ugly problem that was
# caused by previously setting a default for the option # caused by previously setting a default for the option

View file

@ -1,158 +1,169 @@
pkgs: pkgs: [
[
# modules missing only setuptools # modules missing only setuptools
(final: prev: (
(builtins.listToAttrs (map (name: { final: prev:
inherit name; (builtins.listToAttrs (
value = prev.${name}.overrideAttrs (oA: { map
nativeBuildInputs = (oA.nativeBuildInputs or []) ++ [ final.setuptools ]; (name: {
}); inherit name;
}) [ value = prev.${name}.overrideAttrs (oA: {
"django-cte" nativeBuildInputs = (oA.nativeBuildInputs or [ ]) ++ [ final.setuptools ];
"django-tenants" });
"dumb-init" })
"drf-orjson-renderer" [
])) "django-cte"
"django-tenants"
"dumb-init"
"drf-orjson-renderer"
]
))
) )
(final: prev: { (final: prev: {
xmlsec = prev.xmlsec.overridePythonAttrs (oA: { xmlsec = prev.xmlsec.overridePythonAttrs (oA: {
nativeBuildInputs = oA.nativeBuildInputs ++ [ final.setuptools final.pkgconfig ]; nativeBuildInputs = oA.nativeBuildInputs ++ [
buildInputs = [ pkgs.xmlsec.dev pkgs.xmlsec pkgs.libxml2 pkgs.libtool ]; final.setuptools
env.NIX_CFLAGS_COMPILE = "-Wno-error=incompatible-pointer-types"; final.pkgconfig
}); ];
opencontainers = prev.opencontainers.overrideAttrs (oA: { buildInputs = [
nativeBuildInputs = oA.nativeBuildInputs ++ [ pkgs.xmlsec.dev
final.setuptools pkgs.xmlsec
final.pytest pkgs.libxml2
]; pkgs.libtool
postPatch = '' ];
substituteInPlace setup.py --replace-fail '"pytest-runner"' ''' env.NIX_CFLAGS_COMPILE = "-Wno-error=incompatible-pointer-types";
''; });
}); opencontainers = prev.opencontainers.overrideAttrs (oA: {
psycopg-c = prev.psycopg-c.overrideAttrs (oA: { nativeBuildInputs = oA.nativeBuildInputs ++ [
nativeBuildInputs = oA.nativeBuildInputs ++ [ final.setuptools
final.setuptools final.pytest
final.tomli ];
pkgs.postgresql postPatch = ''
]; substituteInPlace setup.py --replace-fail '"pytest-runner"' '''
}); '';
twisted = prev.twisted.overrideAttrs (oA: { });
buildInputs = oA.buildInputs ++ [ psycopg-c = prev.psycopg-c.overrideAttrs (oA: {
final.hatchling nativeBuildInputs = oA.nativeBuildInputs ++ [
final.hatch-fancy-pypi-readme final.setuptools
]; final.tomli
}); pkgs.postgresql
#cryptography = prev.cryptography.overridePythonAttrs (oA: { ];
# cargoDeps = pkgs.rustPlatform.fetchCargoTarball { });
# src = oA.src; twisted = prev.twisted.overrideAttrs (oA: {
# sourceRoot = "${oA.pname}-${oA.version}/src/rust"; buildInputs = oA.buildInputs ++ [
# name = "${oA.pname}-${oA.version}"; final.hatchling
# sha256 = "sha256-PgxPcFocEhnQyrsNtCN8YHiMptBmk1PUhEDQFdUR1nU="; final.hatch-fancy-pypi-readme
# }; ];
#}); });
dnspython = prev.dnspython.overrideAttrs (oA: { #cryptography = prev.cryptography.overridePythonAttrs (oA: {
buildInputs = oA.buildInputs ++ [ # cargoDeps = pkgs.rustPlatform.fetchCargoTarball {
final.hatchling # src = oA.src;
]; # sourceRoot = "${oA.pname}-${oA.version}/src/rust";
}); # name = "${oA.pname}-${oA.version}";
sqlparse = prev.sqlparse.overrideAttrs (oA: { # sha256 = "sha256-PgxPcFocEhnQyrsNtCN8YHiMptBmk1PUhEDQFdUR1nU=";
nativeBuildInputs = oA.nativeBuildInputs ++ [ # };
final.hatchling #});
]; dnspython = prev.dnspython.overrideAttrs (oA: {
}); buildInputs = oA.buildInputs ++ [
scim2-filter-parser = prev.scim2-filter-parser.overrideAttrs (oA: { final.hatchling
patches = [ ];
(pkgs.fetchpatch { });
name = "replace-poetry-with-poetry-core.patch"; sqlparse = prev.sqlparse.overrideAttrs (oA: {
url = "https://patch-diff.githubusercontent.com/raw/15five/scim2-filter-parser/pull/43.patch"; nativeBuildInputs = oA.nativeBuildInputs ++ [
hash = "sha256-PjJH1S5CDe/BMI0+mB34KdpNNcHfexBFYBmHolsWH4o="; final.hatchling
}) ];
]; });
nativeBuildInputs = oA.nativeBuildInputs ++ [ scim2-filter-parser = prev.scim2-filter-parser.overrideAttrs (oA: {
final.poetry-core patches = [
]; (pkgs.fetchpatch {
}); name = "replace-poetry-with-poetry-core.patch";
pendulum = prev.pendulum.overrideAttrs (oA: { url = "https://patch-diff.githubusercontent.com/raw/15five/scim2-filter-parser/pull/43.patch";
nativeBuildInputs = oA.nativeBuildInputs ++ [ hash = "sha256-PjJH1S5CDe/BMI0+mB34KdpNNcHfexBFYBmHolsWH4o=";
pkgs.rustPlatform.cargoSetupHook })
pkgs.rustPlatform.maturinBuildHook ];
]; nativeBuildInputs = oA.nativeBuildInputs ++ [
cargoRoot = "rust"; final.poetry-core
cargoDeps = pkgs.rustPlatform.fetchCargoTarball { ];
src = oA.src; });
sourceRoot = "${oA.pname}-${oA.version}/rust"; pendulum = prev.pendulum.overrideAttrs (oA: {
name = "${oA.pname}-${oA.version}"; nativeBuildInputs = oA.nativeBuildInputs ++ [
sha256 = "sha256-6fw0KgnPIMfdseWcunsGjvjVB+lJNoG3pLDqkORPJ0I="; pkgs.rustPlatform.cargoSetupHook
}; pkgs.rustPlatform.maturinBuildHook
}); ];
django-pgactivity = prev.django-pgactivity.overrideAttrs (oA: { cargoRoot = "rust";
nativeBuildInputs = oA.nativeBuildInputs ++ [ cargoDeps = pkgs.rustPlatform.fetchCargoTarball {
final.poetry-core src = oA.src;
]; sourceRoot = "${oA.pname}-${oA.version}/rust";
}); name = "${oA.pname}-${oA.version}";
docker = prev.docker.overrideAttrs (oA: { sha256 = "sha256-6fw0KgnPIMfdseWcunsGjvjVB+lJNoG3pLDqkORPJ0I=";
nativeBuildInputs = oA.nativeBuildInputs ++ [ };
prev.hatchling });
prev.hatch-vcs django-pgactivity = prev.django-pgactivity.overrideAttrs (oA: {
]; nativeBuildInputs = oA.nativeBuildInputs ++ [
}); final.poetry-core
django-pglock= prev.django-pglock.overrideAttrs (oA: { ];
nativeBuildInputs = oA.nativeBuildInputs ++ [ });
final.poetry-core docker = prev.docker.overrideAttrs (oA: {
]; nativeBuildInputs = oA.nativeBuildInputs ++ [
}); prev.hatchling
# https://github.com/pyradius/pyrad/pull/168/files prev.hatch-vcs
# not included in the latest release :/ ];
pyrad = prev.pyrad.overrideAttrs (oA: { });
postPatch = '' django-pglock = prev.django-pglock.overrideAttrs (oA: {
substituteInPlace pyproject.toml \ nativeBuildInputs = oA.nativeBuildInputs ++ [
--replace-fail "poetry.masonry.api" "poetry.core.masonry.api" \ final.poetry-core
--replace-fail "repository =" "Repository =" ];
''; });
}); # https://github.com/pyradius/pyrad/pull/168/files
msgraph-sdk = prev.msgraph-sdk.overrideAttrs (oA: { # not included in the latest release :/
nativeBuildInputs = oA.nativeBuildInputs ++ [ pyrad = prev.pyrad.overrideAttrs (oA: {
final.flit-core postPatch = ''
]; substituteInPlace pyproject.toml \
}); --replace-fail "poetry.masonry.api" "poetry.core.masonry.api" \
python-kadmin-rs = prev.python-kadmin-rs.overrideAttrs (oA: { --replace-fail "repository =" "Repository ="
pythonImportsCheck = [ "kadmin" ]; '';
nativeBuildInputs = oA.nativeBuildInputs ++ [ });
pkgs.rustPlatform.cargoSetupHook msgraph-sdk = prev.msgraph-sdk.overrideAttrs (oA: {
pkgs.rustc nativeBuildInputs = oA.nativeBuildInputs ++ [
pkgs.cargo final.flit-core
final.setuptools ];
final.setuptools-scm });
final.setuptools-rust python-kadmin-rs = prev.python-kadmin-rs.overrideAttrs (oA: {
pkgs.sccache pythonImportsCheck = [ "kadmin" ];
pkgs.pkg-config nativeBuildInputs = oA.nativeBuildInputs ++ [
pkgs.rustPlatform.bindgenHook pkgs.rustPlatform.cargoSetupHook
pkgs.libkrb5 pkgs.rustc
]; pkgs.cargo
buildInputs = oA.buildInputs ++ [ final.setuptools
pkgs.krb5 final.setuptools-scm
]; final.setuptools-rust
cargoDeps = pkgs.rustPlatform.fetchCargoTarball { pkgs.sccache
inherit (oA) pname version src; pkgs.pkg-config
hash = "sha256-iH2fm4OUwLdx+lqmPNOkzM3LH6gBVYDtZ+livhOQrE4="; pkgs.rustPlatform.bindgenHook
}; pkgs.libkrb5
}); ];
gssapi = prev.gssapi.overrideAttrs (oA: { buildInputs = oA.buildInputs ++ [
nativeBuildInputs = oA.nativeBuildInputs ++ [ pkgs.krb5
final.setuptools ];
final.cython cargoDeps = pkgs.rustPlatform.fetchCargoTarball {
pkgs.krb5 inherit (oA) pname version src;
]; hash = "sha256-iH2fm4OUwLdx+lqmPNOkzM3LH6gBVYDtZ+livhOQrE4=";
postPatch = '' };
substituteInPlace setup.py \ });
--replace-fail 'get_output(f"{kc} gssapi --prefix")' '"${pkgs.krb5.dev}"' gssapi = prev.gssapi.overrideAttrs (oA: {
''; nativeBuildInputs = oA.nativeBuildInputs ++ [
pythonImportsCheck = [ "gssapi" ]; final.setuptools
}); final.cython
# break dependency cycle that causes an infinite recursion pkgs.krb5
ua-parser-builtins = prev.ua-parser-builtins.overridePythonAttrs (oA: { ];
propagatedBuildInputs = builtins.filter (p: p.pname != "ua-parser") oA.propagatedBuildInputs; postPatch = ''
}); substituteInPlace setup.py \
} --replace-fail 'get_output(f"{kc} gssapi --prefix")' '"${pkgs.krb5.dev}"'
) '';
pythonImportsCheck = [ "gssapi" ];
});
# break dependency cycle that causes an infinite recursion
ua-parser-builtins = prev.ua-parser-builtins.overridePythonAttrs (oA: {
propagatedBuildInputs = builtins.filter (p: p.pname != "ua-parser") oA.propagatedBuildInputs;
});
})
] ]

View file

@ -1,10 +1,9 @@
(import (import (
( let
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in lock = builtins.fromJSON (builtins.readFile ./flake.lock);
fetchTarball { in
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; fetchTarball {
sha256 = lock.nodes.flake-compat.locked.narHash; url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
} sha256 = lock.nodes.flake-compat.locked.narHash;
) }
{ src = ./.; } ) { src = ./.; }).shellNix
).shellNix

View file

@ -1,6 +1,7 @@
{ pkgs {
, authentik-version pkgs,
, nixosModules authentik-version,
nixosModules,
}: }:
let let
# use a root-owned EnvironmentFile in production instead (services.authentik.environmentFile) # use a root-owned EnvironmentFile in production instead (services.authentik.environmentFile)
@ -80,7 +81,7 @@ pkgs.nixosTest {
machine.succeed("su - alice -c 'xdotool key --delay 100 Page_Down' >&2") machine.succeed("su - alice -c 'xdotool key --delay 100 Page_Down' >&2")
# sometimes the cursor covers the version string # sometimes the cursor covers the version string
machine.succeed("su - alice -c 'xdotool mousemove_relative 50 50' >&2") machine.succeed("su - alice -c 'xdotool mousemove_relative 50 50' >&2")
machine.wait_for_text("${builtins.replaceStrings ["."] [".?"] authentik-version}") machine.wait_for_text("${builtins.replaceStrings [ "." ] [ ".?" ] authentik-version}")
machine.screenshot("4_correct_version_in_admin_interface") machine.screenshot("4_correct_version_in_admin_interface")
with subtest("nginx proxies to authentik"): with subtest("nginx proxies to authentik"):

View file

@ -1,20 +1,21 @@
{ pkgs {
, authentik-version pkgs,
, nixosModules authentik-version,
, mkAuthentikScope nixosModules,
mkAuthentikScope,
}: }:
/* /*
* This is just meant as a demonstration on how to override the scope which includes the This is just meant as a demonstration on how to override the scope which includes the
* authentik components. This is an extended version of ./minimal-vmtest.nix authentik components. This is an extended version of ./minimal-vmtest.nix
*
* First, a new scope is created from the default one using `overrideScope` on the result First, a new scope is created from the default one using `overrideScope` on the result
* from `mkAuthentikScope`. from `mkAuthentikScope`.
* Components with overrides in that scope are used by their dependents, i.e. dependents Components with overrides in that scope are used by their dependents, i.e. dependents
* of `pythonEnv` (e.g. gopkgs) also pull in that overridden `pythonEnv` of `pythonEnv` (e.g. gopkgs) also pull in that overridden `pythonEnv`
* Then, that scope is passed to the module via the `services.authentik.authentikComponents` option Then, that scope is passed to the module via the `services.authentik.authentikComponents` option
* And finally, the test script checks if the patched welcome string is present. And finally, the test script checks if the patched welcome string is present.
*/ */
let let
# use a root-owned EnvironmentFile in production instead (services.authentik.environmentFile) # use a root-owned EnvironmentFile in production instead (services.authentik.environmentFile)
@ -26,22 +27,25 @@ let
# creates a new scope using python 3.12 for mkPoetryEnv # creates a new scope using python 3.12 for mkPoetryEnv
# and overrides the welcome string for the default oobe intial-setup flow # and overrides the welcome string for the default oobe intial-setup flow
customScope = (mkAuthentikScope { inherit pkgs; }).overrideScope customScope = (mkAuthentikScope { inherit pkgs; }).overrideScope (
(final: prev: { final: prev: {
authentikComponents = prev.authentikComponents // { authentikComponents = prev.authentikComponents // {
pythonEnv = prev.authentikComponents.pythonEnv.overrideAttrs (_: { pythonEnv = prev.authentikComponents.pythonEnv.overrideAttrs (_: {
python = pkgs.python312; python = pkgs.python312;
}); });
staticWorkdirDeps = prev.authentikComponents.staticWorkdirDeps.overrideAttrs (oA: { staticWorkdirDeps = prev.authentikComponents.staticWorkdirDeps.overrideAttrs (oA: {
buildCommand = oA.buildCommand + '' buildCommand =
rm -v $out/blueprints oA.buildCommand
cp -vr ${prev.authentik-src}/blueprints $out/blueprints + ''
substituteInPlace $out/blueprints/default/flow-oobe.yaml \ rm -v $out/blueprints
--replace "Welcome to authentik" "${customWelcome}" cp -vr ${prev.authentik-src}/blueprints $out/blueprints
''; substituteInPlace $out/blueprints/default/flow-oobe.yaml \
--replace "Welcome to authentik" "${customWelcome}"
'';
}); });
}; };
}); }
);
in in
pkgs.nixosTest { pkgs.nixosTest {
name = "authentik"; name = "authentik";
@ -117,7 +121,7 @@ pkgs.nixosTest {
machine.succeed("su - alice -c 'xdotool key --delay 100 Page_Down' >&2") machine.succeed("su - alice -c 'xdotool key --delay 100 Page_Down' >&2")
# sometimes the cursor covers the version string # sometimes the cursor covers the version string
machine.succeed("su - alice -c 'xdotool mousemove_relative 50 50' >&2") machine.succeed("su - alice -c 'xdotool mousemove_relative 50 50' >&2")
machine.wait_for_text("${builtins.replaceStrings ["."] [".?"] authentik-version}") machine.wait_for_text("${builtins.replaceStrings [ "." ] [ ".?" ] authentik-version}")
machine.screenshot("4_correct_version_in_admin_interface") machine.screenshot("4_correct_version_in_admin_interface")
with subtest("nginx proxies to authentik"): with subtest("nginx proxies to authentik"):