summary refs log tree commit diff
path: root/nixos/modules/config
diff options
context:
space:
mode:
authorVladimír Čunát <vcunat@gmail.com>2015-10-03 13:33:13 +0200
committerVladimír Čunát <vcunat@gmail.com>2015-10-03 13:33:37 +0200
commit5227fb1dd53fcb5918b9342dff4868f4ad68427e (patch)
treed6cd521e3f67944031216a27f740f28f22b73b41 /nixos/modules/config
parentd6dd3b8bd1eaeeb21dfdb5051cd4732c748ce5d7 (diff)
parent33373d939a19f465228ddede6d38ce9032b5916b (diff)
downloadnixlib-5227fb1dd53fcb5918b9342dff4868f4ad68427e.tar
nixlib-5227fb1dd53fcb5918b9342dff4868f4ad68427e.tar.gz
nixlib-5227fb1dd53fcb5918b9342dff4868f4ad68427e.tar.bz2
nixlib-5227fb1dd53fcb5918b9342dff4868f4ad68427e.tar.lz
nixlib-5227fb1dd53fcb5918b9342dff4868f4ad68427e.tar.xz
nixlib-5227fb1dd53fcb5918b9342dff4868f4ad68427e.tar.zst
nixlib-5227fb1dd53fcb5918b9342dff4868f4ad68427e.zip
Merge commit staging+systemd into closure-size
Many non-conflict problems weren't (fully) resolved in this commit yet.
Diffstat (limited to 'nixos/modules/config')
-rw-r--r--nixos/modules/config/fonts/corefonts.nix4
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix39
-rw-r--r--nixos/modules/config/fonts/fonts.nix1
-rw-r--r--nixos/modules/config/i18n.nix18
-rw-r--r--nixos/modules/config/krb5.nix2
-rw-r--r--nixos/modules/config/ldap.nix10
-rw-r--r--nixos/modules/config/networking.nix11
-rw-r--r--nixos/modules/config/pulseaudio.nix19
-rw-r--r--nixos/modules/config/shells-environment.nix22
-rw-r--r--nixos/modules/config/system-environment.nix15
-rw-r--r--nixos/modules/config/system-path.nix29
-rw-r--r--nixos/modules/config/timezone.nix1
-rw-r--r--nixos/modules/config/users-groups.nix62
13 files changed, 151 insertions, 82 deletions
diff --git a/nixos/modules/config/fonts/corefonts.nix b/nixos/modules/config/fonts/corefonts.nix
index ad7970879324..b9f69879a103 100644
--- a/nixos/modules/config/fonts/corefonts.nix
+++ b/nixos/modules/config/fonts/corefonts.nix
@@ -1,3 +1,6 @@
+# This module is deprecated, since you can just say ‘fonts.fonts = [
+# pkgs.corefonts ];’ instead.
+
 { config, lib, pkgs, ... }:
 
 with lib;
