authentik-container/authentik-container.nix

260 lines
7.8 KiB
Nix
Raw Permalink Normal View History

2023-08-29 22:12:23 -07:00
{ config, lib, pkgs, ... }@toplevel:
2023-08-28 17:54:22 -07:00
with lib;
let
cfg = config.services.authentikContainer;
2023-08-28 18:33:55 -07:00
hostname = config.instance.hostname;
2023-08-29 22:12:23 -07:00
domainName = config.fudo.hosts."${hostname}".domain;
2023-08-28 18:33:55 -07:00
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
2023-08-28 17:54:22 -07:00
mkEnvFile = envVars:
let
envLines =
mapAttrsToList (var: val: ''${var}="${toString val}"'') envVars;
in pkgs.writeText "envFile" (concatStringsSep "\n" envLines);
2023-08-28 18:45:39 -07:00
mkUserMap = uid: "${toString uid}:${toString uid}";
2023-08-28 17:54:22 -07:00
postgresPasswdFile =
pkgs.lib.passwd.stablerandom-passwd-file "authentik-postgresql-passwd"
config.instance.build-seed;
2023-08-29 09:57:21 -07:00
authentikSecretKeyFile =
pkgs.lib.passwd.stablerandom-passwd-file "authentik-secret-key"
config.instance.build-seed;
2023-08-28 17:54:22 -07:00
in {
options.services.authentikContainer = with types; {
enable = mkEnableOption "Enable Authentik running in an Arion container.";
state-directory = mkOption {
type = str;
description = "Directory at which to store server state data.";
};
images = {
authentik = mkOption { type = str; };
postgres = mkOption { type = str; };
redis = mkOption { type = str; };
};
2023-08-28 18:36:49 -07:00
ports = {
http = mkOption {
type = port;
default = 5030;
};
https = mkOption {
type = port;
default = 5031;
};
};
2023-08-29 22:12:23 -07:00
smtp = {
host = mkOption {
type = str;
default = "smtp.${domainName}";
};
port = mkOption {
type = port;
2023-12-01 12:46:51 -08:00
default = 587;
2023-08-29 22:12:23 -07:00
};
user = mkOption {
type = str;
default = "authentik";
};
password-file = mkOption { type = str; };
from-address = mkOption {
type = str;
default =
2023-12-01 14:14:00 -08:00
"Fudo Authentication <${toplevel.config.services.authentikContainer.smtp.user}@${domainName}>";
2023-08-29 22:12:23 -07:00
};
};
2024-02-14 12:29:49 -08:00
extraCerts = mkOption {
type = attrsOf str;
description = "Map of certificate name to certificate location.";
2024-02-14 12:29:49 -08:00
default = { };
};
2023-08-28 17:54:22 -07:00
uids = {
authentik = mkOption {
type = int;
default = 721;
};
2023-08-28 18:49:26 -07:00
postgres = mkOption {
2023-08-28 17:54:22 -07:00
type = int;
default = 722;
};
2023-08-28 18:49:26 -07:00
redis = mkOption {
2023-08-28 17:54:22 -07:00
type = int;
default = 723;
};
};
};
config = mkIf cfg.enable {
systemd = {
tmpfiles.rules = [
"d ${cfg.state-directory}/postgres 0700 authentik-postgres root - -"
"d ${cfg.state-directory}/redis 0700 authentik-redis root - -"
"d ${cfg.state-directory}/media 0700 authentik root - -"
"d ${cfg.state-directory}/templates 0700 authentik root - -"
"d ${cfg.state-directory}/certs 0700 authentik root - -"
];
2024-02-14 12:29:49 -08:00
services = {
authentik-cert-copy = {
wantedBy = [ "arion-authentik.service" ];
before = [ "arion-authentik.service" ];
script = let
mkCopyCommand = name: src:
let target = "${cfg.state-directory}/certs/${name}";
in ''
cp -v "${src}" "${target}"
chown authentik:root "${target}"
'';
in concatStringsSep "\n"
(mapAttrsToList mkCopyCommand cfg.extraCerts);
2024-02-14 12:29:49 -08:00
};
arion-authentik = {
after = [ "network-online.target" "podman.service" ];
requires = [ "network-online.target" "podman.service" ];
serviceConfig = {
Restart = "on-failure";
RestartSec = 120;
};
2024-02-03 16:40:07 -08:00
};
2023-08-28 17:54:22 -07:00
};
};
2024-01-06 10:38:58 -08:00
users = {
users = {
authentik = {
isSystemUser = true;
group = "authentik";
uid = cfg.uids.authentik;
};
authentik-postgres = {
isSystemUser = true;
group = "authentik";
uid = cfg.uids.postgres;
};
authentik-redis = {
isSystemUser = true;
group = "authentik";
uid = cfg.uids.redis;
};
2023-08-28 17:54:22 -07:00
};
2024-01-06 10:38:58 -08:00
groups.authentik.members =
[ "authentik" "authentik-postgres" "authentik-redis" ];
2023-08-28 17:54:22 -07:00
};
fudo.secrets.host-secrets."${hostname}" = {
authentikEnv = {
source-file = mkEnvFile {
AUTHENTIK_REDIS__HOST = "redis";
2023-08-29 22:12:23 -07:00
2023-08-28 17:54:22 -07:00
AUTHENTIK_POSTGRESQL__HOST = "postgres";
AUTHENTIK_POSTGRESQL__NAME = "authentik";
AUTHENTIK_POSTGRESQL__USER = "authentik";
AUTHENTIK_POSTGRESQL__PASSWORD = readFile postgresPasswdFile;
2023-08-29 22:12:23 -07:00
2023-08-29 09:57:21 -07:00
AUTHENTIK_SECRET_KEY = readFile authentikSecretKeyFile;
2023-08-29 22:12:23 -07:00
2024-02-06 14:42:23 -08:00
AUTHENTIK_DEFAULT_USER_CHANGE_USERNAME = toString false;
2023-08-29 22:12:23 -07:00
AUTHENTIK_EMAIL__HOST = cfg.smtp.host;
AUTHENTIK_EMAIL__PORT = toString cfg.smtp.port;
AUTHENTIK_EMAIL__USERNAME = cfg.smtp.user;
AUTHENTIK_EMAIL__PASSWORD =
removeSuffix "\n" (readFile cfg.smtp.password-file);
2023-12-01 13:15:58 -08:00
AUTHENTIK_EMAIL__USE_SSL =
optionalString (cfg.smtp.port == 465) "TRUE";
AUTHENTIK_EMAIL__USE_TLS =
optionalString (cfg.smtp.port == 25 || cfg.smtp.port == 587) "TRUE";
2023-08-29 22:12:23 -07:00
AUTHENTIK_EMAIL__TIMEOUT = 10;
AUTHENTIK_EMAIL__FROM = cfg.smtp.from-address;
2023-08-28 17:54:22 -07:00
};
target-file = "/run/authentik/authentik.env";
};
authentikPostgresEnv = {
source-file = mkEnvFile {
POSTGRES_DB = "authentik";
POSTGRES_USER = "authentik";
POSTGRES_PASSWORD = readFile postgresPasswdFile;
};
target-file = "/run/authentik/postgres.env";
};
};
virtualisation.arion.projects.authentik.settings = let
image = { ... }: {
project.name = "authentik";
services = {
postgres.service = {
2023-08-28 17:54:22 -07:00
image = cfg.images.postgres;
restart = "always";
2023-08-29 22:47:10 -07:00
command = "-c 'max_connections=300'";
2023-08-28 17:54:22 -07:00
volumes =
[ "${cfg.state-directory}/postgres:/var/lib/postgresql/data" ];
healthcheck = {
2023-08-29 10:25:26 -07:00
test = [ "CMD" "pg_isready" "-U" "authentik" "-d" "authentik" ];
2023-08-28 17:54:22 -07:00
start_period = "20s";
interval = "30s";
2023-08-28 18:52:41 -07:00
retries = 5;
2023-08-28 17:54:22 -07:00
timeout = "3s";
};
user = mkUserMap cfg.uids.postgres;
env_file = [ hostSecrets.authentikPostgresEnv.target-file ];
};
redis.service = {
2023-08-28 17:54:22 -07:00
image = cfg.images.redis;
restart = "always";
command = "--save 60 1 --loglevel warning";
2023-08-29 10:24:13 -07:00
volumes = [ "${cfg.state-directory}/redis:/data" ];
2023-08-28 17:54:22 -07:00
healthcheck = {
test = [ "CMD" "redis-cli" "ping" ];
start_period = "20s";
interval = "30s";
2023-08-28 18:52:41 -07:00
retries = 5;
2023-08-28 17:54:22 -07:00
timeout = "3s";
};
user = mkUserMap cfg.uids.redis;
};
server.service = {
2023-08-28 17:54:22 -07:00
image = cfg.images.authentik;
restart = "always";
command = "server";
env_file = [ hostSecrets.authentikEnv.target-file ];
volumes = [
"${cfg.state-directory}/media:/media"
"${cfg.state-directory}/templates:/templates"
];
user = mkUserMap cfg.uids.authentik;
ports = [
"${toString cfg.ports.http}:9000"
"${toString cfg.ports.https}:9443"
];
2023-08-29 09:43:28 -07:00
depends_on = [ "postgres" "redis" ];
2023-08-28 17:54:22 -07:00
};
worker.service = {
2023-08-28 17:54:22 -07:00
image = cfg.images.authentik;
restart = "always";
command = "worker";
env_file = [ hostSecrets.authentikEnv.target-file ];
volumes = [
"${cfg.state-directory}/media:/media"
"${cfg.state-directory}/certs:/certs"
"${cfg.state-directory}/templates:/templates"
];
user = mkUserMap cfg.uids.authentik;
2023-08-29 09:43:28 -07:00
depends_on = [ "postgres" "redis" ];
2023-08-28 17:54:22 -07:00
};
};
};
in { imports = [ image ]; };
};
}