235 lines
8.2 KiB
Nix
235 lines
8.2 KiB
Nix
{ config, lib, pkgs, homeyConfig, ... }:
|
|
|
|
# Authelia — SSO gateway.
|
|
#
|
|
# Connects to OpenLDAP on 127.0.0.1:389.
|
|
# Exposes port 9091 on localhost; Caddy reverse-proxies it and provides
|
|
# the forward_auth endpoint for protected vhosts.
|
|
#
|
|
# Volume layout:
|
|
# <dataDir>/authelia/config/ → /config (sqlite db, notification log, etc.)
|
|
#
|
|
# The configuration file is rendered by Nix (no Go templates) and written
|
|
# to a NixOS-managed path, then bind-mounted read-only into the container.
|
|
#
|
|
# Secrets consumed from sops:
|
|
# authelia/jwt_secret
|
|
# authelia/session_secret
|
|
# authelia/storage_encryption_key
|
|
# openldap/ro_password (shared with openldap module)
|
|
|
|
let
|
|
cfg = config.homey.authelia;
|
|
dataDir = config.homey.storage.mountPoint;
|
|
domain = homeyConfig.domain;
|
|
|
|
# LDAP base DN derived from domain: zakobar.com → dc=zakobar,dc=com
|
|
ldapBaseDN = lib.concatStringsSep ","
|
|
(map (p: "dc=${p}") (lib.splitString "." domain));
|
|
|
|
# The authelia config is written as a Nix string so all values are
|
|
# resolved at build time except for secrets, which are injected at
|
|
# runtime via a wrapper script (same pattern as openldap).
|
|
autheliaConfig = ''
|
|
###############################################################
|
|
# Authelia configuration #
|
|
# Generated by NixOS — do not edit by hand #
|
|
###############################################################
|
|
theme: "light"
|
|
log:
|
|
level: "info"
|
|
|
|
# jwt_secret injected at runtime via env var AUTHELIA_JWT_SECRET_FILE
|
|
authentication_backend:
|
|
ldap:
|
|
implementation: "custom"
|
|
url: "ldap://openldap:389"
|
|
timeout: "5s"
|
|
start_tls: false
|
|
base_dn: "${ldapBaseDN}"
|
|
users_filter: "({username_attribute}={input})"
|
|
username_attribute: "uid"
|
|
additional_users_dn: "ou=users"
|
|
groups_filter: "(&(uniquemember=uid={input},ou=users,${ldapBaseDN})(objectclass=groupOfUniqueNames))"
|
|
group_name_attribute: "cn"
|
|
additional_groups_dn: "ou=groups"
|
|
mail_attribute: "mail"
|
|
display_name_attribute: "uid"
|
|
permit_referrals: false
|
|
permit_unauthenticated_bind: false
|
|
user: "cn=readonly,${ldapBaseDN}"
|
|
# password injected at runtime via AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE
|
|
|
|
totp:
|
|
issuer: "${domain}"
|
|
disable: false
|
|
|
|
session:
|
|
name: authelia_session
|
|
# secret injected at runtime via AUTHELIA_SESSION_SECRET_FILE
|
|
expiration: 3600
|
|
inactivity: 7200
|
|
domain: "${domain}"
|
|
|
|
storage:
|
|
local:
|
|
path: "/config/db.sqlite3"
|
|
# encryption_key injected at runtime via AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE
|
|
|
|
access_control:
|
|
default_policy: "deny"
|
|
rules:
|
|
- domain:
|
|
- "auth.${domain}"
|
|
policy: "bypass"
|
|
- domain:
|
|
- "ldapadmin.${domain}"
|
|
subject:
|
|
- "group:admins"
|
|
policy: "two_factor"
|
|
- domain:
|
|
- "ldapadmin.${domain}"
|
|
policy: "deny"
|
|
- domain:
|
|
- "torrent.${domain}"
|
|
subject:
|
|
- "group:admins"
|
|
policy: "two_factor"
|
|
- domain:
|
|
- "torrent.${domain}"
|
|
policy: "deny"
|
|
- domain:
|
|
- "git.${domain}"
|
|
policy: "one_factor"
|
|
- domain:
|
|
- "nextcloud.${domain}"
|
|
policy: "one_factor"
|
|
- domain:
|
|
- "jellyfin.${domain}"
|
|
policy: "one_factor"
|
|
- domain:
|
|
- "uptime.${domain}"
|
|
subject:
|
|
- "group:admins"
|
|
policy: "two_factor"
|
|
- domain:
|
|
- "uptime.${domain}"
|
|
policy: "deny"
|
|
- domain:
|
|
- "grafana.${domain}"
|
|
subject:
|
|
- "group:admins"
|
|
policy: "two_factor"
|
|
- domain:
|
|
- "grafana.${domain}"
|
|
policy: "deny"
|
|
# ntfy: bypass — ntfy enforces its own token/password auth;
|
|
# the mobile app must be able to connect without Authelia SSO.
|
|
- domain:
|
|
- "ntfy.${domain}"
|
|
policy: "bypass"
|
|
|
|
notifier:
|
|
filesystem:
|
|
filename: "/config/emails.txt"
|
|
|
|
ntp:
|
|
address: "udp://time.cloudflare.com:123"
|
|
version: 3
|
|
max_desync: "3s"
|
|
disable_startup_check: false
|
|
disable_failure: true
|
|
'';
|
|
|
|
in
|
|
{
|
|
options.homey.authelia = {
|
|
enable = lib.mkEnableOption "Authelia SSO gateway";
|
|
|
|
image = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "docker.io/authelia/authelia:latest";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 9091;
|
|
description = "Host port Authelia listens on (bound to 127.0.0.1).";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# -----------------------------------------------------------------------
|
|
# Secrets
|
|
# -----------------------------------------------------------------------
|
|
sops.secrets."authelia/jwt_secret" = { owner = "root"; };
|
|
sops.secrets."authelia/session_secret" = { owner = "root"; };
|
|
sops.secrets."authelia/storage_encryption_key" = { owner = "root"; };
|
|
# openldap/ro_password is declared in openldap.nix; reference it here too
|
|
# (sops-nix deduplicates identical declarations)
|
|
sops.secrets."openldap/ro_password" = { owner = "root"; };
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Write the config file into /etc (read-only in the container)
|
|
# -----------------------------------------------------------------------
|
|
environment.etc."authelia/configuration.yml" = {
|
|
text = autheliaConfig;
|
|
mode = "0444";
|
|
};
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Container
|
|
# -----------------------------------------------------------------------
|
|
virtualisation.oci-containers.containers.authelia = {
|
|
image = cfg.image;
|
|
|
|
ports = [ "127.0.0.1:${toString cfg.port}:9091" ];
|
|
|
|
environment = {
|
|
TZ = homeyConfig.timezone;
|
|
# Tell authelia to read secrets from files (its native mechanism)
|
|
AUTHELIA_JWT_SECRET_FILE = "/run/secrets/jwt_secret";
|
|
AUTHELIA_SESSION_SECRET_FILE = "/run/secrets/session_secret";
|
|
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE = "/run/secrets/storage_encryption_key";
|
|
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = "/run/secrets/ldap_ro_password";
|
|
# Changing this forces a container restart when the config changes.
|
|
# NixOS bind-mounts resolve symlinks at container start, so the running
|
|
# container would otherwise keep the old nix-store config until restarted.
|
|
NIXOS_CONFIG_HASH = builtins.hashString "sha256" autheliaConfig;
|
|
};
|
|
|
|
volumes = [
|
|
"/etc/authelia/configuration.yml:/config/configuration.yml:ro"
|
|
"${dataDir}/authelia/config:/config"
|
|
# Mount sops secret files into the container under /run/secrets/
|
|
"${config.sops.secrets."authelia/jwt_secret".path}:/run/secrets/jwt_secret:ro"
|
|
"${config.sops.secrets."authelia/session_secret".path}:/run/secrets/session_secret:ro"
|
|
"${config.sops.secrets."authelia/storage_encryption_key".path}:/run/secrets/storage_encryption_key:ro"
|
|
"${config.sops.secrets."openldap/ro_password".path}:/run/secrets/ldap_ro_password:ro"
|
|
];
|
|
|
|
extraOptions = [
|
|
"--network=homey"
|
|
"--hostname=authelia"
|
|
];
|
|
};
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Systemd — wait for openldap and external HD
|
|
# -----------------------------------------------------------------------
|
|
systemd.services."podman-authelia" = {
|
|
after = lib.mkAfter [ "mnt-data.mount" "podman-openldap.service" "podman-homey-network.service" ];
|
|
requires = lib.mkAfter [ "mnt-data.mount" "podman-openldap.service" "podman-homey-network.service" ];
|
|
};
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Uptime Kuma monitor for this service
|
|
# -----------------------------------------------------------------------
|
|
homey.monitoring.monitors = [{
|
|
name = "Authelia";
|
|
url = "https://auth.${domain}/api/health";
|
|
interval = 60;
|
|
}];
|
|
};
|
|
}
|