···11# Configuration for the pwdutils suite of tools: passwd, useradd, etc.
22-32{ config, lib, utils, pkgs, ... }:
44-53with lib;
66-74let
55+ cfg = config.security.loginDefs;
66+in
77+{
88+ options = with types; {
99+ security.loginDefs = {
1010+ package = mkPackageOptionMD pkgs "shadow" { };
81199- /*
1010- There are three different sources for user/group id ranges, each of which gets
1111- used by different programs:
1212- - The login.defs file, used by the useradd, groupadd and newusers commands
1313- - The update-users-groups.pl file, used by NixOS in the activation phase to
1414- decide on which ids to use for declaratively defined users without a static
1515- id
1616- - Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
1717- by systemd for features like ConditionUser=@system and systemd-sysusers
1818- */
1919- loginDefs =
2020- ''
2121- DEFAULT_HOME yes
1212+ chfnRestrict = mkOption {
1313+ description = mdDoc ''
1414+ Use chfn SUID to allow non-root users to change their account GECOS information.
1515+ '';
1616+ type = nullOr str;
1717+ default = null;
1818+ };
22192323- SYS_UID_MIN 400
2424- SYS_UID_MAX 999
2525- UID_MIN 1000
2626- UID_MAX 29999
2020+ settings = mkOption {
2121+ description = mdDoc ''
2222+ Config options for the /etc/login.defs file, that defines
2323+ the site-specific configuration for the shadow password suite.
2424+ See login.defs(5) man page for available options.
2525+ '';
2626+ type = submodule {
2727+ freeformType = (pkgs.formats.keyValue { }).type;
2828+ /* There are three different sources for user/group id ranges, each of which gets
2929+ used by different programs:
3030+ - The login.defs file, used by the useradd, groupadd and newusers commands
3131+ - The update-users-groups.pl file, used by NixOS in the activation phase to
3232+ decide on which ids to use for declaratively defined users without a static
3333+ id
3434+ - Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
3535+ by systemd for features like ConditionUser=@system and systemd-sysusers
3636+ */
3737+ options = {
3838+ DEFAULT_HOME = mkOption {
3939+ description = mdDoc "Indicate if login is allowed if we can't cd to the home directory.";
4040+ default = "yes";
4141+ type = enum [ "yes" "no" ];
4242+ };
27432828- SYS_GID_MIN 400
2929- SYS_GID_MAX 999
3030- GID_MIN 1000
3131- GID_MAX 29999
4444+ ENCRYPT_METHOD = mkOption {
4545+ description = mdDoc "This defines the system default encryption algorithm for encrypting passwords.";
4646+ # The default crypt() method, keep in sync with the PAM default
4747+ default = "YESCRYPT";
4848+ type = enum [ "YESCRYPT" "SHA512" "SHA256" "MD5" "DES"];
4949+ };
32503333- TTYGROUP tty
3434- TTYPERM 0620
5151+ SYS_UID_MIN = mkOption {
5252+ description = mdDoc "Range of user IDs used for the creation of system users by useradd or newusers.";
5353+ default = 400;
5454+ type = int;
5555+ };
35563636- # Ensure privacy for newly created home directories.
3737- UMASK 077
5757+ SYS_UID_MAX = mkOption {
5858+ description = mdDoc "Range of user IDs used for the creation of system users by useradd or newusers.";
5959+ default = 999;
6060+ type = int;
6161+ };
38623939- # Uncomment this and install chfn SUID to allow non-root
4040- # users to change their account GECOS information.
4141- # This should be made configurable.
4242- #CHFN_RESTRICT frwh
6363+ UID_MIN = mkOption {
6464+ description = mdDoc "Range of user IDs used for the creation of regular users by useradd or newusers.";
6565+ default = 1000;
6666+ type = int;
6767+ };
43684444- # The default crypt() method, keep in sync with the PAM default
4545- ENCRYPT_METHOD YESCRYPT
4646- '';
6969+ UID_MAX = mkOption {
7070+ description = mdDoc "Range of user IDs used for the creation of regular users by useradd or newusers.";
7171+ default = 29999;
7272+ type = int;
7373+ };
47744848- mkSetuidRoot = source:
4949- { setuid = true;
5050- owner = "root";
5151- group = "root";
5252- inherit source;
5353- };
7575+ SYS_GID_MIN = mkOption {
7676+ description = mdDoc "Range of group IDs used for the creation of system groups by useradd, groupadd, or newusers";
7777+ default = 400;
7878+ type = int;
7979+ };
54805555-in
8181+ SYS_GID_MAX = mkOption {
8282+ description = mdDoc "Range of group IDs used for the creation of system groups by useradd, groupadd, or newusers";
8383+ default = 999;
8484+ type = int;
8585+ };
56865757-{
8787+ GID_MIN = mkOption {
8888+ description = mdDoc "Range of group IDs used for the creation of regular groups by useradd, groupadd, or newusers.";
8989+ default = 1000;
9090+ type = int;
9191+ };
58925959- ###### interface
9393+ GID_MAX = mkOption {
9494+ description = mdDoc "Range of group IDs used for the creation of regular groups by useradd, groupadd, or newusers.";
9595+ default = 29999;
9696+ type = int;
9797+ };
60986161- options = {
9999+ TTYGROUP = mkOption {
100100+ description = mdDoc ''
101101+ The terminal permissions: the login tty will be owned by the TTYGROUP group,
102102+ and the permissions will be set to TTYPERM'';
103103+ default = "tty";
104104+ type = str;
105105+ };
621066363- users.defaultUserShell = lib.mkOption {
6464- description = lib.mdDoc ''
107107+ TTYPERM = mkOption {
108108+ description = mdDoc ''
109109+ The terminal permissions: the login tty will be owned by the TTYGROUP group,
110110+ and the permissions will be set to TTYPERM'';
111111+ default = "0620";
112112+ type = str;
113113+ };
114114+115115+ # Ensure privacy for newly created home directories.
116116+ UMASK = mkOption {
117117+ description = mdDoc "The file mode creation mask is initialized to this value.";
118118+ default = "077";
119119+ type = str;
120120+ };
121121+ };
122122+ };
123123+ default = { };
124124+ };
125125+ };
126126+127127+ users.defaultUserShell = mkOption {
128128+ description = mdDoc ''
65129 This option defines the default shell assigned to user
66130 accounts. This can be either a full system path or a shell package.
67131···69133 used outside the store (in particular in /etc/passwd).
70134 '';
71135 example = literalExpression "pkgs.zsh";
7272- type = types.either types.path types.shellPackage;
136136+ type = either path shellPackage;
73137 };
7474-75138 };
7676-7713978140 ###### implementation
7914180142 config = {
143143+ assertions = [
144144+ {
145145+ assertion = cfg.settings.SYS_UID_MIN <= cfg.settings.SYS_UID_MAX;
146146+ message = "SYS_UID_MIN must be less than or equal to SYS_UID_MAX";
147147+ }
148148+ {
149149+ assertion = cfg.settings.UID_MIN <= cfg.settings.UID_MAX;
150150+ message = "UID_MIN must be less than or equal to UID_MAX";
151151+ }
152152+ {
153153+ assertion = cfg.settings.SYS_GID_MIN <= cfg.settings.SYS_GID_MAX;
154154+ message = "SYS_GID_MIN must be less than or equal to SYS_GID_MAX";
155155+ }
156156+ {
157157+ assertion = cfg.settings.GID_MIN <= cfg.settings.GID_MAX;
158158+ message = "GID_MIN must be less than or equal to GID_MAX";
159159+ }
160160+ ];
811618282- environment.systemPackages =
8383- lib.optional config.users.mutableUsers pkgs.shadow ++
8484- lib.optional (types.shellPackage.check config.users.defaultUserShell)
8585- config.users.defaultUserShell;
162162+ security.loginDefs.settings.CHFN_RESTRICT =
163163+ mkIf (cfg.chfnRestrict != null) cfg.chfnRestrict;
164164+165165+ environment.systemPackages = optional config.users.mutableUsers cfg.package
166166+ ++ optional (types.shellPackage.check config.users.defaultUserShell) config.users.defaultUserShell
167167+ ++ optional (cfg.chfnRestrict != null) pkgs.util-linux;
8616887169 environment.etc =
8888- { # /etc/login.defs: global configuration for pwdutils. You
8989- # cannot login without it!
9090- "login.defs".source = pkgs.writeText "login.defs" loginDefs;
170170+ # Create custom toKeyValue generator
171171+ # see https://man7.org/linux/man-pages/man5/login.defs.5.html for config specification
172172+ let
173173+ toKeyValue = generators.toKeyValue {
174174+ mkKeyValue = generators.mkKeyValueDefault { } " ";
175175+ };
176176+ in
177177+ {
178178+ # /etc/login.defs: global configuration for pwdutils.
179179+ # You cannot login without it!
180180+ "login.defs".source = pkgs.writeText "login.defs" (toKeyValue cfg.settings);
9118192182 # /etc/default/useradd: configuration for useradd.
9393- "default/useradd".source = pkgs.writeText "useradd"
9494- ''
9595- GROUP=100
9696- HOME=/home
9797- SHELL=${utils.toShellPath config.users.defaultUserShell}
9898- '';
183183+ "default/useradd".source = pkgs.writeText "useradd" ''
184184+ GROUP=100
185185+ HOME=/home
186186+ SHELL=${utils.toShellPath config.users.defaultUserShell}
187187+ '';
99188 };
100189101101- security.pam.services =
102102- { chsh = { rootOK = true; };
103103- chfn = { rootOK = true; };
104104- su = { rootOK = true; forwardXAuth = true; logFailures = true; };
105105- passwd = {};
106106- # Note: useradd, groupadd etc. aren't setuid root, so it
107107- # doesn't really matter what the PAM config says as long as it
108108- # lets root in.
109109- useradd = { rootOK = true; };
110110- usermod = { rootOK = true; };
111111- userdel = { rootOK = true; };
112112- groupadd = { rootOK = true; };
113113- groupmod = { rootOK = true; };
114114- groupmems = { rootOK = true; };
115115- groupdel = { rootOK = true; };
116116- login = { startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; };
117117- chpasswd = { rootOK = true; };
190190+ security.pam.services = {
191191+ chsh = { rootOK = true; };
192192+ chfn = { rootOK = true; };
193193+ su = {
194194+ rootOK = true;
195195+ forwardXAuth = true;
196196+ logFailures = true;
118197 };
119119-120120- security.wrappers = {
121121- su = mkSetuidRoot "${pkgs.shadow.su}/bin/su";
122122- sg = mkSetuidRoot "${pkgs.shadow.out}/bin/sg";
123123- newgrp = mkSetuidRoot "${pkgs.shadow.out}/bin/newgrp";
124124- newuidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newuidmap";
125125- newgidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newgidmap";
126126- } // lib.optionalAttrs config.users.mutableUsers {
127127- chsh = mkSetuidRoot "${pkgs.shadow.out}/bin/chsh";
128128- passwd = mkSetuidRoot "${pkgs.shadow.out}/bin/passwd";
198198+ passwd = { };
199199+ # Note: useradd, groupadd etc. aren't setuid root, so it
200200+ # doesn't really matter what the PAM config says as long as it
201201+ # lets root in.
202202+ useradd.rootOK = true;
203203+ usermod.rootOK = true;
204204+ userdel.rootOK = true;
205205+ groupadd.rootOK = true;
206206+ groupmod.rootOK = true;
207207+ groupmems.rootOK = true;
208208+ groupdel.rootOK = true;
209209+ login = {
210210+ startSession = true;
211211+ allowNullPassword = true;
212212+ showMotd = true;
213213+ updateWtmp = true;
214214+ };
215215+ chpasswd = { rootOK = true; };
129216 };
217217+218218+ security.wrappers =
219219+ let
220220+ mkSetuidRoot = source: {
221221+ setuid = true;
222222+ owner = "root";
223223+ group = "root";
224224+ inherit source;
225225+ };
226226+ in
227227+ {
228228+ su = mkSetuidRoot "${cfg.package.su}/bin/su";
229229+ sg = mkSetuidRoot "${cfg.package.out}/bin/sg";
230230+ newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp";
231231+ newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap";
232232+ newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap";
233233+ }
234234+ // optionalAttrs config.users.mutableUsers {
235235+ chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";
236236+ passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd";
237237+ };
130238 };
131239}