WIP: feat(authelia): Add authentication with authelia

This commit is contained in:
Tristan Daniël Maat 2025-05-24 07:26:11 +08:00
parent 94ec261a94
commit 27f28457b2
Signed by: tlater
GPG key ID: 49670FD774E43268
9 changed files with 256 additions and 9 deletions

View file

@ -13,6 +13,7 @@
"${modulesPath}/profiles/minimal.nix"
(import ../modules)
./services/auth
./services/backups.nix
./services/battery-manager.nix
./services/conduit

View file

@ -0,0 +1,93 @@
{ config, ... }:
{
systemd.services.authelia-tlaternet.after = [ config.systemd.services.lldap-provisioning.name ];
services = {
authelia.instances.tlaternet = {
enable = true;
environmentVariables.AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE =
config.sops.secrets."authelia/lldap-password".path;
settings = {
authentication_backend.ldap =
let
cfglldap = config.services.lldap.settings;
in
{
# TODO(tlater): Enable when authelia has a webhook notifier:
# https://github.com/authelia/authelia/issues/7695
password_reset.disable = true;
refresh_interval = "1m";
address = "ldap://${cfglldap.ldap_host}:${toString cfglldap.ldap_port}";
implementation = "lldap";
base_dn = cfglldap.ldap_base_dn;
user = "cn=authelia,ou=people,${cfglldap.ldap_base_dn}";
};
password_policy.zxcvbn.enabled = true;
telemetry.metrics.enabled = true;
access_control = {
default_policy = "deny";
rules = [
{
domain = "*.${config.services.nginx.domain}";
policy = "one_factor";
}
];
};
notifier.filesystem.filename = "/var/lib/authelia-tlaternet/notification.txt";
session = {
cookies = [
{
domain = "${config.services.nginx.domain}";
authelia_url = "https://auth.${config.services.nginx.domain}";
}
];
redis.host = config.services.redis.servers.authelia.unixSocket;
};
storage = {
postgres = {
address = "/var/run/postgresql";
username = config.services.authelia.instances.tlaternet.user;
database = config.services.authelia.instances.tlaternet.user;
};
};
# Auth options
default_2fa_method = "totp";
totp.issuer = "tlater.net";
webauthn = {
display_name = "tlater.net";
enable_passkey_login = true;
attestation_conveyance_preference = "direct";
filtering.prohibit_backup_eligibility = true;
metadata = {
enabled = true;
validate_trust_anchor = true;
validate_entry = true;
validate_status = true;
validate_entry_permit_zero_aaguid = false;
};
};
duo_api.disable = true;
};
secrets = {
storageEncryptionKeyFile = config.sops.secrets."authelia/storage-encryption-key".path;
jwtSecretFile = config.sops.secrets."authelia/jwt-secret".path;
sessionSecretFile = config.sops.secrets."authelia/session-secret".path;
};
};
redis.servers.authelia = {
enable = true;
user = config.services.authelia.instances.tlaternet.user;
};
};
}

View file

@ -0,0 +1,6 @@
{
imports = [
./authelia.nix
./lldap.nix
];
}

View file

@ -0,0 +1,44 @@
#!/usr/bin/env nushell
let groups = [{
}]
let users = [
]
let settings = open $env.LLDAP_CONFIG
let url = (
'http://' |
$in + ($settings | get http_host | default '127.0.0.1') |
$in + ':' |
$in + ($settings | get http_port | default '17170' | into string))
let user = $settings | get ldap_user_dn | default admin
let pass = open $env.LLDAP_LDAP_USER_PASS_FILE
let token = { username: $user, password: $pass } | to json | http post $'($url)/auth/simple/login' | get token
def query [operation: string, query: string, variables: list<string>] {
let body = {
query: $query,
operationName: $operation,
variables: $variables
}
let res = $body | to json | http post --headers [Authorization $'Bearer ($token)'] $'($url)/api/graphql'
if ("errors" in $res) {
let msg = "GraphQL query to LLDAP failed:\n" + ($res.errors | each {|e| $'- ($e)' | str join (char newline)})
error make {
msg: $msg,
label: {
text: "Query defined here",
span: (metadata $query).span
}
}
} else {
$res.data
}
}

