1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.krb5;
8
9 # This is to provide support for old configuration options (as much as is
10 # reasonable). This can be removed after 18.03 was released.
11 defaultConfig = {
12 libdefaults = optionalAttrs (cfg.defaultRealm != null)
13 { default_realm = cfg.defaultRealm; };
14
15 realms = optionalAttrs (lib.all (value: value != null) [
16 cfg.defaultRealm cfg.kdc cfg.kerberosAdminServer
17 ]) {
18 "${cfg.defaultRealm}" = {
19 kdc = cfg.kdc;
20 admin_server = cfg.kerberosAdminServer;
21 };
22 };
23
24 domain_realm = optionalAttrs (lib.all (value: value != null) [
25 cfg.domainRealm cfg.defaultRealm
26 ]) {
27 ".${cfg.domainRealm}" = cfg.defaultRealm;
28 "${cfg.domainRealm}" = cfg.defaultRealm;
29 };
30 };
31
32 mergedConfig = (recursiveUpdate defaultConfig {
33 inherit (config.krb5)
34 kerberos libdefaults realms domain_realm capaths appdefaults plugins
35 extraConfig config;
36 });
37
38 filterEmbeddedMetadata = value: if isAttrs value then
39 (filterAttrs
40 (attrName: attrValue: attrName != "_module" && attrValue != null)
41 value)
42 else value;
43
44 mkIndent = depth: concatStrings (builtins.genList (_: " ") (2 * depth));
45
46 mkRelation = name: value: "${name} = ${mkVal { inherit value; }}";
47
48 mkVal = { value, depth ? 0 }:
49 if (value == true) then "true"
50 else if (value == false) then "false"
51 else if (isInt value) then (toString value)
52 else if (isList value) then
53 concatMapStringsSep " " mkVal { inherit value depth; }
54 else if (isAttrs value) then
55 (concatStringsSep "\n${mkIndent (depth + 1)}"
56 ([ "{" ] ++ (mapAttrsToList
57 (attrName: attrValue: let
58 mappedAttrValue = mkVal {
59 value = attrValue;
60 depth = depth + 1;
61 };
62 in "${attrName} = ${mappedAttrValue}")
63 value))) + "\n${mkIndent depth}}"
64 else value;
65
66 mkMappedAttrsOrString = value: concatMapStringsSep "\n"
67 (line: if builtins.stringLength line > 0
68 then "${mkIndent 1}${line}"
69 else line)
70 (splitString "\n"
71 (if isAttrs value then
72 concatStringsSep "\n"
73 (mapAttrsToList mkRelation value)
74 else value));
75
76in {
77
78 ###### interface
79
80 options = {
81 krb5 = {
82 enable = mkEnableOption "Whether to enable Kerberos V.";
83
84 kerberos = mkOption {
85 type = types.package;
86 default = pkgs.krb5Full;
87 defaultText = "pkgs.krb5Full";
88 example = literalExample "pkgs.heimdalFull";
89 description = ''
90 The Kerberos implementation that will be present in
91 <literal>environment.systemPackages</literal> after enabling this
92 service.
93 '';
94 };
95
96 libdefaults = mkOption {
97 type = with types; either attrs lines;
98 default = {};
99 apply = attrs: filterEmbeddedMetadata attrs;
100 example = literalExample ''
101 {
102 default_realm = "ATHENA.MIT.EDU";
103 };
104 '';
105 description = ''
106 Settings used by the Kerberos V5 library.
107 '';
108 };
109
110 realms = mkOption {
111 type = with types; either attrs lines;
112 default = {};
113 example = literalExample ''
114 {
115 "ATHENA.MIT.EDU" = {
116 admin_server = "athena.mit.edu";
117 kdc = "athena.mit.edu";
118 };
119 };
120 '';
121 apply = attrs: filterEmbeddedMetadata attrs;
122 description = "Realm-specific contact information and settings.";
123 };
124
125 domain_realm = mkOption {
126 type = with types; either attrs lines;
127 default = {};
128 example = literalExample ''
129 {
130 "example.com" = "EXAMPLE.COM";
131 ".example.com" = "EXAMPLE.COM";
132 };
133 '';
134 apply = attrs: filterEmbeddedMetadata attrs;
135 description = ''
136 Map of server hostnames to Kerberos realms.
137 '';
138 };
139
140 capaths = mkOption {
141 type = with types; either attrs lines;
142 default = {};
143 example = literalExample ''
144 {
145 "ATHENA.MIT.EDU" = {
146 "EXAMPLE.COM" = ".";
147 };
148 "EXAMPLE.COM" = {
149 "ATHENA.MIT.EDU" = ".";
150 };
151 };
152 '';
153 apply = attrs: filterEmbeddedMetadata attrs;
154 description = ''
155 Authentication paths for non-hierarchical cross-realm authentication.
156 '';
157 };
158
159 appdefaults = mkOption {
160 type = with types; either attrs lines;
161 default = {};
162 example = literalExample ''
163 {
164 pam = {
165 debug = false;
166 ticket_lifetime = 36000;
167 renew_lifetime = 36000;
168 max_timeout = 30;
169 timeout_shift = 2;
170 initial_timeout = 1;
171 };
172 };
173 '';
174 apply = attrs: filterEmbeddedMetadata attrs;
175 description = ''
176 Settings used by some Kerberos V5 applications.
177 '';
178 };
179
180 plugins = mkOption {
181 type = with types; either attrs lines;
182 default = {};
183 example = literalExample ''
184 {
185 ccselect = {
186 disable = "k5identity";
187 };
188 };
189 '';
190 apply = attrs: filterEmbeddedMetadata attrs;
191 description = ''
192 Controls plugin module registration.
193 '';
194 };
195
196 extraConfig = mkOption {
197 type = with types; nullOr lines;
198 default = null;
199 example = ''
200 [logging]
201 kdc = SYSLOG:NOTICE
202 admin_server = SYSLOG:NOTICE
203 default = SYSLOG:NOTICE
204 '';
205 description = ''
206 These lines go to the end of <literal>krb5.conf</literal> verbatim.
207 <literal>krb5.conf</literal> may include any of the relations that are
208 valid for <literal>kdc.conf</literal> (see <literal>man
209 kdc.conf</literal>), but it is not a recommended practice.
210 '';
211 };
212
213 config = mkOption {
214 type = with types; nullOr lines;
215 default = null;
216 example = ''
217 [libdefaults]
218 default_realm = EXAMPLE.COM
219
220 [realms]
221 EXAMPLE.COM = {
222 admin_server = kerberos.example.com
223 kdc = kerberos.example.com
224 default_principal_flags = +preauth
225 }
226
227 [domain_realm]
228 example.com = EXAMPLE.COM
229 .example.com = EXAMPLE.COM
230
231 [logging]
232 kdc = SYSLOG:NOTICE
233 admin_server = SYSLOG:NOTICE
234 default = SYSLOG:NOTICE
235 '';
236 description = ''
237 Verbatim <literal>krb5.conf</literal> configuration. Note that this
238 is mutually exclusive with configuration via
239 <literal>libdefaults</literal>, <literal>realms</literal>,
240 <literal>domain_realm</literal>, <literal>capaths</literal>,
241 <literal>appdefaults</literal>, <literal>plugins</literal> and
242 <literal>extraConfig</literal> configuration options. Consult
243 <literal>man krb5.conf</literal> for documentation.
244 '';
245 };
246
247 defaultRealm = mkOption {
248 type = with types; nullOr str;
249 default = null;
250 example = "ATHENA.MIT.EDU";
251 description = ''
252 DEPRECATED, please use
253 <literal>krb5.libdefaults.default_realm</literal>.
254 '';
255 };
256
257 domainRealm = mkOption {
258 type = with types; nullOr str;
259 default = null;
260 example = "athena.mit.edu";
261 description = ''
262 DEPRECATED, please create a map of server hostnames to Kerberos realms
263 in <literal>krb5.domain_realm</literal>.
264 '';
265 };
266
267 kdc = mkOption {
268 type = with types; nullOr str;
269 default = null;
270 example = "kerberos.mit.edu";
271 description = ''
272 DEPRECATED, please pass a <literal>kdc</literal> attribute to a realm
273 in <literal>krb5.realms</literal>.
274 '';
275 };
276
277 kerberosAdminServer = mkOption {
278 type = with types; nullOr str;
279 default = null;
280 example = "kerberos.mit.edu";
281 description = ''
282 DEPRECATED, please pass an <literal>admin_server</literal> attribute
283 to a realm in <literal>krb5.realms</literal>.
284 '';
285 };
286 };
287 };
288
289 ###### implementation
290
291 config = mkIf cfg.enable {
292
293 environment.systemPackages = [ cfg.kerberos ];
294
295 environment.etc."krb5.conf".text = if isString cfg.config
296 then cfg.config
297 else (''
298 [libdefaults]
299 ${mkMappedAttrsOrString mergedConfig.libdefaults}
300
301 [realms]
302 ${mkMappedAttrsOrString mergedConfig.realms}
303
304 [domain_realm]
305 ${mkMappedAttrsOrString mergedConfig.domain_realm}
306
307 [capaths]
308 ${mkMappedAttrsOrString mergedConfig.capaths}
309
310 [appdefaults]
311 ${mkMappedAttrsOrString mergedConfig.appdefaults}
312
313 [plugins]
314 ${mkMappedAttrsOrString mergedConfig.plugins}
315 '' + optionalString (mergedConfig.extraConfig != null)
316 ("\n" + mergedConfig.extraConfig));
317
318 warnings = flatten [
319 (optional (cfg.defaultRealm != null) ''
320 The option krb5.defaultRealm is deprecated, please use
321 krb5.libdefaults.default_realm.
322 '')
323 (optional (cfg.domainRealm != null) ''
324 The option krb5.domainRealm is deprecated, please use krb5.domain_realm.
325 '')
326 (optional (cfg.kdc != null) ''
327 The option krb5.kdc is deprecated, please pass a kdc attribute to a
328 realm in krb5.realms.
329 '')
330 (optional (cfg.kerberosAdminServer != null) ''
331 The option krb5.kerberosAdminServer is deprecated, please pass an
332 admin_server attribute to a realm in krb5.realms.
333 '')
334 ];
335
336 assertions = [
337 { assertion = !((builtins.any (value: value != null) [
338 cfg.defaultRealm cfg.domainRealm cfg.kdc cfg.kerberosAdminServer
339 ]) && ((builtins.any (value: value != {}) [
340 cfg.libdefaults cfg.realms cfg.domain_realm cfg.capaths
341 cfg.appdefaults cfg.plugins
342 ]) || (builtins.any (value: value != null) [
343 cfg.config cfg.extraConfig
344 ])));
345 message = ''
346 Configuration of krb5.conf by deprecated options is mutually exclusive
347 with configuration by section. Please migrate your config using the
348 attributes suggested in the warnings.
349 '';
350 }
351 { assertion = !(cfg.config != null
352 && ((builtins.any (value: value != {}) [
353 cfg.libdefaults cfg.realms cfg.domain_realm cfg.capaths
354 cfg.appdefaults cfg.plugins
355 ]) || (builtins.any (value: value != null) [
356 cfg.extraConfig cfg.defaultRealm cfg.domainRealm cfg.kdc
357 cfg.kerberosAdminServer
358 ])));
359 message = ''
360 Configuration of krb5.conf using krb.config is mutually exclusive with
361 configuration by section. If you want to mix the two, you can pass
362 lines to any configuration section or lines to krb5.extraConfig.
363 '';
364 }
365 ];
366 };
367}