@@ -9,6 +12,7 @@ with lib;
     fonts = {
 
       enableCoreFonts = mkOption {
+        visible = false;
         default = false;
         description = ''
           Whether to include Microsoft's proprietary Core Fonts.  These fonts
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
index 6f17bd396a0b..be6662decea6 100644
--- a/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -108,10 +108,8 @@ with lib;
         subpixel = {
 
           rgba = mkOption {
-            type = types.string // {
-              check = flip elem ["rgb" "bgr" "vrgb" "vbgr" "none"];
-            };
             default = "rgb";
+            type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"];
             description = ''
               Subpixel order, one of <literal>none</literal>,
               <literal>rgb</literal>, <literal>bgr</literal>,
@@ -120,10 +118,8 @@ with lib;
           };
 
           lcdfilter = mkOption {
-            type = types.str // {
-              check = flip elem ["none" "default" "light" "legacy"];
-            };
             default = "default";
+            type = types.enum ["none" "default" "light" "legacy"];
             description = ''
               FreeType LCD filter, one of <literal>none</literal>,
               <literal>default</literal>, <literal>light</literal>, or
@@ -142,7 +138,7 @@ with lib;
   config =
     let fontconfig = config.fonts.fontconfig;
         fcBool = x: "<bool>" + (if x then "true" else "false") + "</bool>";
-        nixosConf = ''
+        renderConf = ''
           <?xml version='1.0'?>
           <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
           <fontconfig>
@@ -169,6 +165,21 @@ with lib;
               </edit>
             </match>
 
+            ${optionalString (fontconfig.dpi != 0) ''
+            <match target="pattern">
+              <edit name="dpi" mode="assign">
+                <double>${toString fontconfig.dpi}</double>
+              </edit>
+            </match>
+            ''}
+
+          </fontconfig>
+        '';
+        genericAliasConf = ''
+          <?xml version='1.0'?>
+          <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+          <fontconfig>
+
             <!-- Default fonts -->
             ${optionalString (fontconfig.defaultFonts.sansSerif != []) ''
             <alias>
@@ -201,14 +212,6 @@ with lib;
             </alias>
             ''}
 
-            ${optionalString (fontconfig.dpi != 0) ''
-            <match target="pattern">
-              <edit name="dpi" mode="assign">
-                <double>${toString fontconfig.dpi}</double>
-              </edit>
-            </match>
-            ''}
-
           </fontconfig>
         '';
     in mkIf fontconfig.enable {
@@ -219,7 +222,8 @@ with lib;
       environment.etc."fonts/fonts.conf".source =
         pkgs.makeFontsConf { fontconfig = pkgs.fontconfig_210; fontDirectories = config.fonts.fonts; };
 
-      environment.etc."fonts/conf.d/98-nixos.conf".text = nixosConf;
+      environment.etc."fonts/conf.d/10-nixos-rendering.conf".text = renderConf;
+      environment.etc."fonts/conf.d/60-nixos-generic-alias.conf".text = genericAliasConf;
 
       # Versioned fontconfig > 2.10. Take shared fonts.conf from fontconfig.
       # Otherwise specify only font directories.
@@ -236,7 +240,8 @@ with lib;
           </fontconfig>
         '';
 
-      environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/98-nixos.conf".text = nixosConf;
+      environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/10-nixos-rendering.conf".text = renderConf;
+      environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/60-nixos-generic-alias.conf".text = genericAliasConf;
 
       environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/99-user.conf" = {
         enable = fontconfig.includeUserConf;
diff --git a/nixos/modules/config/fonts/fonts.nix b/nixos/modules/config/fonts/fonts.nix
index a3fa4bd97783..ea0a67038572 100644
--- a/nixos/modules/config/fonts/fonts.nix
+++ b/nixos/modules/config/fonts/fonts.nix
@@ -31,6 +31,7 @@ with lib;
         pkgs.xorg.fontbh100dpi
         pkgs.xorg.fontmiscmisc
         pkgs.xorg.fontcursormisc
+        pkgs.unifont
       ];
 
   };
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index f2aacf9b2924..f58e540a6e5c 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -43,7 +43,7 @@ in
 
       consoleFont = mkOption {
         type = types.str;
-        default = "lat9w-16";
+        default = "Lat2-Terminus16";
         example = "LatArCyrHeb-16";
         description = ''
           The font used for the virtual consoles.  Leave empty to use
@@ -52,6 +52,15 @@ in
         '';
       };
 
+      consoleUseXkbConfig = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If set, configure the console keymap from the xserver keyboard
+          settings.
+        '';
+      };
+
       consoleKeyMap = mkOption {
         type = mkOptionType {
           name = "string or path";
@@ -74,6 +83,13 @@ in
 
   config = {
 
+    i18n.consoleKeyMap = with config.services.xserver;
+      mkIf config.i18n.consoleUseXkbConfig
+        (pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
+          '${pkgs.ckbcomp}/bin/ckbcomp' -model '${xkbModel}' -layout '${layout}' \
+            -option '${xkbOptions}' -variant '${xkbVariant}' > "$out"
+        '');
+
     environment.systemPackages =
       optional (config.i18n.supportedLocales != []) glibcLocales;
 
diff --git a/nixos/modules/config/krb5.nix b/nixos/modules/config/krb5.nix
index 991b5b16cc68..d2198e4ac1ae 100644
--- a/nixos/modules/config/krb5.nix
+++ b/nixos/modules/config/krb5.nix
@@ -48,7 +48,7 @@ in
 
   config = mkIf config.krb5.enable {
 
-    environment.systemPackages = [ pkgs.krb5 ];
+    environment.systemPackages = [ pkgs.krb5Full ];
 
     environment.etc."krb5.conf".text =
       ''
diff --git a/nixos/modules/config/ldap.nix b/nixos/modules/config/ldap.nix
index 1a01533c585b..c87996df8855 100644
--- a/nixos/modules/config/ldap.nix
+++ b/nixos/modules/config/ldap.nix
@@ -108,7 +108,7 @@ in
 
         extraConfig = mkOption {
           default =  "";
-          type = types.string;
+          type = types.lines;
           description = ''
             Extra configuration options that will be added verbatim at
             the end of the nslcd configuration file (nslcd.conf).
@@ -120,7 +120,7 @@ in
         distinguishedName = mkOption {
           default = "";
           example = "cn=admin,dc=example,dc=com";
-          type = types.string;
+          type = types.str;
           description = ''
             The distinguished name to bind to the LDAP server with. If this
             is not specified, an anonymous bind will be done.
@@ -129,7 +129,7 @@ in
 
         password = mkOption {
           default = "/etc/ldap/bind.password";
-          type = types.string;
+          type = types.str;
           description = ''
             The path to a file containing the credentials to use when binding
             to the LDAP server (if not binding anonymously).
@@ -149,7 +149,7 @@ in
 
         policy = mkOption {
           default = "hard_open";
-          type = types.string;
+          type = types.enum [ "hard_open" "hard_init" "soft" ];
           description = ''
             Specifies the policy to use for reconnecting to an unavailable
             LDAP server. The default is <literal>hard_open</literal>, which
@@ -168,7 +168,7 @@ in
 
       extraConfig = mkOption {
         default = "";
-        type = types.string;
+        type = types.lines;
         description = ''
           Extra configuration options that will be added verbatim at
           the end of the ldap configuration file (ldap.conf).
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
index f99cea7d17b1..b49f8a156d1d 100644
--- a/nixos/modules/config/networking.nix
+++ b/nixos/modules/config/networking.nix
@@ -39,6 +39,16 @@ in
       '';
     };
 
+    networking.extraResolvconfConf = lib.mkOption {
+      type = types.lines;
+      default = "";
+      example = "libc=NO";
+      description = ''
+        Extra configuration to append to <filename>resolvconf.conf</filename>.
+      '';
+    };
+
+
     networking.proxy = {
 
       default = lib.mkOption {
@@ -150,6 +160,7 @@ in
             '' + optionalString dnsmasqResolve ''
               dnsmasq_conf=/etc/dnsmasq-conf.conf
               dnsmasq_resolv=/etc/dnsmasq-resolv.conf
+            '' + cfg.extraResolvconfConf + ''
             '';
 
       } // (optionalAttrs config.services.resolved.enable (
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index c41e4ea604d5..2ebc61260558 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -12,7 +12,7 @@ let
 
   # Forces 32bit pulseaudio and alsaPlugins to be built/supported for apps
   # using 32bit alsa on 64bit linux.
-  enable32BitAlsaPlugins = stdenv.isx86_64 && (pkgs_i686.alsaLib != null && pkgs_i686.pulseaudio != null);
+  enable32BitAlsaPlugins = cfg.support32Bit && stdenv.isx86_64 && (pkgs_i686.alsaLib != null && pkgs_i686.libpulseaudio != null);
 
   ids = config.ids;
 
@@ -78,6 +78,15 @@ in {
         '';
       };
 
+      support32Bit = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to include the 32-bit pulseaudio libraries in the systemn or not.
+          This is only useful on 64-bit systems and currently limited to x86_64-linux.
+        '';
+      };
+
       configFile = mkOption {
         type = types.path;
         description = ''
@@ -89,12 +98,12 @@ in {
 
       package = mkOption {
         type = types.package;
-        default = pulseaudioFull;
+        default = pulseaudioLight;
         example = literalExample "pkgs.pulseaudioFull";
         description = ''
-          The PulseAudio derivation to use.  This can be used to disable
-          features (such as JACK support, Bluetooth) that are enabled in the
-          pulseaudioFull package in Nixpkgs.
+          The PulseAudio derivation to use.  This can be used to enable
+          features (such as JACK support, Bluetooth) via the
+          <literal>pulseaudioFull</literal> package.
         '';
       };
 
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index e5b342afcc41..d0243f9775c5 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -41,20 +41,7 @@ in
         strings.  The latter is concatenated, interspersed with colon
         characters.
       '';
-      type = types.attrsOf (mkOptionType {
-        name = "a string or a list of strings";
-        merge = loc: defs:
-          let
-            defs' = filterOverrides defs;
-            res = (head defs').value;
-          in
-          if isList res then concatLists (getValues defs')
-          else if lessThan 1 (length defs') then
-            throw "The option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}."
-          else if !isString res then
-            throw "The option `${showOption loc}' does not have a string value, in ${showFiles (getFiles defs)}."
-          else res;
-      });
+      type = types.attrsOf (types.loeOf types.str);
       apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
     };
 
@@ -63,15 +50,15 @@ in
       description = ''
         A list of profiles used to setup the global environment.
       '';
-      type = types.listOf types.string;
+      type = types.listOf types.str;
     };
 
     environment.profileRelativeEnvVars = mkOption {
       type = types.attrsOf (types.listOf types.str);
       example = { PATH = [ "/bin" "/sbin" ]; MANPATH = [ "/man" "/share/man" ]; };
       description = ''
-	Attribute set of environment variable.  Each attribute maps to a list
-	of relative paths.  Each relative path is appended to the each profile
+        Attribute set of environment variable.  Each attribute maps to a list
+        of relative paths.  Each relative path is appended to the each profile
         of <option>environment.profiles</option> to form the content of the
         corresponding environment variable.
       '';
@@ -136,6 +123,7 @@ in
         "''${pkgs.dash}/bin/dash"
       '';
       type = types.path;
+      visible = false;
       description = ''
         The shell executable that is linked system-wide to
         <literal>/bin/sh</literal>. Please note that NixOS assumes all
diff --git a/nixos/modules/config/system-environment.nix b/nixos/modules/config/system-environment.nix
index 3ab32f00fd1d..3362400326d2 100644
--- a/nixos/modules/config/system-environment.nix
+++ b/nixos/modules/config/system-environment.nix
@@ -23,20 +23,7 @@ in
         strings.  The latter is concatenated, interspersed with colon
         characters.
       '';
-      type = types.attrsOf (mkOptionType {
-        name = "a string or a list of strings";
-        merge = loc: defs:
-          let
-            defs' = filterOverrides defs;
-            res = (head defs').value;
-          in
-          if isList res then concatLists (getValues defs')
-          else if lessThan 1 (length defs') then
-            throw "The option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}."
-          else if !isString res then
-            throw "The option `${showOption loc}' does not have a string value, in ${showFiles (getFiles defs)}."
-          else res;
-      });
+      type = types.attrsOf (types.loeOf types.str);
       apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
     };
 
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index 58ebea1dabc2..26f4ba5fd706 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -38,13 +38,14 @@ let
       pkgs.nano
       pkgs.ncurses
       pkgs.netcat
-      pkgs.openssh
+      config.programs.ssh.package
       pkgs.perl
       pkgs.procps
       pkgs.rsync
       pkgs.strace
       pkgs.su
       pkgs.time
+      pkgs.texinfoInteractive
       pkgs.utillinux
       extraManpages
     ];
@@ -57,7 +58,7 @@ in
     environment = {
 
       systemPackages = mkOption {
-        type = types.listOf types.path;
+        type = types.listOf types.package;
         default = [];
         example = literalExample "[ pkgs.firefox pkgs.thunderbird ]";
         description = ''
@@ -102,15 +103,24 @@ in
       [ "/bin"
         "/etc/xdg"
         "/info"
-        "/lib"
+        "/lib" # FIXME: remove
+        #"/lib/debug/.build-id" # enables GDB to find separated debug info
         "/man"
         "/sbin"
+        "/share/applications"
+        "/share/desktop-directories"
+        "/share/doc"
         "/share/emacs"
-        "/share/vim-plugins"
-        "/share/org"
+        "/share/icons"
         "/share/info"
-        "/share/terminfo"
         "/share/man"
+        "/share/menus"
+        "/share/mime"
+        "/share/nano"
+        "/share/org"
+        "/share/terminfo"
+        "/share/themes"
+        "/share/vim-plugins"
       ];
 
     system.path = pkgs.buildEnv {
@@ -144,6 +154,13 @@ in
           if [ -x $out/bin/update-desktop-database -a -w $out/share/applications ]; then
               $out/bin/update-desktop-database $out/share/applications
           fi
+
+          if [ -x $out/bin/install-info -a -w $out/share/info ]; then
+            shopt -s nullglob
+            for i in $out/share/info/*.info $out/share/info/*.info.gz; do
+                $out/bin/install-info $i $out/share/info/dir
+            done
+          fi
         '';
     };
 
diff --git a/nixos/modules/config/timezone.nix b/nixos/modules/config/timezone.nix
index 068571393116..b9844b4adade 100644
--- a/nixos/modules/config/timezone.nix
+++ b/nixos/modules/config/timezone.nix
@@ -26,6 +26,7 @@ in
 
       hardwareClockInLocalTime = mkOption {
         default = false;
+        type = types.bool;
         description = "If set, keep the hardware clock in local time instead of UTC.";
       };
 
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 9d48edf2f26c..adc014eed415 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -108,6 +108,15 @@ let
         description = "The user's home directory.";
       };
 
+      cryptHomeLuks = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Path to encrypted luks device that contains
+          the user's home directory.
+        '';
+      };
+
       shell = mkOption {
         type = types.str;
         default = "/run/current-system/sw/bin/nologin";
@@ -207,7 +216,7 @@ let
           exist. If <option>users.mutableUsers</option> is true, the
           password can be changed subsequently using the
           <command>passwd</command> command. Otherwise, it's
-          equivalent to setting the <option>password</option> option.
+          equivalent to setting the <option>hashedPassword</option> option.
 
           ${hashedPasswordDescription}
         '';
@@ -327,13 +336,13 @@ let
     map (range: "${user.name}:${toString range.startUid}:${toString range.count}\n")
       user.subUidRanges);
 
-  subuidFile = concatStrings (map mkSubuidEntry (attrValues cfg.extraUsers));
+  subuidFile = concatStrings (map mkSubuidEntry (attrValues cfg.users));
 
   mkSubgidEntry = user: concatStrings (
     map (range: "${user.name}:${toString range.startGid}:${toString range.count}\n")
         user.subGidRanges);
 
-  subgidFile = concatStrings (map mkSubgidEntry (attrValues cfg.extraUsers));
+  subgidFile = concatStrings (map mkSubgidEntry (attrValues cfg.users));
 
   idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
     let
@@ -345,8 +354,8 @@ let
       else { dup = false; acc = newAcc; }
     ) { dup = false; acc = {}; } (builtins.attrNames set)).dup;
 
-  uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.extraUsers) "uid";
-  gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.extraGroups) "gid";
+  uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.users) "uid";
+  gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.groups) "gid";
 
   spec = pkgs.writeText "users-groups.json" (builtins.toJSON {
     inherit (cfg) mutableUsers;
@@ -355,13 +364,13 @@ let
           name uid group description home shell createHome isSystemUser
           password passwordFile hashedPassword
           initialPassword initialHashedPassword;
-      }) cfg.extraUsers;
+      }) cfg.users;
     groups = mapAttrsToList (n: g:
       { inherit (g) name gid;
         members = g.members ++ (mapAttrsToList (n: u: u.name) (
-          filterAttrs (n: u: elem g.name u.extraGroups) cfg.extraUsers
+          filterAttrs (n: u: elem g.name u.extraGroups) cfg.users
         ));
-      }) cfg.extraGroups;
+      }) cfg.groups;
   });
 
 in {
@@ -379,10 +388,10 @@ in {
         <literal>groupadd</literal> commands. On system activation, the
         existing contents of the <literal>/etc/passwd</literal> and
         <literal>/etc/group</literal> files will be merged with the
-        contents generated from the <literal>users.extraUsers</literal> and
-        <literal>users.extraGroups</literal> options.
+        contents generated from the <literal>users.users</literal> and
+        <literal>users.groups</literal> options.
         The initial password for a user will be set
-        according to <literal>users.extraUsers</literal>, but existing passwords
+        according to <literal>users.users</literal>, but existing passwords
         will not be changed.
 
         <warning><para>
@@ -390,7 +399,7 @@ in {
         group files will simply be replaced on system activation. This also
         holds for the user passwords; all changed
         passwords will be reset according to the
-        <literal>users.extraUsers</literal> configuration on activation.
+        <literal>users.users</literal> configuration on activation.
         </para></warning>
       '';
     };
@@ -403,7 +412,7 @@ in {
       '';
     };
 
-    users.extraUsers = mkOption {
+    users.users = mkOption {
       default = {};
       type = types.loaOf types.optionSet;
       example = {
@@ -424,7 +433,7 @@ in {
       options = [ userOpts ];
     };
 
-    users.extraGroups = mkOption {
+    users.groups = mkOption {
       default = {};
       example =
         { students.gid = 1001;
@@ -452,7 +461,7 @@ in {
 
   config = {
 
-    users.extraUsers = {
+    users.users = {
       root = {
         uid = ids.uids.root;
         description = "System administrator";
@@ -469,7 +478,7 @@ in {
       };
     };
 
-    users.extraGroups = {
+    users.groups = {
       root.gid = ids.gids.root;
       wheel.gid = ids.gids.wheel;
       disk.gid = ids.gids.disk;
@@ -516,6 +525,27 @@ in {
       { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
         message = "UIDs and GIDs must be unique!";
       }
+      { # If mutableUsers is false, to prevent users creating a
+        # configuration that locks them out of the system, ensure that
+        # there is at least one "privileged" account that has a
+        # password or an SSH authorized key. Privileged accounts are
+        # root and users in the wheel group.
+        assertion = !cfg.mutableUsers ->
+          any id (mapAttrsToList (name: cfg:
+            (name == "root"
+             || cfg.group == "wheel"
+             || elem "wheel" cfg.extraGroups)
+            &&
+            ((cfg.hashedPassword != null && cfg.hashedPassword != "!")
+             || cfg.password != null
+             || cfg.passwordFile != null
+             || cfg.openssh.authorizedKeys.keys != []
+             || cfg.openssh.authorizedKeys.keyFiles != [])
+          ) cfg.users);
+        message = ''
+          Neither the root account nor any wheel user has a password or SSH authorized key.
+          You must set one to prevent being locked out of your system.'';
+      }
     ];
 
   };