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