# TODO: it would be nice to have an abstraction layer that deals with long # lines for us, analogous to the one we have for haproxy. it's not immediately # clear to me what the best design for that is; ideally we could use it with # any olcAccess section. it's not a need that's likely to arise with any # section but those, although in principle it could. # # TODO: this is not hooked up to anything yet; when you feel it's ready to # turn on, add it to the list of imported modules in flake.nix # # TODO: once we have all { lib, pkgs, ... }: { imports = [ ]; services.openldap = { enable = true; urlList = [ "ldap:///" ]; mutableConfig = false; settings = { attrs = { olcLogLevel = "config stats"; olcAuthzPolicy = "to"; }; children = { # TODO: There are a lot of different "person" types, and we should # really pick one. It is my firm political belief that first name, # last name, and display name should all be optional. Also, for our # needs organizationally, since we have both internal and external # users, sometimes we want to have uid and sometimes we want to have # email and sometimes we want both. I don't think any of the existing # schemas do this, but feel free to take a look and see how close we # can get. If we decide it's time to make our own, I'll register an # ISL private enterprise number, but it's bureaucracy I'd prefer to # skip if we can. "cn=schema".includes = [ # TODO: We probably don't need all these schemas. "${pkgs.openldap}/etc/schema/core.ldif" "${pkgs.openldap}/etc/schema/cosine.ldif" "${pkgs.openldap}/etc/schema/inetorgperson.ldif" "${pkgs.openldap}/etc/schema/nis.ldif" ]; "olcDatabase={-1}frontend".attrs = { objectClass = [ "olcDatabaseConfig" "olcFrontendConfig" ]; olcDatabase = "{-1}frontend"; # Otherwise the default is the root, which is not world-readable. olcDefaultSearchBase = "dc=internetsafetylabs,dc=org"; # This is the access control list that pertains to configuration # settings. This needs to be set explicitly or else you can't check # what the settings are at runtime, and user-management portals # benefit from being able to. olcAccess = [ '' {0}to dn.base="" by * read '' '' {1}to dn.base="cn=subschema" by * read '' '' {2}to * by group.exact="cn=ldap-admins,ou=groups,dc=internetsafetylabs,dc=org" read by * none '' ]; }; "olcDatabase={0}config".attrs = { objectClass = [ "olcDatabaseConfig" ]; olcDatabase = "{0}config"; # These things were in the default setup, before we did anything # to explicitly set up the config database. Setting up the olcAccess # values overrode them, so here they are in case they're important. # They haven't been the subject of much thought. olcAddContentAcl = "TRUE"; olcLastMod = "TRUE"; olcLastBind = "FALSE"; olcLastBindPrecision = "0"; olcMaxDerefDepth = "15"; olcReadOnly = "FALSE"; olcSyncUseSubentry = "FALSE"; olcMonitoring = "FALSE"; olcAccess = [ '' {0}to dn.subtree="cn=config" by group.exact="cn=ldap-admins,ou=groups,dc=internetsafetylabs,dc=org" read by group.exact="cn=ldap-frontends,ou=groups,dc=internetsafetylabs,dc=org" read by * none '' '' {1}to * by * none '' ]; }; "olcDatabase={1}mdb" = { attrs = { objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ]; olcDatabase = "{1}mdb"; olcDbDirectory = "/var/lib/openldap/data"; olcSuffix = "dc=internetsafetylabs,dc=org"; # This is needed because the memberof overlay has to do its # changes as a DN. olcRootDN = "cn=admin,dc=internetsafetylabs,dc=org"; # This should probably be commented out when there's nothing # horrible going on. It's important for bootstrapping and for # recovering broken situations, but it's a security risk. # # The important property of logins that use the root DN and # password is that logging in that way ignores all ACLs and also # the account doesn't have to actually exist in the database. # # TODO: we might consider adding some sort of auto-expiration # feature to the secret manager? #olcRootPW = builtins.readFile # "/etc/nixos/secrets/openldap/root-password"; # TODO: once we have better formatting for this code (see the TODO # about an abstraction layer for long lines), the thinking behind # each individual rule should be documented olcAccess = [ '' {0}to attrs=userPassword by self =xw by group.exact="cn=ldap-admins,ou=groups,dc=internetsafetylabs,dc=org" =xw by group.exact="cn=ldap-password-managers,ou=groups,dc=internetsafetylabs,dc=org" =xw by anonymous auth by * none '' '' {1}to attrs=authzTo by group.exact="cn=ldap-frontends,ou=groups,dc=internetsafetylabs,dc=org" auth by group.exact="cn=ldap-admins,ou=groups,dc=internetsafetylabs,dc=org" write by * none '' '' {2}to attrs=memberOf by group.exact="cn=ldap-admins,ou=groups,dc=internetsafetylabs,dc=org" read by self read by * none '' '' {3}to dn.subtree="dc=internetsafetylabs,dc=org" by self write by group.exact="cn=ldap-admins,ou=groups,dc=internetsafetylabs,dc=org" write by users read by * auth '' '' {4}to * by self write by users read by * none '' ]; }; # The `memberof` operator is extremely useful in writing ACLs, so # we enable it. # # We specifically turn on referential integrity for it, meaning # the server will reject edits that would break the bidirectional # nature of the link. These error messages can be confusing, so it's # worth knowing about. children = { "olcOverlay={0}memberof".attrs = { objectClass = [ "olcOverlayConfig" "olcMemberOf" ]; olcOverlay = "memberof"; olcMemberOfRefint = "TRUE"; }; }; }; }; }; }; }