diff --git a/common/services/lldap.nix b/common/services/lldap.nix new file mode 100644 index 0000000..e71bf9b --- /dev/null +++ b/common/services/lldap.nix @@ -0,0 +1,75 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib) mkEnableOption types mkIf mkOption; + + cfg = config.foehammer.services.lldap; +in { + options.foehammer.services.lldap = { + enable = mkEnableOption "Enable LLDAP Server"; + + url = mkOption { + type = types.str; + }; + + port = mkOption { + type = lib.types.port; + default = 8226; + description = '' + What external port to serve over. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + }; + + jwtSecretFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to your JWT secret used during identity verificaton. + ''; + }; + + adminUserPasswordFile = mkOption { + type = types.nullOr types.path; + default = null; + }; + + base_dn = mkOption { + type = types.str; + example = "dc=example,dc=com"; + }; + }; + + config = mkIf cfg.enable { + services.lldap = { + enable = true; + environmentFile = cfg.environmentFile; + + settings = { + # Base setup. + http_port = cfg.port; + http_url = cfg.url; + ldap_base_dn = cfg.base_dn; + jwt_secret_file = cfg.jwtSecretFile; + + # Reproducable admin password. + force_ldap_user_pass_reset = "always"; + ldap_user_pass_file = cfg.adminUserPasswordFile; + }; + }; + + users.users.lldap = { + isSystemUser = true; + createHome = true; + group = "lldap"; + }; + users.groups.lldap = {}; + }; +} diff --git a/machines/lebesgue/config/configuration.nix b/machines/lebesgue/config/configuration.nix index 1c1811c..ddf9a9b 100644 --- a/machines/lebesgue/config/configuration.nix +++ b/machines/lebesgue/config/configuration.nix @@ -33,6 +33,14 @@ storageEncryptionKeyFile = config.sops.secrets.authelia-storage-encryption.path; }; + services.lldap = { + enable = true; + url = "https://lldap.foehammer.me"; + base_dn = "dc=foehammer,dc=me"; + + adminUserPasswordFile = config.sops.secrets.lldap-admin-password.path; + }; + services.vaultwarden = { enable = true; domain = "https://passwords.foehammer.me"; diff --git a/machines/lebesgue/config/secrets.nix b/machines/lebesgue/config/secrets.nix index f8538b9..2cbf4d2 100644 --- a/machines/lebesgue/config/secrets.nix +++ b/machines/lebesgue/config/secrets.nix @@ -18,6 +18,8 @@ restic-password = {owner = "restic";}; restic-repository = {owner = "restic";}; + lldap-admin-password.owner = "lldap"; + authelia-jwtsecret = autheliaSecret; authelia-oidc-privkey = autheliaSecret; authelia-oidc-hmac = autheliaSecret; diff --git a/machines/lebesgue/config/state.nix b/machines/lebesgue/config/state.nix index 26efd42..f593234 100644 --- a/machines/lebesgue/config/state.nix +++ b/machines/lebesgue/config/state.nix @@ -13,6 +13,8 @@ "/var/lib/authelia-main" "/var/lib/caddy/.local/share/caddy" "/var/lib/vaultwarden" + + { directory = "/var/lib/private/lldap"; user = "lldap"; group = "lldap"; mode = "0700"; } ]; files = [ diff --git a/machines/lebesgue/secrets/main.yaml b/machines/lebesgue/secrets/main.yaml index e7208b5..37ce6dd 100644 --- a/machines/lebesgue/secrets/main.yaml +++ b/machines/lebesgue/secrets/main.yaml @@ -4,6 +4,7 @@ vaultwarden-env: ENC[AES256_GCM,data:A1iRHxFxgI5P8DtsXQa1KvEKKnF+qZY7LVuJba00CLj restic-password: ENC[AES256_GCM,data:Ympe5/hJxOzJp7IeJy5mZy0fMIrnV+3cWJo1uKwbHHDJ0G4TNivMNrHEdff6CjVnAbkVgjkR90z1FJOpExd+KQ==,iv:CRJaA3fTG8B/qBDkwctgma4DaGDjoyk4eX6/SynIcLE=,tag:pJW45ijV+wVTR+4IRnLcsw==,type:str] restic-repository: ENC[AES256_GCM,data:KkFaam8iltY9nz89sVxk4u0xZ46Sq+7UsOY/9wieASD5A2FRruou7BiudX9X4hRA2RMTctO8aqYkrg==,iv:mIZ9z7BJV9s+wSiVMnzYAWM1/zsa6C+RCK1UhSiJVxI=,tag:S7tedxcfd/UaQ5hMEYfBVQ==,type:str] restic-env: ENC[AES256_GCM,data:KW9ma36zmHJF3xBStpoStDRQqg34wlMJMVSYfbLSnWq26R6e6eGf3+kTVkobhn/bqL6ZYi8ctlyvDS8IOz8VveYogsqxZ7/LK62mA0d9I3xEZMG7eNQ8M1PdeZ9RqAUgFJU=,iv:RxwvZ2vNuwmUc3haK2Ub8vHk9UQhjepLCwsfIcSJg9s=,tag:Tvq2RDh8mJ3jGhmpL1uuCA==,type:str] +lldap-admin-password: ENC[AES256_GCM,data:OUGtguGSBt7DXZ2eHZ3pq+NCHGFzM4HRSmAAUKulf0v5GGi1we9iMZb54ZFqn57W/t3+nesq/zMudt2Bry7wJQ==,iv:4Mop0qpSBzYnoeMPPoHHoDP1LgCwXW4vs+o6Pd2jRlM=,tag:t+nhS9uhvdibp6PuHMfPrA==,type:str] sops: age: - recipient: age1kjy9wym6cmz6wqmewws4ledsne47c0e4sr0ksmm66rff3u2f6u3qxvnyg9 @@ -15,8 +16,8 @@ sops: L2VhMXV4WityYUFDZytxVTJHOXZGVVkKgbKR56dsru6U7I4KpnxfxQsswFwJsTM7 8dzAaFl30mdRwFIH9kzdY3XxyYsJ0Yr0x3xwJ8mI4rjgpI8S9ihJFw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-06-04T21:04:47Z" - mac: ENC[AES256_GCM,data:fGTVTDhqVNLQJaZyBFhBEauW/Cnb/V57aHOcaeODNeA9g1oZiC3IzUkpRVnEC+gPx4KLDrBwuCk7Au/TarVpFVK+nyqcwrDgr2RsWtVDP0UQH/+8G8PkASxnMnTp/oQnvEKGAbySfGelqEQkDhbMiR7GaP99lJcIoIQ/wG87peA=,iv:+NJnPQmh6VYzDu/UoGv1YHVGfMocKMdX5XxZG6FmS90=,tag:vnHzhvOQOw0U7BwNJKA0kw==,type:str] + lastmodified: "2026-01-01T02:28:46Z" + mac: ENC[AES256_GCM,data:S/pSDlf0DUWvOfBbf1xmtzh46nBKXzIKg/xyLvmah5EnsxlfrQhvhjN628HbexluXDWe3a4QAWv7i4awu+61zem41gpWCy8vJJbKBRnNKBnWEPvZKUJwLlLD7+B8jWttxaUlX/Vz/oyWZR35oD2D4c9Bx5MkzMqxb4w5M1DEzVo=,iv:jqL8NVjw3TK1Oue4Jgc5GXLezNDUm1bKuEBKI7zKhRY=,tag:SMoezaPc+iS2FqSqHHTP1w==,type:str] pgp: - created_at: "2025-02-03T18:58:54Z" enc: |- @@ -30,4 +31,4 @@ sops: -----END PGP MESSAGE----- fp: A972C2063F4F2554 unencrypted_suffix: _unencrypted - version: 3.10.2 + version: 3.11.0