authentik yay

This commit is contained in:
Lucy Hochkamp 2025-09-07 00:11:16 +02:00
parent d3a93fd115
commit f2fcbfb679
No known key found for this signature in database
34 changed files with 612 additions and 363 deletions

View file

@ -18,6 +18,7 @@
./presets/cli.nix
./presets/common.nix
./presets/gui.nix
./presets/server.nix
./presets/home-manager.nix
./services/authentik.nix
./services/caddy.nix

View file

@ -38,11 +38,11 @@ in
nix.settings = {
substituters = [
"https://cache.lix.systems"
# "https://cache.lix.systems"
"https://helix.cachix.org"
];
trusted-public-keys = [
"cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o="
# "cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o="
"helix.cachix.org-1:ejp9KQpR1FBI2onstMQ34yogDm4OgU2ru6lIwPvuCVs="
];
trusted-users = lib.mkDefault [

View file

@ -28,10 +28,6 @@ in
# ];
});
security.polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
polkit.log("action=" + action);
polkit.log("subject=" + subject);
});
polkit.addRule(function(action, subject) { // make run0 keep pw for some time (tm)
if (
subject.isInGroup("wheel")

View file

@ -6,22 +6,56 @@
}:
let
cfg = config.xyno.presets.server;
msmtpConfigItems = [
"host"
"port"
"from"
"user"
"password"
];
in
{
options.xyno.presets.server.enable =
lib.mkEnableOption "enables xynos base server config (ssh/smart/email/zed/...)";
config = lib.mkIf cfg.enable {
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/oMAi5jyQsNohfhcSH2ItisTpBGB0WtYTVxJYKKqhj"]; # theseus
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/oMAi5jyQsNohfhcSH2ItisTpBGB0WtYTVxJYKKqhj"
]; # theseus
environment.etc."msmtprc".enable = false;
sops.secrets."msmtp/rc" = {
sops.defaultSopsFile = ../../secrets/common.yaml;
sops.secrets = lib.mkMerge (
[
{
"msmtp/aliases" = {
path = "/etc/aliases";
};
}
]
++ (map (x: { "msmtp/${x}" = { }; }) msmtpConfigItems)
);
sops.templates."msmtprc" = {
path = "/etc/msmtprc";
content = ''
defaults
allow_from_override off
set_from_header on
auth on
tls on
tls_starttls off
account default
${lib.concatStringsSep "\n" (
map (x: "${x} ${config.sops.placeholder."msmtp/${x}"}") msmtpConfigItems
)}
auth on
aliases /etc/aliases
'';
};
sops.secrets."msmtp/aliases" = {
path = "/etc/aliases";
};
programs.msmtp = {
enable = true;
};

View file

@ -19,7 +19,7 @@ let
};
groups = mkOption {
type = types.listOf types.str;
default = [];
default = [ ];
};
meta_description = mkOption {
type = types.nullOr types.str;
@ -47,7 +47,13 @@ let
modules = [
./authentik/provider.nix
{
inherit (cfg) oauthApps ldapApps proxyApps;
inherit (cfg)
oauthApps
ldapApps
proxyApps
url
insecure
;
stateFile = "${terrraformStateDir}/state.tfstate";
}
];
@ -89,6 +95,18 @@ in
})
);
};
options.xyno.services.authentik.url = mkOption {
type = types.str;
default = "https://auth.hailsatan.eu";
};
options.xyno.services.authentik.insecure = mkOption {
type = types.bool;
default = false;
};
options.xyno.services.authentik.after = mkOption {
type = types.listOf types.str;
default = [];
};
config = lib.mkIf cfg.enable {
environment.etc."authentik-config/config.tf.json".source = terranixConfig;
xyno.impermanence.directories = [
@ -98,13 +116,20 @@ in
enable = true;
createDatabase = true;
environmentFile = config.sops.secrets."authentik/env".path;
settings = {
disable_startup_analytics = true;
};
};
systemd.services.authentik.after = cfg.after;
systemd.services.authentik-ldap.after = [ "authentik-config.service" ];
systemd.services.authentik-ldap.environment.AUTHENTIK_LISTEN__METRICS = "[::1]:9302";
services.authentik-ldap = {
environmentFile = "${environmentFileDir}/ldap_config";
enable = true;
};
systemd.services.authentik-proxy.after = [ "authentik-config.service" ];
systemd.services.authentik-proxy.environment.AUTHENTIK_LISTEN__HTTP = "[::1]:9001";
systemd.services.authentik-proxy.environment.AUTHENTIK_LISTEN__METRICS = "[::1]:9301";
services.authentik-proxy = {
enable = true;
environmentFile = "${environmentFileDir}/proxy_config";
@ -112,49 +137,80 @@ in
systemd.services.authentik-config = {
after = [ "authentik.service" ];
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
before = [
"authentik-ldap.service"
"authentik-proxy.service"
];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
StateDirectory = terrraformStateDir;
EnvironmentFile = [ config.services.authentik.environmentFile ];
Restart = "on-failure";
RestartSec = 30;
StateDirectory = "authentik-config";
};
script = ''
umask u=rw,go=
export PATH=$PATH:${pkgs.opentofu}/bin
cd terrraformStateDir
cp ${terranixConfig} ./main.tf.json
source ${config.services.authentik.environmentFile}
export AUTHENTIK_URL=http://localhost:9000
export AUTHENTIK_TOKEN=$AUTHENTIK_BOOTSTRAP_TOKEN
script =
let
opentofu = pkgs.opentofu.withPlugins (p: [
inputs.authentik.packages.${pkgs.system}.terraform-provider-authentik
]);
in
''
set -xeuo pipefail
umask u=rw,go=
export PATH=$PATH:${opentofu}/bin:${pkgs.curl}/bin
cd $STATE_DIRECTORY
cp ${terranixConfig} ./main.tf.json
export AUTHENTIK_URL=${cfg.url}
export AUTHENTIK_INSECURE=${toString cfg.insecure}
export AUTHENTIK_TOKEN=$AUTHENTIK_BOOTSTRAP_TOKEN
tofu init
tofu validate || exit 1
tofu apply
tofu init
tofu validate || exit 1
tofu output -raw proxy_config > ${environmentFileDir}/proxy_config
tofu output -raw ldap_config > ${environmentFileDir}/ldap_config
${concatStringsSep "\n" (
mapAttrsToList (n: v: "tofu output -raw ${n}_environment > ${v.environmentFile}") cfg.oauthApps
)}
'';
while [[ "$(curl -L -s -o /dev/null -I -w "%{http_code}" ''${AUTHENTIK_URL}/api/v3/flows/instances/)" =~ (502|000) ]]; do
echo "[+] Authentik still starting, sleeping for 2s"
sleep 2
done
tofu apply -auto-approve
tofu show -state
mkdir ${environmentFileDir}
tofu output -show-sensitive -raw proxy_config > ${environmentFileDir}/proxy_config
cat ${environmentFileDir}/proxy_config
tofu output -show-sensitive -raw ldap_config > ${environmentFileDir}/ldap_config
${concatStringsSep "\n" (
mapAttrsToList (
n: v: "tofu output -show-sensitive -raw ${n}_environment > ${v.environmentFile}"
) cfg.oauthApps
)}
'';
};
sops.secrets."authentik/env" = {
sopsFile = ../../instances/${config.networking.hostName}/secrets/authentik.yaml;
};
services.caddy.extraConfig = ''
(reverse_proxy_auth) {
route {
# always forward outpost path to actual outpost
reverse_proxy /outpost.goauthentik.io/* http://[::1]:9000 {
reverse_proxy /outpost.goauthentik.io/* http://[::1]:9001 {
}
forward_auth http://[::1]:9000 {
forward_auth http://[::1]:9001 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Copyparty-Group X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version X-Grafana-Role
}
reverse_proxy {args[:]}
}
'';
xyno.services.monitoring.exporters.authentik = 9300;
xyno.services.monitoring.exporters.authentik-proxy = 9301;
xyno.services.monitoring.exporters.authentik-ldap = 9302;
};
}

View file

@ -0,0 +1,2 @@
authentik:
env: meow

View file

@ -6,6 +6,7 @@ let
authorizationFlow = tfRef "data.authentik_flow.default-authorization-flow.id";
authenticationFlow = tfRef "data.authentik_flow.default-authentication-flow.id";
invalidationFlow = tfRef "data.authentik_flow.default-provider-invalidation-flow.id";
genApp = provider: n: v: {
protocol_provider = provider;
slug = n;
@ -25,29 +26,34 @@ in
oauthApps = mkOption { type = types.attrs; };
proxyApps = mkOption { type = types.attrs; };
ldapApps = mkOption { type = types.attrs; };
url = mkOption { type = types.str; };
insecure = mkOption { type = types.bool; };
};
config = {
terraform.backend.local.path = config.stateFile;
provider.authentik = { };
terraform.required_providers.authentik.source = "goauthentik/authentik";
data.authentik_flow."default-authorization-flow" = {
slug = "default-provider-authorization-implicit-consent";
};
data."authentik_flow"."default-authentication-flow" = {
slug = "default-authentication-flow";
};
data."authentik_flow"."default-provider-invalidation-flow" = {
slug = "default-provider-invalidation-flow";
};
resource.authentik_outpost.proxy = {
name = "proxy";
type = "proxy";
protocol_providers = mapAttrsToList (
n: v: (tfRef "authentik_provider_proxy.${n}.id")
n: v: (tfRef "resource.authentik_provider_proxy.${n}.id")
) config.proxyApps;
};
resource.authentik_outpost.ldap = {
name = "ldap";
type = "ldap";
protocol_providers = mapAttrsToList (
n: v: (tfRef "authentik_provider_ldap.${n}.id")
n: v: (tfRef "resource.authentik_provider_ldap.${n}.id")
) config.ldapApps;
};
@ -55,6 +61,7 @@ in
name = n;
client_id = n;
authorization_flow = authorizationFlow;
invalidation_flow = invalidationFlow;
}) config.oauthApps;
data.authentik_provider_oauth2_config = mapAttrs (n: v: {
provider_id = tfRef "resource.authentik_provider_oauth2.${n}.id";
@ -62,19 +69,23 @@ in
resource.authentik_provider_proxy = mapAttrs (n: v: {
name = n;
mode = "forward-single";
mode = "forward_single";
external_host = v.externalHost;
authorization_flow = authorizationFlow;
invalidation_flow = invalidationFlow;
}) config.proxyApps;
resource.authentik_provider_ldap = mapAttrs (n: v: {
name = n;
base_dn = "dc=ldap,dc=goauthentik,dc=io";
bind_flow = authenticationFlow;
unbind_flow = invalidationFlow;
}) config.ldapApps;
output =
(mapAttrs' (
n: v:
nameValuePair ("${n}_environment") ({
sensitive = true;
value =
let
val = val: tfRef "resource.authentik_provider_oauth2.${n}.${val}";
@ -90,10 +101,39 @@ in
})
) config.oauthApps)
// {
proxy_config.value = tfRef "resource.authentik_outpost.proxy.config";
ldap_config.value = tfRef "resource.authentik_outpost.ldap.config";
proxy_config.sensitive = true;
proxy_config.value = ''
AUTHENTIK_HOST=http://localhost:9000
AUTHENTIK_HOST_BROWSER=${config.url}
AUTHENTIK_TOKEN=${tfRef "resource.authentik_token.proxy_outpost.key"}
'';
ldap_config.sensitive = true;
ldap_config.value = ''
AUTHENTIK_HOST=http://localhost:9000
AUTHENTIK_HOST_BROWSER=${config.url}
AUTHENTIK_TOKEN=${tfRef "resource.authentik_token.ldap_outpost.key"}
'';
};
data.authentik_user."proxy" = {
username = "ak-outpost-${tfRef ''replace(resource.authentik_outpost.proxy.id,"-","")''}";
};
data.authentik_user."ldap" = {
username = "ak-outpost-${tfRef ''replace(resource.authentik_outpost.ldap.id,"-","")''}";
};
resource.authentik_token."proxy_outpost" = {
identifier = "proxy-outpost-token";
user = tfRef "data.authentik_user.proxy.id";
expiring = false;
retrieve_key = true;
};
resource.authentik_token."ldap_outpost" = {
identifier = "ldap-outpost-token";
user = tfRef "data.authentik_user.ldap.id";
expiring = false;
retrieve_key = true;
};
resource.authentik_application = mkMerge [
(mapAttrs (n: v: genApp (tfRef "authentik_provider_oauth2.${n}.id") n v) config.oauthApps)
(mapAttrs (n: v: genApp (tfRef "authentik_provider_proxy.${n}.id") n v) config.proxyApps)
@ -122,7 +162,7 @@ in
let
genEnts =
apps:
lib.flatten (
flatten (
mapAttrsToList (
n: v:
(map (g: {
@ -135,10 +175,6 @@ in
) apps
);
in
mkMerge [
(genEnts config.oauthApps)
(genEnts config.proxyApps)
(genEnts config.ldapApps)
];
mkMerge ((genEnts config.oauthApps) ++ (genEnts config.proxyApps) ++ (genEnts config.ldapApps));
};
}

View file

@ -0,0 +1,30 @@
{
name = "authentik";
nodes.machine = {lib, config, pkgs, ...}: {
virtualisation = {
cores = 3;
memorySize = 2048;
};
xyno.services.authentik = {
enable = true;
url = "http://[::1]:9000";
oauthApps."app1".name = "app1-name";
ldapApps."app2".name = "app2-name";
proxyApps."app3".name = "app3-name";
proxyApps."app3".externalHost = "https://test.example.org";
};
services.authentik.environmentFile = lib.mkForce "/etc/authentik-env";
sops.defaultSopsFile = ../../../secrets/common.yaml;
environment.etc."authentik-env".text = ''
AUTHENTIK_SECRET_KEY=db6363ba033b111c6835489c5c0aca5ec192c20ee1ea3116a601065c3b118c3d
AUTHENTIK_BOOTSTRAP_TOKEN=75c041b7e79aac71ff0a74f7905c9f818ab2f0d21d8618fa5fd753f9e57a02f8
'';
sops.secrets."authentik/env" = lib.mkForce { sopsFile = ../../../instances/ds9/secrets/authentik.yaml; };
};
testScript = ''
machine.wait_for_unit("authentik-config.service")
print(machine.succeed("cat /run/authentik-terraform-config/app1_environment"))
machine.wait_for_unit("authentik-proxy.service")
machine.wait_for_unit("authentik-ldap.service")
'';
}

View file

@ -57,16 +57,22 @@ in
type = str;
default = "";
};
hosts = attrsOf (submodule {
options = {
extraConfig = mkOption { type = lines; };
};
});
hosts = mkOption {
default = {};
type = attrsOf (submodule {
options = {
extraConfig = mkOption { type = lines; };
};
});
};
};
});
};
config = lib.mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [ 80 443 ];
networking.firewall.allowedTCPPorts = [
80
443
];
networking.firewall.allowedUDPPorts = [ 443 ];
services.caddy = {
enable = true;
@ -78,7 +84,6 @@ in
metrics {
per_host
}
admin ${config.xyno.monitoring.ip}:2019
'';
extraConfig = ''
(blockBots) {

View file

@ -30,10 +30,10 @@ let
(lib.optional (c?publicHostname) {
# if peer is publicly on the internet
AllowedIPs =
(lib.optionals (c.wg.server) [
(lib.optionals ( c?wg.server && c.wg.server) [
"${ulaPrefix}::/48" # all traffic in the ula shall be sent to the server
])
++ (lib.optionals (!c.wg.server) [
++ (lib.optionals (!c?wg.server || !c.wg.server) [
"${genUlaForHost ulaPrefix c.hostName}/128" # if a host is reachable but shouldn't play server, send only to the hosts ip
]);
Endpoint = "${c.publicHostname}:51820";
@ -90,9 +90,15 @@ in
name = (genUlaForHost monitoringUlaPrefix v.hostName);
}
) instanceConfigs);
networking.firewall.allowedUDPPorts = lib.optional wgServer [ 51820 ];
networking.firewall.interfaces."wg0".allowedUDPPorts = lib.optional wgServer [ 53 ];
networking.firewall.allowedUDPPorts = lib.optional wgServer 51820;
networking.firewall.interfaces."wg0".allowedUDPPorts = lib.optional wgServer 53;
systemd.network.netdevs."wg0" = {
netdevConfig = {
Name = "wg0";
Kind = "wireguard";
Description = "main wireguard tunnel";
};
wireguardConfig = {
ListenPort = lib.mkIf wgServer 51820;
PrivateKeyFile = config.sops.secrets."wg/privkey".path; # TODO

View file

@ -46,6 +46,7 @@ in
"/var/log"
"/var/lib/systemd/coredump"
"/etc/ssh" # host keys
"/var/lib/sbctl" # lanzaboote
];
xyno.impermanence.user.directories = [
@ -72,6 +73,7 @@ in
".local/share/direnv"
];
sops.gnupg.sshKeyPaths = [ "/persistent/etc/ssh/ssh_host_rsa_key" ];
xyno.impermanence.cache.directories = [ "/var/cache" ];
xyno.impermanence.cache.user.directories = [ ".cache" ];
environment.persistence."/persistent" = genImpermanenceCfg cfg;

3
modules/test-list.nix Normal file
View file

@ -0,0 +1,3 @@
[
./services/authentik/test.nix
]