{ config, lib, pkgs, ... }: let cfg = config.sydnix.deertopia.authelia; in { options.sydnix.deertopia.authelia = { enable = lib.mkEnableOption ''Deertopia's Authelia''; httpPort = lib.mkOption { description = '' The port on which Authelia's web UI will be served. ''; type = lib.types.port; default = 9091; }; stateDirectory = lib.mkOption { description = '' The directory under which Authelia's general state will be stored. ''; type = lib.types.path; default = "/var/lib/authelia-deertopia"; }; bindUserName = lib.mkOption { description = '' The name of the LDAP user Authelia will bind as. ''; type = lib.types.str; default = "authelia"; }; }; config = lib.mkIf cfg.enable { sydnix.sops.secrets = let e = { mode = "0600"; owner = config.services.authelia.instances."deertopia".user; group = config.services.authelia.instances."deertopia".group; }; in { authelia-jwt-secret = e; authelia-session-secret = e; authelia-storage-encryption-key = e; authelia-authentication-backend-ldap-password = e; }; # I don't think the Authelia NixOS module exposes or even creates any paths # for the service's state. No big deal, we'll do it ourselves… # # It is obligatory that I mention tmpfiles.d(5) every time this setting is used. systemd.tmpfiles.settings."10-authelia".${cfg.stateDirectory} = { v.user = config.services.authelia.instances."deertopia".user; v.group = config.services.authelia.instances."deertopia".group; }; # See: # - https://github.com/authelia/authelia/blob/v4.38.19/config.template.yml # - https://matwick.ca/authelia-nginx-sso/ # - https://www.gandalfk7.it/posts/20220713_01_sso-with-lldap-authelia-and-nginx/ services.authelia.instances."deertopia" = { enable = true; # "Automatic" secrets are seemingly broken and offer little more than # some assertions from the Nix module. secrets.manual = true; environmentVariables = { AUTHELIA_JWT_SECRET_FILE = "/run/secrets/authelia-jwt-secret"; AUTHELIA_SESSION_SECRET_FILE = "/run/secrets/authelia-session-secret"; AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE = "/run/secrets/authelia-storage-encryption-key"; AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = "/run/secrets/authelia-authentication-backend-ldap-password"; }; settings = { default_2fa_method = "totp"; theme = "auto"; server = { address = "tcp://:${builtins.toString cfg.httpPort}"; asset_path = "${cfg.stateDirectory}/assets"; # Necessary for Nginx integration. No, I do not understand what it # does. endpoints.authz.auth-request.implementation = "AuthRequest"; }; authentication_backend = let base-dn = config.services.lldap.settings.ldap_base_dn; ldap-port = builtins.toString config.services.lldap.settings.ldap_port; in { password_reset.disable = false; refresh_interval = "1 minutes"; ldap = { implementation = "custom"; address = "ldap://127.0.0.1:${ldap-port}"; timeout = "5s"; start_tls = "false"; base_dn = base-dn; additional_users_dn = "ou=people"; additional_groups_dn = "ou=groups"; groups_filter = "(member={dn})"; users_filter = "(&({username_attribute}={input})(objectClass=person))"; attributes = { username = "uid"; group_name = "cn"; mail = "mail"; display_name = "displayName"; }; user = "uid=${cfg.bindUserName},ou=people,${base-dn}"; }; }; access_control = { default_policy = "deny"; rules = [ { # TODO: Remove this. It's only used for a quick demo for myself. # The domain choice is arbitrary. It's just one I happen to have # set up. domain = "ldap.deertopia.net"; policy = "one_factor"; } ]; }; session = { name = "authelia_session"; same_site = "lax"; inactivity = "5 minutes"; expiration = "1 hour"; remember_me = "1 month"; cookies = [ { domain = "deertopia.net"; authelia_url = "https://auth.deertopia.net"; # TODO: Remove this. It's only used for a quick demo for myself. # The domain choice is arbitrary. It's just one I happen to have # set up. default_redirection_url = "https://ldap.deertopia.net"; } ]; }; storage.local.path = "${cfg.stateDirectory}/db.sqlite"; notifier = { disable_startup_check = false; filesystem.filename = "${cfg.stateDirectory}/notifications"; }; # Default is false, which prevents anything from showing up when you run # `systemctl status authelia-deertopia`, which is really, really confusing. log = { keep_stdout = true; file_path = "${cfg.stateDirectory}/authelia.log"; }; }; }; sydnix.deertopia.nginx.vhosts."auth".vhost = { forceSSL = true; enableACME = true; extraConfig = '' set $upstream http://127.0.0.1:${builtins.toString cfg.httpPort}; ''; locations."/".extraConfig = '' include ${./authelia/proxy.conf}; proxy_pass $upstream; ''; locations."/api/verify".proxyPass = "$upstream"; locations."/api/authz".proxyPass = "$upstream"; }; # TODO: Remove this. It's only used for a quick demo for myself. The # domain choice is arbitrary. It's just one I happen to have set up. sydnix.deertopia.nginx.vhosts."ldap" = { directory = null; vhost = { forceSSL = true; enableACME = true; extraConfig = '' include ${./authelia/authelia-location.conf}; ''; locations."/".extraConfig = '' include ${./authelia/authelia-authrequest.conf}; include ${./authelia/proxy.conf}; root /persist/deertopia.net/ldap; ''; }; }; }; }