diff --git a/nixos/modules/config/krb5.nix b/nixos/modules/config/krb5.nix
deleted file mode 100644
index d318b720742..00000000000
--- a/nixos/modules/config/krb5.nix
+++ /dev/null
@@ -1,206 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
- cfg = config.krb5;
-
-in
-
-{
- ###### interface
-
- options = {
-
- krb5 = {
-
- enable = mkOption {
- default = false;
- description = "Whether to enable Kerberos V.";
- };
-
- defaultRealm = mkOption {
- default = "ATENA.MIT.EDU";
- description = "Default realm.";
- };
-
- domainRealm = mkOption {
- default = "atena.mit.edu";
- description = "Default domain realm.";
- };
-
- kdc = mkOption {
- default = "kerberos.mit.edu";
- description = "Key Distribution Center";
- };
-
- kerberosAdminServer = mkOption {
- default = "kerberos.mit.edu";
- description = "Kerberos Admin Server.";
- };
-
- };
-
- };
-
- ###### implementation
-
- config = mkIf config.krb5.enable {
-
- environment.systemPackages = [ pkgs.krb5Full ];
-
- environment.etc."krb5.conf".text =
- ''
- [libdefaults]
- default_realm = ${cfg.defaultRealm}
- encrypt = true
-
- # The following krb5.conf variables are only for MIT Kerberos.
- krb4_config = /etc/krb.conf
- krb4_realms = /etc/krb.realms
- kdc_timesync = 1
- ccache_type = 4
- forwardable = true
- proxiable = true
-
- # The following encryption type specification will be used by MIT Kerberos
- # if uncommented. In general, the defaults in the MIT Kerberos code are
- # correct and overriding these specifications only serves to disable new
- # encryption types as they are added, creating interoperability problems.
-
- # default_tgs_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
- # default_tkt_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
- # permitted_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
-
- # The following libdefaults parameters are only for Heimdal Kerberos.
- v4_instance_resolve = false
- v4_name_convert = {
- host = {
- rcmd = host
- ftp = ftp
- }
- plain = {
- something = something-else
- }
- }
- fcc-mit-ticketflags = true
-
- [realms]
- ${cfg.defaultRealm} = {
- kdc = ${cfg.kdc}
- admin_server = ${cfg.kerberosAdminServer}
- #kpasswd_server = ${cfg.kerberosAdminServer}
- }
- ATHENA.MIT.EDU = {
- kdc = kerberos.mit.edu:88
- kdc = kerberos-1.mit.edu:88
- kdc = kerberos-2.mit.edu:88
- admin_server = kerberos.mit.edu
- default_domain = mit.edu
- }
- MEDIA-LAB.MIT.EDU = {
- kdc = kerberos.media.mit.edu
- admin_server = kerberos.media.mit.edu
- }
- ZONE.MIT.EDU = {
- kdc = casio.mit.edu
- kdc = seiko.mit.edu
- admin_server = casio.mit.edu
- }
- MOOF.MIT.EDU = {
- kdc = three-headed-dogcow.mit.edu:88
- kdc = three-headed-dogcow-1.mit.edu:88
- admin_server = three-headed-dogcow.mit.edu
- }
- CSAIL.MIT.EDU = {
- kdc = kerberos-1.csail.mit.edu
- kdc = kerberos-2.csail.mit.edu
- admin_server = kerberos.csail.mit.edu
- default_domain = csail.mit.edu
- krb524_server = krb524.csail.mit.edu
- }
- IHTFP.ORG = {
- kdc = kerberos.ihtfp.org
- admin_server = kerberos.ihtfp.org
- }
- GNU.ORG = {
- kdc = kerberos.gnu.org
- kdc = kerberos-2.gnu.org
- kdc = kerberos-3.gnu.org
- admin_server = kerberos.gnu.org
- }
- 1TS.ORG = {
- kdc = kerberos.1ts.org
- admin_server = kerberos.1ts.org
- }
- GRATUITOUS.ORG = {
- kdc = kerberos.gratuitous.org
- admin_server = kerberos.gratuitous.org
- }
- DOOMCOM.ORG = {
- kdc = kerberos.doomcom.org
- admin_server = kerberos.doomcom.org
- }
- ANDREW.CMU.EDU = {
- kdc = vice28.fs.andrew.cmu.edu
- kdc = vice2.fs.andrew.cmu.edu
- kdc = vice11.fs.andrew.cmu.edu
- kdc = vice12.fs.andrew.cmu.edu
- admin_server = vice28.fs.andrew.cmu.edu
- default_domain = andrew.cmu.edu
- }
- CS.CMU.EDU = {
- kdc = kerberos.cs.cmu.edu
- kdc = kerberos-2.srv.cs.cmu.edu
- admin_server = kerberos.cs.cmu.edu
- }
- DEMENTIA.ORG = {
- kdc = kerberos.dementia.org
- kdc = kerberos2.dementia.org
- admin_server = kerberos.dementia.org
- }
- stanford.edu = {
- kdc = krb5auth1.stanford.edu
- kdc = krb5auth2.stanford.edu
- kdc = krb5auth3.stanford.edu
- admin_server = krb5-admin.stanford.edu
- default_domain = stanford.edu
- }
-
- [domain_realm]
- .${cfg.domainRealm} = ${cfg.defaultRealm}
- ${cfg.domainRealm} = ${cfg.defaultRealm}
- .mit.edu = ATHENA.MIT.EDU
- mit.edu = ATHENA.MIT.EDU
- .exchange.mit.edu = EXCHANGE.MIT.EDU
- exchange.mit.edu = EXCHANGE.MIT.EDU
- .media.mit.edu = MEDIA-LAB.MIT.EDU
- media.mit.edu = MEDIA-LAB.MIT.EDU
- .csail.mit.edu = CSAIL.MIT.EDU
- csail.mit.edu = CSAIL.MIT.EDU
- .whoi.edu = ATHENA.MIT.EDU
- whoi.edu = ATHENA.MIT.EDU
- .stanford.edu = stanford.edu
-
- [logging]
- kdc = SYSLOG:INFO:DAEMON
- admin_server = SYSLOG:INFO:DAEMON
- default = SYSLOG:INFO:DAEMON
- krb4_convert = true
- krb4_get_tickets = false
-
- [appdefaults]
- pam = {
- debug = false
- ticket_lifetime = 36000
- renew_lifetime = 36000
- max_timeout = 30
- timeout_shift = 2
- initial_timeout = 1
- }
- '';
-
- };
-
-}
diff --git a/nixos/modules/config/krb5/default.nix b/nixos/modules/config/krb5/default.nix
new file mode 100644
index 00000000000..bdcc2d48cd1
--- /dev/null
+++ b/nixos/modules/config/krb5/default.nix
@@ -0,0 +1,367 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.krb5;
+
+ # This is to provide support for old configuration options (as much as is
+ # reasonable). This can probably be removed after some time.
+ defaultConfig = {
+ libdefaults = optionalAttrs (cfg.defaultRealm != null)
+ { default_realm = cfg.defaultRealm; };
+
+ realms = optionalAttrs (lib.all (value: value != null) [
+ cfg.defaultRealm cfg.kdc cfg.kerberosAdminServer
+ ]) {
+ "${cfg.defaultRealm}" = {
+ kdc = cfg.kdc;
+ admin_server = cfg.kerberosAdminServer;
+ };
+ };
+
+ domain_realm = optionalAttrs (lib.all (value: value != null) [
+ cfg.domainRealm cfg.defaultRealm
+ ]) {
+ ".${cfg.domainRealm}" = cfg.defaultRealm;
+ "${cfg.domainRealm}" = cfg.defaultRealm;
+ };
+ };
+
+ mergedConfig = (recursiveUpdate defaultConfig {
+ inherit (config.krb5)
+ kerberos libdefaults realms domain_realm capaths appdefaults plugins
+ extraConfig config;
+ });
+
+ filterEmbeddedMetadata = value: if isAttrs value then
+ (filterAttrs
+ (attrName: attrValue: attrName != "_module" && attrValue != null)
+ value)
+ else value;
+
+ mkIndent = depth: concatStrings (builtins.genList (_: " ") (2 * depth));
+
+ mkRelation = name: value: "${name} = ${mkVal { inherit value; }}";
+
+ mkVal = { value, depth ? 0 }:
+ if (value == true) then "true"
+ else if (value == false) then "false"
+ else if (isInt value) then (toString value)
+ else if (isList value) then
+ concatMapStringsSep " " mkVal { inherit value depth; }
+ else if (isAttrs value) then
+ (concatStringsSep "\n${mkIndent (depth + 1)}"
+ ([ "{" ] ++ (mapAttrsToList
+ (attrName: attrValue: let
+ mappedAttrValue = mkVal {
+ value = attrValue;
+ depth = depth + 1;
+ };
+ in "${attrName} = ${mappedAttrValue}")
+ value))) + "\n${mkIndent depth}}"
+ else value;
+
+ mkMappedAttrsOrString = value: concatMapStringsSep "\n"
+ (line: if builtins.stringLength line > 0
+ then "${mkIndent 1}${line}"
+ else line)
+ (splitString "\n"
+ (if isAttrs value then
+ concatStringsSep "\n"
+ (mapAttrsToList mkRelation value)
+ else value));
+
+in {
+
+ ###### interface
+
+ options = {
+ krb5 = {
+ enable = mkEnableOption "Whether to enable Kerberos V.";
+
+ kerberos = mkOption {
+ type = types.package;
+ default = pkgs.krb5Full;
+ defaultText = "pkgs.krb5Full";
+ example = literalExample "pkgs.heimdalFull";
+ description = ''
+ The Kerberos implementation that will be present in
+ environment.systemPackages after enabling this
+ service.
+ '';
+ };
+
+ libdefaults = mkOption {
+ type = with types; either attrs lines;
+ default = {};
+ apply = attrs: filterEmbeddedMetadata attrs;
+ example = literalExample ''
+ {
+ default_realm = "ATHENA.MIT.EDU";
+ };
+ '';
+ description = ''
+ Settings used by the Kerberos V5 library.
+ '';
+ };
+
+ realms = mkOption {
+ type = with types; either attrs lines;
+ default = {};
+ example = literalExample ''
+ {
+ "ATHENA.MIT.EDU" = {
+ admin_server = "athena.mit.edu";
+ kdc = "athena.mit.edu";
+ };
+ };
+ '';
+ apply = attrs: filterEmbeddedMetadata attrs;
+ description = "Realm-specific contact information and settings.";
+ };
+
+ domain_realm = mkOption {
+ type = with types; either attrs lines;
+ default = {};
+ example = literalExample ''
+ {
+ "example.com" = "EXAMPLE.COM";
+ ".example.com" = "EXAMPLE.COM";
+ };
+ '';
+ apply = attrs: filterEmbeddedMetadata attrs;
+ description = ''
+ Map of server hostnames to Kerberos realms.
+ '';
+ };
+
+ capaths = mkOption {
+ type = with types; either attrs lines;
+ default = {};
+ example = literalExample ''
+ {
+ "ATHENA.MIT.EDU" = {
+ "EXAMPLE.COM" = ".";
+ };
+ "EXAMPLE.COM" = {
+ "ATHENA.MIT.EDU" = ".";
+ };
+ };
+ '';
+ apply = attrs: filterEmbeddedMetadata attrs;
+ description = ''
+ Authentication paths for non-hierarchical cross-realm authentication.
+ '';
+ };
+
+ appdefaults = mkOption {
+ type = with types; either attrs lines;
+ default = {};
+ example = literalExample ''
+ {
+ pam = {
+ debug = false;
+ ticket_lifetime = 36000;
+ renew_lifetime = 36000;
+ max_timeout = 30;
+ timeout_shift = 2;
+ initial_timeout = 1;
+ };
+ };
+ '';
+ apply = attrs: filterEmbeddedMetadata attrs;
+ description = ''
+ Settings used by some Kerberos V5 applications.
+ '';
+ };
+
+ plugins = mkOption {
+ type = with types; either attrs lines;
+ default = {};
+ example = literalExample ''
+ {
+ ccselect = {
+ disable = "k5identity";
+ };
+ };
+ '';
+ apply = attrs: filterEmbeddedMetadata attrs;
+ description = ''
+ Controls plugin module registration.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = with types; nullOr lines;
+ default = null;
+ example = ''
+ [logging]
+ kdc = SYSLOG:NOTICE
+ admin_server = SYSLOG:NOTICE
+ default = SYSLOG:NOTICE
+ '';
+ description = ''
+ These lines go to the end of krb5.conf verbatim.
+ krb5.conf may include any of the relations that are
+ valid for kdc.conf (see man
+ kdc.conf), but it is not a recommended practice.
+ '';
+ };
+
+ config = mkOption {
+ type = with types; nullOr lines;
+ default = null;
+ example = ''
+ [libdefaults]
+ default_realm = EXAMPLE.COM
+
+ [realms]
+ EXAMPLE.COM = {
+ admin_server = kerberos.example.com
+ kdc = kerberos.example.com
+ default_principal_flags = +preauth
+ }
+
+ [domain_realm]
+ example.com = EXAMPLE.COM
+ .example.com = EXAMPLE.COM
+
+ [logging]
+ kdc = SYSLOG:NOTICE
+ admin_server = SYSLOG:NOTICE
+ default = SYSLOG:NOTICE
+ '';
+ description = ''
+ Verbatim krb5.conf configuration. Note that this
+ is mutually exclusive with configuration via
+ libdefaults, realms,
+ domain_realm, capaths,
+ appdefaults, plugins and
+ extraConfig configuration options. Consult
+ man krb5.conf for documentation.
+ '';
+ };
+
+ defaultRealm = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ example = "ATHENA.MIT.EDU";
+ description = ''
+ DEPRECATED, please use
+ krb5.libdefaults.default_realm.
+ '';
+ };
+
+ domainRealm = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ example = "athena.mit.edu";
+ description = ''
+ DEPRECATED, please create a map of server hostnames to Kerberos realms
+ in krb5.domain_realm.
+ '';
+ };
+
+ kdc = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ example = "kerberos.mit.edu";
+ description = ''
+ DEPRECATED, please pass a kdc attribute to a realm
+ in krb5.realms.
+ '';
+ };
+
+ kerberosAdminServer = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ example = "kerberos.mit.edu";
+ description = ''
+ DEPRECATED, please pass an admin_server attribute
+ to a realm in krb5.realms.
+ '';
+ };
+ };
+ };
+
+ ###### implementation
+
+ config = mkIf cfg.enable {
+
+ environment.systemPackages = [ cfg.kerberos ];
+
+ environment.etc."krb5.conf".text = if isString cfg.config
+ then cfg.config
+ else (''
+ [libdefaults]
+ ${mkMappedAttrsOrString mergedConfig.libdefaults}
+
+ [realms]
+ ${mkMappedAttrsOrString mergedConfig.realms}
+
+ [domain_realm]
+ ${mkMappedAttrsOrString mergedConfig.domain_realm}
+
+ [capaths]
+ ${mkMappedAttrsOrString mergedConfig.capaths}
+
+ [appdefaults]
+ ${mkMappedAttrsOrString mergedConfig.appdefaults}
+
+ [plugins]
+ ${mkMappedAttrsOrString mergedConfig.plugins}
+ '' + optionalString (mergedConfig.extraConfig != null)
+ ("\n" + mergedConfig.extraConfig));
+
+ warnings = flatten [
+ (optional (cfg.defaultRealm != null) ''
+ The option krb5.defaultRealm is deprecated, please use
+ krb5.libdefaults.default_realm.
+ '')
+ (optional (cfg.domainRealm != null) ''
+ The option krb5.domainRealm is deprecated, please use krb5.domain_realm.
+ '')
+ (optional (cfg.kdc != null) ''
+ The option krb5.kdc is deprecated, please pass a kdc attribute to a
+ realm in krb5.realms.
+ '')
+ (optional (cfg.kerberosAdminServer != null) ''
+ The option krb5.kerberosAdminServer is deprecated, please pass an
+ admin_server attribute to a realm in krb5.realms.
+ '')
+ ];
+
+ assertions = [
+ { assertion = !((builtins.any (value: value != null) [
+ cfg.defaultRealm cfg.domainRealm cfg.kdc cfg.kerberosAdminServer
+ ]) && ((builtins.any (value: value != {}) [
+ cfg.libdefaults cfg.realms cfg.domain_realm cfg.capaths
+ cfg.appdefaults cfg.plugins
+ ]) || (builtins.any (value: value != null) [
+ cfg.config cfg.extraConfig
+ ])));
+ message = ''
+ Configuration of krb5.conf by deprecated options is mutually exclusive
+ with configuration by section. Please migrate your config using the
+ attributes suggested in the warnings.
+ '';
+ }
+ { assertion = !(cfg.config != null
+ && ((builtins.any (value: value != {}) [
+ cfg.libdefaults cfg.realms cfg.domain_realm cfg.capaths
+ cfg.appdefaults cfg.plugins
+ ]) || (builtins.any (value: value != null) [
+ cfg.extraConfig cfg.defaultRealm cfg.domainRealm cfg.kdc
+ cfg.kerberosAdminServer
+ ])));
+ message = ''
+ Configuration of krb5.conf using krb.config is mutually exclusive with
+ configuration by section. If you want to mix the two, you can pass
+ lines to any configuration section or lines to krb5.extraConfig.
+ '';
+ }
+ ];
+ };
+}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index e849e634fc9..06dcd70d166 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -9,7 +9,7 @@
./config/fonts/ghostscript.nix
./config/gnu.nix
./config/i18n.nix
- ./config/krb5.nix
+ ./config/krb5/default.nix
./config/ldap.nix
./config/networking.nix
./config/no-x-libs.nix
diff --git a/nixos/tests/krb5/default.nix b/nixos/tests/krb5/default.nix
new file mode 100644
index 00000000000..dd5b2f37202
--- /dev/null
+++ b/nixos/tests/krb5/default.nix
@@ -0,0 +1,5 @@
+{ system ? builtins.currentSystem }:
+{
+ example-config = import ./example-config.nix { inherit system; };
+ deprecated-config = import ./deprecated-config.nix { inherit system; };
+}
diff --git a/nixos/tests/krb5/deprecated-config.nix b/nixos/tests/krb5/deprecated-config.nix
new file mode 100644
index 00000000000..980b3e762dc
--- /dev/null
+++ b/nixos/tests/krb5/deprecated-config.nix
@@ -0,0 +1,48 @@
+# Verifies that the configuration suggested in deprecated example values
+# will result in the expected output.
+
+import ../make-test.nix ({ pkgs, ...} : {
+ name = "krb5-with-deprecated-config";
+ meta = with pkgs.stdenv.lib.maintainers; {
+ maintainers = [ eqyiel ];
+ };
+
+ machine =
+ { config, pkgs, ... }: {
+ krb5 = {
+ enable = true;
+ defaultRealm = "ATHENA.MIT.EDU";
+ domainRealm = "athena.mit.edu";
+ kdc = "kerberos.mit.edu";
+ kerberosAdminServer = "kerberos.mit.edu";
+ };
+ };
+
+ testScript =
+ let snapshot = pkgs.writeText "krb5-with-deprecated-config.conf" ''
+ [libdefaults]
+ default_realm = ATHENA.MIT.EDU
+
+ [realms]
+ ATHENA.MIT.EDU = {
+ admin_server = kerberos.mit.edu
+ kdc = kerberos.mit.edu
+ }
+
+ [domain_realm]
+ .athena.mit.edu = ATHENA.MIT.EDU
+ athena.mit.edu = ATHENA.MIT.EDU
+
+ [capaths]
+
+
+ [appdefaults]
+
+
+ [plugins]
+
+ '';
+ in ''
+ $machine->succeed("diff /etc/krb5.conf ${snapshot}");
+ '';
+})
diff --git a/nixos/tests/krb5/example-config.nix b/nixos/tests/krb5/example-config.nix
new file mode 100644
index 00000000000..d5328720931
--- /dev/null
+++ b/nixos/tests/krb5/example-config.nix
@@ -0,0 +1,106 @@
+# Verifies that the configuration suggested in (non-deprecated) example values
+# will result in the expected output.
+
+import ../make-test.nix ({ pkgs, ...} : {
+ name = "krb5-with-example-config";
+ meta = with pkgs.stdenv.lib.maintainers; {
+ maintainers = [ eqyiel ];
+ };
+
+ machine =
+ { config, pkgs, ... }: {
+ krb5 = {
+ enable = true;
+ kerberos = pkgs.krb5Full;
+ libdefaults = {
+ default_realm = "ATHENA.MIT.EDU";
+ };
+ realms = {
+ "ATHENA.MIT.EDU" = {
+ admin_server = "athena.mit.edu";
+ kdc = "athena.mit.edu";
+ };
+ };
+ domain_realm = {
+ "example.com" = "EXAMPLE.COM";
+ ".example.com" = "EXAMPLE.COM";
+ };
+ capaths = {
+ "ATHENA.MIT.EDU" = {
+ "EXAMPLE.COM" = ".";
+ };
+ "EXAMPLE.COM" = {
+ "ATHENA.MIT.EDU" = ".";
+ };
+ };
+ appdefaults = {
+ pam = {
+ debug = false;
+ ticket_lifetime = 36000;
+ renew_lifetime = 36000;
+ max_timeout = 30;
+ timeout_shift = 2;
+ initial_timeout = 1;
+ };
+ };
+ plugins = {
+ ccselect = {
+ disable = "k5identity";
+ };
+ };
+ extraConfig = ''
+ [logging]
+ kdc = SYSLOG:NOTICE
+ admin_server = SYSLOG:NOTICE
+ default = SYSLOG:NOTICE
+ '';
+ };
+ };
+
+ testScript =
+ let snapshot = pkgs.writeText "krb5-with-example-config.conf" ''
+ [libdefaults]
+ default_realm = ATHENA.MIT.EDU
+
+ [realms]
+ ATHENA.MIT.EDU = {
+ admin_server = athena.mit.edu
+ kdc = athena.mit.edu
+ }
+
+ [domain_realm]
+ .example.com = EXAMPLE.COM
+ example.com = EXAMPLE.COM
+
+ [capaths]
+ ATHENA.MIT.EDU = {
+ EXAMPLE.COM = .
+ }
+ EXAMPLE.COM = {
+ ATHENA.MIT.EDU = .
+ }
+
+ [appdefaults]
+ pam = {
+ debug = false
+ initial_timeout = 1
+ max_timeout = 30
+ renew_lifetime = 36000
+ ticket_lifetime = 36000
+ timeout_shift = 2
+ }
+
+ [plugins]
+ ccselect = {
+ disable = k5identity
+ }
+
+ [logging]
+ kdc = SYSLOG:NOTICE
+ admin_server = SYSLOG:NOTICE
+ default = SYSLOG:NOTICE
+ '';
+ in ''
+ $machine->succeed("diff /etc/krb5.conf ${snapshot}");
+ '';
+})