1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
# 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=ldap-admins,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";
};
};
};
};
};
};
}
|