View file

@ -0,0 +1,64 @@
{
lib,
pkgs,
config,
...
}:
{
services = {
lldap = {
enable = true;
settings = {
ldap_base_dn = "dc=tlater,dc=net";
database_url = "postgres://lldap:@localhost/lldap?host=/var/run/postgresql";
ldap_host = "127.0.0.1";
http_host = "127.0.0.1";
http_url = "https://lldap.${config.services.nginx.domain}";
force_ldap_user_pass_reset = "always";
smtp_options.enable_password_reset = false;
environment = {
LLDAP_JWT_SECRET_FILE = config.sops.secrets."authelia/jwt-secret".path;
LLDAP_LDAP_USER_PASS_FILE = config.sops.secrets."lldap/admin-password".path;
LLDAP_KEY_SEED_FILE = config.sops.secrets."lldap/key".path;
};
};
};
nginx.virtualHosts = {
"lldap.${config.services.nginx.domain}" = {
useACMEHost = "tlater.net";
forceSSL = true;
enableHSTS = true;
locations."/".proxyPass =
"http://${config.services.lldap.settings.http_host}:${toString config.services.lldap.settings.http_port}";
};
};
};
systemd.services.lldap.after = [ config.systemd.services.postgresql.name ];
systemd.services.lldap-provisioning = {
requisite = [ config.systemd.services.lldap.name ];
wantedBy = [ config.systemd.services.lldap.name ];
after = [ config.systemd.services.lldap.name ];
path = [
pkgs.nushell
pkgs.lldap-cli
];
script = "exec nu ${./lldap-provisioning.nu}";
environment = {
LLDAP_LDAP_USER_PASS_FILE = config.sops.secrets."lldap/admin-password".path;
# LLDAP_CONFIG = ((pkgs.formats.toml { }).generate config.services.lldap.settings).outPath;
};
serviceConfig.Type = "oneshot";
};
}

View file

@ -9,6 +9,10 @@ in
extraOptions = [ "-storage.minFreeDiskSpaceBytes=5GB" ];
scrapeConfigs = {
authelia = {
targets = [ "127.0.0.1:9959" ];
};
forgejo = {
targets = [ "127.0.0.1:${toString config.services.forgejo.settings.server.HTTP_PORT}" ];
extraSettings.authorization.credentials_file = config.sops.secrets."forgejo/metrics-token".path;

View file

@ -1,4 +1,4 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
services.postgresql = {
package = pkgs.postgresql_14;
@ -17,10 +17,18 @@
# that operation needs to be performed manually on the system as
# well.
ensureUsers = [
{
name = config.services.authelia.instances.tlaternet.user;
ensureDBOwnership = true;
}
{
name = "grafana";
ensureDBOwnership = true;
}
{
name = "lldap";
ensureDBOwnership = true;
}
{
name = "nextcloud";
ensureDBOwnership = true;
@ -28,7 +36,9 @@
];
ensureDatabases = [
config.services.authelia.instances.tlaternet.user
"grafana"
"lldap"
"nextcloud"
];
};

View file

@ -3,6 +3,24 @@
defaultSopsFile = ../keys/production.yaml;
secrets = {
"authelia/storage-encryption-key" = {
owner = "authelia-tlaternet";
group = "authelia-tlaternet";
};
"authelia/jwt-secret" = {
owner = "authelia-tlaternet";
group = "authelia-tlaternet";
};
"authelia/session-secret" = {
owner = "authelia-tlaternet";
group = "authelia-tlaternet";
};
"authelia/lldap-password" = {
owner = "authelia-tlaternet";
group = "lldap";
mode = "0440";
};
"battery-manager/email" = { };
"battery-manager/password" = { };
@ -28,6 +46,10 @@
"heisenbridge/as-token" = { };
"heisenbridge/hs-token" = { };
# lldap
"lldap/admin-password" = { };
"lldap/key" = { };
# Matrix-hookshot
"matrix-hookshot/as-token" = { };
"matrix-hookshot/hs-token" = { };