summary refs log tree commit diff
diff options
context:
space:
mode:
authorRuben Maher <r@rkm.id.au>2017-09-23 12:48:44 +0930
committerRuben Maher <r@rkm.id.au>2017-10-02 14:30:19 +1030
commit06e15e59f950590b4005b293081823d2efaec9e5 (patch)
treed3b52996cef23d089a8162935c5471284bdf6b0a
parent047c576353bce80a4c418addc84f1adf96e2cf0e (diff)
downloadnixpkgs-06e15e59f950590b4005b293081823d2efaec9e5.tar
nixpkgs-06e15e59f950590b4005b293081823d2efaec9e5.tar.gz
nixpkgs-06e15e59f950590b4005b293081823d2efaec9e5.tar.bz2
nixpkgs-06e15e59f950590b4005b293081823d2efaec9e5.tar.lz
nixpkgs-06e15e59f950590b4005b293081823d2efaec9e5.tar.xz
nixpkgs-06e15e59f950590b4005b293081823d2efaec9e5.tar.zst
nixpkgs-06e15e59f950590b4005b293081823d2efaec9e5.zip
nixos/krb5: complete rewrite
The `krb5` service was a bit lacking.

Addresses NixOS/nixpkgs#11268, partially addresses NixOS/nixpkgs#29623.
-rw-r--r--nixos/modules/config/krb5.nix206
-rw-r--r--nixos/modules/config/krb5/default.nix367
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/tests/krb5/default.nix5
-rw-r--r--nixos/tests/krb5/deprecated-config.nix48
-rw-r--r--nixos/tests/krb5/example-config.nix106
6 files changed, 527 insertions, 207 deletions
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
+          <literal>environment.systemPackages</literal> 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 <literal>krb5.conf</literal> verbatim.
+          <literal>krb5.conf</literal> may include any of the relations that are
+          valid for <literal>kdc.conf</literal> (see <literal>man
+          kdc.conf</literal>), 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 <literal>krb5.conf</literal> configuration.  Note that this
+          is mutually exclusive with configuration via
+          <literal>libdefaults</literal>, <literal>realms</literal>,
+          <literal>domain_realm</literal>, <literal>capaths</literal>,
+          <literal>appdefaults</literal>, <literal>plugins</literal> and
+          <literal>extraConfig</literal> configuration options.  Consult
+          <literal>man krb5.conf</literal> for documentation.
+        '';
+      };
+
+      defaultRealm = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        example = "ATHENA.MIT.EDU";
+        description = ''
+          DEPRECATED, please use
+          <literal>krb5.libdefaults.default_realm</literal>.
+        '';
+      };
+
+      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 <literal>krb5.domain_realm</literal>.
+        '';
+      };
+
+      kdc = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        example = "kerberos.mit.edu";
+        description = ''
+          DEPRECATED, please pass a <literal>kdc</literal> attribute to a realm
+          in <literal>krb5.realms</literal>.
+        '';
+      };
+
+      kerberosAdminServer = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        example = "kerberos.mit.edu";
+        description = ''
+          DEPRECATED, please pass an <literal>admin_server</literal> attribute
+          to a realm in <literal>krb5.realms</literal>.
+        '';
+      };
+    };
+  };
+
+  ###### 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}");
+  '';
+})