From a29fcde32d95c1237007a6a4daded81a693ad7b1 Mon Sep 17 00:00:00 2001 From: Madeleine Sydney Date: Fri, 21 Feb 2025 18:20:21 -0700 Subject: [PATCH] fix(authelia,lldap): Persist important state directories --- modules/nixos/deertopia/authelia.nix | 280 +++++++++++++-------------- modules/nixos/deertopia/lldap.nix | 4 + 2 files changed, 139 insertions(+), 145 deletions(-) diff --git a/modules/nixos/deertopia/authelia.nix b/modules/nixos/deertopia/authelia.nix index 1d83e3d..873eda6 100644 --- a/modules/nixos/deertopia/authelia.nix +++ b/modules/nixos/deertopia/authelia.nix @@ -11,13 +11,6 @@ in { 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. @@ -27,157 +20,154 @@ in { }; }; - 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; - }; + config = lib.mkIf cfg.enable + (let authelia-state-dir = "/var/lib/authelia-deertopia"; + in { + 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; - }; + sydnix.impermanence.directories = [ + authelia-state-dir + ]; - # 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"; - }; - }; - }; + # 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 = "${authelia-state-dir}/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 = "${authelia-state-dir}/db.sqlite"; + notifier = { + disable_startup_check = false; + filesystem.filename = "${authelia-state-dir}/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 = "${authelia-state-dir}/authelia.log"; + }; + }; + }; - sydnix.deertopia.nginx.vhosts."auth".vhost = { - forceSSL = true; - enableACME = true; - extraConfig = '' + sydnix.deertopia.nginx.vhosts."auth".vhost = { + forceSSL = true; + enableACME = true; + extraConfig = '' set $upstream http://127.0.0.1:${builtins.toString cfg.httpPort}; ''; - locations."/".extraConfig = '' + locations."/".extraConfig = '' include ${./authelia/proxy.conf}; proxy_pass $upstream; ''; - locations."/api/verify".proxyPass = "$upstream"; - locations."/api/authz".proxyPass = "$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 = '' + # 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 = '' + locations."/".extraConfig = '' include ${./authelia/authelia-authrequest.conf}; include ${./authelia/proxy.conf}; root /persist/deertopia.net/ldap; ''; - }; - }; - }; + }; + }; + }); } diff --git a/modules/nixos/deertopia/lldap.nix b/modules/nixos/deertopia/lldap.nix index de2b4d0..5cf07bd 100644 --- a/modules/nixos/deertopia/lldap.nix +++ b/modules/nixos/deertopia/lldap.nix @@ -18,6 +18,10 @@ in { }; users.groups.lldap = {}; + sydnix.impermanence.directories = [ + "/var/lib/private/lldap" + ]; + sydnix.sops.secrets = let e = { mode = "0440";