at 25.11-pre 4.7 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 utils, 6 ... 7}: 8 9let 10 cfg = config.services.lldap; 11 format = pkgs.formats.toml { }; 12in 13{ 14 options.services.lldap = with lib; { 15 enable = mkEnableOption "lldap, a lightweight authentication server that provides an opinionated, simplified LDAP interface for authentication"; 16 17 package = mkPackageOption pkgs "lldap" { }; 18 19 environment = mkOption { 20 type = with types; attrsOf str; 21 default = { }; 22 example = { 23 LLDAP_JWT_SECRET_FILE = "/run/lldap/jwt_secret"; 24 LLDAP_LDAP_USER_PASS_FILE = "/run/lldap/user_password"; 25 }; 26 description = '' 27 Environment variables passed to the service. 28 Any config option name prefixed with `LLDAP_` takes priority over the one in the configuration file. 29 ''; 30 }; 31 32 environmentFile = mkOption { 33 type = types.nullOr types.path; 34 default = null; 35 description = '' 36 Environment file as defined in {manpage}`systemd.exec(5)` passed to the service. 37 ''; 38 }; 39 40 settings = mkOption { 41 description = '' 42 Free-form settings written directly to the `lldap_config.toml` file. 43 Refer to <https://github.com/lldap/lldap/blob/main/lldap_config.docker_template.toml> for supported values. 44 ''; 45 46 default = { }; 47 48 type = types.submodule { 49 freeformType = format.type; 50 options = { 51 ldap_host = mkOption { 52 type = types.str; 53 description = "The host address that the LDAP server will be bound to."; 54 default = "::"; 55 }; 56 57 ldap_port = mkOption { 58 type = types.port; 59 description = "The port on which to have the LDAP server."; 60 default = 3890; 61 }; 62 63 http_host = mkOption { 64 type = types.str; 65 description = "The host address that the HTTP server will be bound to."; 66 default = "::"; 67 }; 68 69 http_port = mkOption { 70 type = types.port; 71 description = "The port on which to have the HTTP server, for user login and administration."; 72 default = 17170; 73 }; 74 75 http_url = mkOption { 76 type = types.str; 77 description = "The public URL of the server, for password reset links."; 78 default = "http://localhost"; 79 }; 80 81 ldap_base_dn = mkOption { 82 type = types.str; 83 description = "Base DN for LDAP."; 84 example = "dc=example,dc=com"; 85 }; 86 87 ldap_user_dn = mkOption { 88 type = types.str; 89 description = "Admin username"; 90 default = "admin"; 91 }; 92 93 ldap_user_email = mkOption { 94 type = types.str; 95 description = "Admin email."; 96 default = "admin@example.com"; 97 }; 98 99 database_url = mkOption { 100 type = types.str; 101 description = "Database URL."; 102 default = "sqlite://./users.db?mode=rwc"; 103 example = "postgres://postgres-user:password@postgres-server/my-database"; 104 }; 105 }; 106 }; 107 }; 108 }; 109 110 config = lib.mkIf cfg.enable { 111 systemd.services.lldap = { 112 description = "Lightweight LDAP server (lldap)"; 113 wants = [ "network-online.target" ]; 114 after = [ "network-online.target" ]; 115 wantedBy = [ "multi-user.target" ]; 116 # lldap defaults to a hardcoded `jwt_secret` value if none is provided, which is bad, because 117 # an attacker could create a valid admin jwt access token fairly trivially. 118 # Because there are 3 different ways `jwt_secret` can be provided, we check if any one of them is present, 119 # and if not, bootstrap a secret in `/var/lib/lldap/jwt_secret_file` and give that to lldap. 120 script = 121 lib.optionalString (!cfg.settings ? jwt_secret) '' 122 if [[ -z "$LLDAP_JWT_SECRET_FILE" ]] && [[ -z "$LLDAP_JWT_SECRET" ]]; then 123 if [[ ! -e "./jwt_secret_file" ]]; then 124 ${lib.getExe pkgs.openssl} rand -base64 -out ./jwt_secret_file 32 125 fi 126 export LLDAP_JWT_SECRET_FILE="./jwt_secret_file" 127 fi 128 '' 129 + '' 130 ${lib.getExe cfg.package} run --config-file ${format.generate "lldap_config.toml" cfg.settings} 131 ''; 132 serviceConfig = { 133 StateDirectory = "lldap"; 134 StateDirectoryMode = "0750"; 135 WorkingDirectory = "%S/lldap"; 136 UMask = "0027"; 137 User = "lldap"; 138 Group = "lldap"; 139 DynamicUser = true; 140 EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; 141 }; 142 inherit (cfg) environment; 143 }; 144 }; 145}