Turn security.pam.services into an attribute set

That is, you can say

  security.pam.services.sshd = { options... };

instead of

  security.pam.services = [ { name = "sshd"; options... } ];

making it easier to override PAM settings from other modules.
This commit is contained in:
Eelco Dolstra 2013-10-15 14:47:51 +02:00
parent 3f2c0bf9bd
commit a2c820c678
15 changed files with 225 additions and 162 deletions

View file

@ -79,22 +79,22 @@ in
]; ];
security.pam.services = security.pam.services =
[ { name = "chsh"; rootOK = true; } { chsh = { rootOK = true; };
{ name = "chfn"; rootOK = true; } chfn = { rootOK = true; };
{ name = "su"; rootOK = true; forwardXAuth = true; } su = { rootOK = true; forwardXAuth = true; };
{ name = "passwd"; } passwd = {};
# Note: useradd, groupadd etc. aren't setuid root, so it # Note: useradd, groupadd etc. aren't setuid root, so it
# doesn't really matter what the PAM config says as long as it # doesn't really matter what the PAM config says as long as it
# lets root in. # lets root in.
{ name = "useradd"; rootOK = true; } useradd = { rootOK = true; };
{ name = "usermod"; rootOK = true; } usermod = { rootOK = true; };
{ name = "userdel"; rootOK = true; } userdel = { rootOK = true; };
{ name = "groupadd"; rootOK = true; } groupadd = { rootOK = true; };
{ name = "groupmod"; rootOK = true; } groupmod = { rootOK = true; };
{ name = "groupmems"; rootOK = true; } groupmems = { rootOK = true; };
{ name = "groupdel"; rootOK = true; } groupdel = { rootOK = true; };
{ name = "login"; startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; } login = { startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; };
]; };
security.setuidPrograms = [ "passwd" "chfn" "su" "newgrp" ]; security.setuidPrograms = [ "passwd" "chfn" "su" "newgrp" ];

View file

@ -7,77 +7,138 @@ with pkgs.lib;
let let
inherit (pkgs) pam_krb5 pam_ccreds; pamOpts = args: {
pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap; options = {
otherService = pkgs.writeText "other.pam" name = mkOption {
'' example = "sshd";
auth required pam_warn.so type = types.uniq types.string;
auth required pam_deny.so description = "Name of the PAM service.";
account required pam_warn.so };
account required pam_deny.so
password required pam_warn.so
password required pam_deny.so
session required pam_warn.so
session required pam_deny.so
'';
# Create a limits.conf(5) file. rootOK = mkOption {
makeLimitsConf = limits: default = false;
pkgs.writeText "limits.conf" type = types.bool;
(concatStringsSep "\n" description = ''
(map ({ domain, type, item, value }: If set, root doesn't need to authenticate (e.g. for the
concatStringsSep " " [ domain type item value ]) <command>useradd</command> service).
limits)); '';
};
motd = pkgs.writeText "motd" config.users.motd; usbAuth = mkOption {
default = config.security.pam.usb.enable;
type = types.bool;
description = ''
If set, users listed in
<filename>/etc/pamusb.conf</filename> are able to log in
with the associated USB key.
'';
};
makePAMService = otpwAuth = mkOption {
{ name default = config.security.pam.enableOTPW;
, # If set, root doesn't need to authenticate (e.g. for the "chsh" type = types.bool;
# service). description = ''
rootOK ? false If set, the OTPW system will be used (if
, # If set, user listed in /etc/pamusb.conf are able to log in with <filename>~/.otpw</filename> exists).
# the associated usb key. '';
usbAuth ? config.security.pam.usb.enable };
, # If set, OTPW system will be used (if ~/.otpw exists)
otpwAuth ? config.security.pam.enableOTPW
, # If set, the calling user's SSH agent is used to authenticate
# against the keys in the calling user's ~/.ssh/authorized_keys.
# This is useful for "sudo" on password-less remote systems.
sshAgentAuth ? false
, # If set, the service will register a new session with systemd's
# login manager. If the service is running locally, this will
# give the user ownership of audio devices etc.
startSession ? false
, # Set the login uid of the process (/proc/self/loginuid) for
# auditing purposes. The login uid is only set by "entry
# points" like login and sshd, not by commands like sudo.
setLoginUid ? startSession
, # Whether to forward XAuth keys between users. Mostly useful
# for "su".
forwardXAuth ? false
, # Whether to allow logging into accounts that have no password
# set (i.e., have an empty password field in /etc/passwd or
# /etc/group). This does not enable logging into disabled
# accounts (i.e., that have the password field set to `!').
# Note that regardless of what the pam_unix documentation says,
# accounts with hashed empty passwords are always allowed to log
# in.
allowNullPassword ? false
, # The limits, as per limits.conf(5).
limits ? config.security.pam.loginLimits
, # Whether to show the message of the day.
showMotd ? false
, # Whether to update /var/log/wtmp.
updateWtmp ? false
}:
{ source = pkgs.writeText "${name}.pam" sshAgentAuth = mkOption {
# !!! TODO: move the LDAP stuff to the LDAP module, and the default = false;
# Samba stuff to the Samba module. This requires that the PAM type = types.bool;
# module provides the right hooks. description = ''
If set, the calling user's SSH agent is used to authenticate
against the keys in the calling user's
<filename>~/.ssh/authorized_keys</filename>. This is useful
for <command>sudo</command> on password-less remote systems.
'';
};
startSession = mkOption {
default = false;
type = types.bool;
description = ''
If set, the service will register a new session with
systemd's login manager. For local sessions, this will give
the user access to audio devices, CD-ROM drives. In the
default PolicyKit configuration, it also allows the user to
reboot the system.
'';
};
setLoginUid = mkOption {
type = types.bool;
description = ''
Set the login uid of the process
(<filename>/proc/self/loginuid</filename>) for auditing
purposes. The login uid is only set by entry points like
<command>login</command> and <command>sshd</command>, not by
commands like <command>sudo</command>.
'';
};
forwardXAuth = mkOption {
default = false;
type = types.bool;
description = ''
Whether X authentication keys should be passed from the
calling user to the target user (e.g. for
<command>su</command>)
'';
};
allowNullPassword = mkOption {
default = false;
type = types.bool;
description = ''
Whether to allow logging into accounts that have no password
set (i.e., have an empty password field in
<filename>/etc/passwd</filename> or
<filename>/etc/group</filename>). This does not enable
logging into disabled accounts (i.e., that have the password
field set to <literal>!</literal>). Note that regardless of
what the pam_unix documentation says, accounts with hashed
empty passwords are always allowed to log in.
'';
};
limits = mkOption {
description = ''
Attribute set describing resource limits. Defaults to the
value of <option>security.pam.loginLimits</option>.
'';
};
showMotd = mkOption {
default = false;
type = types.bool;
description = "Whether to show the message of the day.";
};
updateWtmp = mkOption {
default = false;
type = types.bool;
description = "Whether to update <filename>/var/log/wtmp</filename>.";
};
text = mkOption {
type = types.nullOr types.string;
description = "Contents of the PAM service file.";
};
};
config = let cfg = args.config; in {
name = mkDefault args.name;
setLoginUid = mkDefault cfg.startSession;
limits = mkDefault config.security.pam.loginLimits;
# !!! TODO: move the LDAP stuff to the LDAP module, and the
# Samba stuff to the Samba module. This requires that the PAM
# module provides the right hooks.
text = mkDefault
'' ''
# Account management. # Account management.
account sufficient pam_unix.so account sufficient pam_unix.so
@ -87,14 +148,14 @@ let
"account sufficient ${pam_krb5}/lib/security/pam_krb5.so"} "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
# Authentication management. # Authentication management.
${optionalString rootOK ${optionalString cfg.rootOK
"auth sufficient pam_rootok.so"} "auth sufficient pam_rootok.so"}
${optionalString (config.security.pam.enableSSHAgentAuth && sshAgentAuth) ${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth)
"auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=~/.ssh/authorized_keys:~/.ssh/authorized_keys2:/etc/ssh/authorized_keys.d/%u"} "auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=~/.ssh/authorized_keys:~/.ssh/authorized_keys2:/etc/ssh/authorized_keys.d/%u"}
${optionalString usbAuth ${optionalString cfg.usbAuth
"auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"} "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
auth sufficient pam_unix.so ${optionalString allowNullPassword "nullok"} likeauth auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth
${optionalString otpwAuth ${optionalString cfg.otpwAuth
"auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"} "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
${optionalString config.users.ldap.enable ${optionalString config.users.ldap.enable
"auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"} "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
@ -116,26 +177,47 @@ let
# Session management. # Session management.
session required pam_unix.so session required pam_unix.so
${optionalString updateWtmp ${optionalString cfg.updateWtmp
"session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"} "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
${optionalString config.users.ldap.enable ${optionalString config.users.ldap.enable
"session optional ${pam_ldap}/lib/security/pam_ldap.so"} "session optional ${pam_ldap}/lib/security/pam_ldap.so"}
${optionalString config.krb5.enable ${optionalString config.krb5.enable
"session optional ${pam_krb5}/lib/security/pam_krb5.so"} "session optional ${pam_krb5}/lib/security/pam_krb5.so"}
${optionalString otpwAuth ${optionalString cfg.otpwAuth
"session optional ${pkgs.otpw}/lib/security/pam_otpw.so"} "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"}
${optionalString startSession ${optionalString cfg.startSession
"session optional ${pkgs.systemd}/lib/security/pam_systemd.so"} "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
${optionalString setLoginUid ${optionalString cfg.setLoginUid
"session required pam_loginuid.so"} "session required pam_loginuid.so"}
${optionalString forwardXAuth ${optionalString cfg.forwardXAuth
"session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"} "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"}
${optionalString (limits != []) ${optionalString (cfg.limits != [])
"session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf limits}"} "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf limits}"}
${optionalString (showMotd && config.users.motd != null) ${optionalString (cfg.showMotd && config.users.motd != null)
"session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"} "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"}
''; '';
target = "pam.d/${name}"; };
};
inherit (pkgs) pam_krb5 pam_ccreds;
pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
# Create a limits.conf(5) file.
makeLimitsConf = limits:
pkgs.writeText "limits.conf"
(concatStringsSep "\n"
(map ({ domain, type, item, value }:
concatStringsSep " " [ domain type item value ])
limits));
motd = pkgs.writeText "motd" config.users.motd;
makePAMService = pamService:
{ source = pkgs.writeText "${pamService.name}.pam" pamService.text;
target = "pam.d/${pamService.name}";
}; };
in in
@ -173,44 +255,15 @@ in
security.pam.services = mkOption { security.pam.services = mkOption {
default = []; default = [];
example = [ type = types.loaOf types.optionSet;
{ name = "chsh"; rootOK = true; } options = [ pamOpts ];
{ name = "login"; startSession = true; allowNullPassword = true;
limits = [
{ domain = "ftp";
type = "hard";
item = "nproc";
value = "0";
}
];
}
];
description = description =
'' ''
This option defines the PAM services. A service typically This option defines the PAM services. A service typically
corresponds to a program that uses PAM, corresponds to a program that uses PAM,
e.g. <command>login</command> or <command>passwd</command>. e.g. <command>login</command> or <command>passwd</command>.
Each element of this list is an attribute set describing a Each attribute of this set defines a PAM service, with the attribute name
service. The attribute <varname>name</varname> specifies defining the name of the service.
the name of the service. The attribute
<varname>rootOK</varname> specifies whether the root user is
allowed to use this service without authentication. The
attribute <varname>startSession</varname> specifies whether
systemd's PAM connector module should be used to start a new
session; for local sessions, this will give the user
ownership of devices such as audio and CD-ROM drives. The
attribute <varname>forwardXAuth</varname> specifies whether
X authentication keys should be passed from the calling user
to the target user (e.g. for <command>su</command>).
The attribute <varname>limits</varname> defines resource limits
that should apply to users or groups for the service. Each item in
the list should be an attribute set with a
<varname>domain</varname>, <varname>type</varname>,
<varname>item</varname>, and <varname>value</varname> attribute.
The syntax and semantics of these attributes must be that described
in the limits.conf(5) man page.
''; '';
}; };
@ -228,7 +281,7 @@ in
security.pam.enableOTPW = mkOption { security.pam.enableOTPW = mkOption {
default = false; default = false;
description = '' description = ''
Enable the OTPW (one-time password) PAM module Enable the OTPW (one-time password) PAM module.
''; '';
}; };
@ -254,11 +307,7 @@ in
++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]; ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ];
environment.etc = environment.etc =
map makePAMService config.security.pam.services mapAttrsToList (n: v: makePAMService v) config.security.pam.services;
++ singleton
{ source = otherService;
target = "pam.d/other";
};
security.setuidOwners = [ { security.setuidOwners = [ {
program = "unix_chkpwd"; program = "unix_chkpwd";
@ -268,18 +317,27 @@ in
} ]; } ];
security.pam.services = security.pam.services =
# Most of these should be moved to specific modules. { other.text =
[ { name = "cups"; } ''
{ name = "ejabberd"; } auth required pam_warn.so
{ name = "ftp"; } auth required pam_deny.so
{ name = "i3lock"; } account required pam_warn.so
{ name = "lshd"; } account required pam_deny.so
{ name = "samba"; } password required pam_warn.so
{ name = "screen"; } password required pam_deny.so
{ name = "vlock"; } session required pam_warn.so
{ name = "xlock"; } session required pam_deny.so
{ name = "xscreensaver"; } '';
];
# Most of these should be moved to specific modules.
cups = {};
ftp = {};
i3lock = {};
screen = {};
vlock = {};
xlock = {};
xscreensaver = {};
};
}; };

View file

@ -8,7 +8,7 @@ let
cfg = config.security.pam.usb; cfg = config.security.pam.usb;
anyUsbAuth = any (attrByPath ["usbAuth"] false) config.security.pam.services; anyUsbAuth = any (attrByPath ["usbAuth"] false) (attrValues config.security.pam.services);
in in
@ -19,8 +19,8 @@ in
enable = mkOption { enable = mkOption {
default = false; default = false;
description = '' description = ''
Enable USB login for all login system unless the service disabled Enable USB login for all login systems that support it. For
it. For more information, visit <link more information, visit <link
xlink:href="http://pamusb.org/doc/quickstart#setting_up" />. xlink:href="http://pamusb.org/doc/quickstart#setting_up" />.
''; '';
}; };

View file

@ -94,7 +94,7 @@ in
services.dbus.packages = [ pkgs.polkit ]; services.dbus.packages = [ pkgs.polkit ];
security.pam.services = [ { name = "polkit-1"; } ]; security.pam.services.polkit-1 = {};
security.setuidPrograms = [ "pkexec" ]; security.setuidPrograms = [ "pkexec" ];

View file

@ -74,7 +74,7 @@ in
environment.systemPackages = [ sudo ]; environment.systemPackages = [ sudo ];
security.pam.services = [ { name = "sudo"; sshAgentAuth = true; } ]; security.pam.services.sudo = { sshAgentAuth = true; };
environment.etc = singleton environment.etc = singleton
{ source = pkgs.writeText "sudoers-in" cfg.configFile; { source = pkgs.writeText "sudoers-in" cfg.configFile;

View file

@ -124,7 +124,7 @@ in
config = mkIf config.services.dovecot2.enable { config = mkIf config.services.dovecot2.enable {
security.pam.services = [ { name = "dovecot2"; } ]; security.pam.services.dovecot2 = {};
users.extraUsers = [ users.extraUsers = [
{ name = cfg.user; { name = cfg.user;

View file

@ -198,6 +198,7 @@ in
} }
(mkIf config.services.samba.enable { (mkIf config.services.samba.enable {
users.extraUsers.smbguest = { users.extraUsers.smbguest = {
description = "Samba service user"; description = "Samba service user";
group = group; group = group;
@ -228,6 +229,8 @@ in
}; };
}; };
security.pam.services.sambda = {};
}) })
]; ];

View file

@ -130,6 +130,8 @@ in
''; '';
}; };
security.pam.services.ejabberd = {};
}; };
} }

View file

@ -170,6 +170,8 @@ in
''; '';
}; };
security.pam.services.lshd = {};
}; };
} }

View file

@ -219,5 +219,7 @@ in
# Allow CUPS to receive IPP printer announcements via UDP. # Allow CUPS to receive IPP printer announcements via UDP.
networking.firewall.allowedUDPPorts = [ 631 ]; networking.firewall.allowedUDPPorts = [ 631 ];
security.pam.services.cups = {};
}; };
} }

View file

@ -49,7 +49,7 @@ in
environment.systemPackages = [ at ]; environment.systemPackages = [ at ];
security.pam.services = [ { name = "atd"; } ]; security.pam.services.atd = {};
users.extraUsers = singleton users.extraUsers = singleton
{ name = "atd"; { name = "atd";

View file

@ -162,7 +162,7 @@ in
services.udisks2.enable = wantsUdisks2; services.udisks2.enable = wantsUdisks2;
services.upower.enable = config.powerManagement.enable; services.upower.enable = config.powerManagement.enable;
security.pam.services = [ { name = "kde"; allowNullPassword = true; startSession = true; } ]; security.pam.services.kde = { allowNullPassword = true; };
}; };

View file

@ -138,7 +138,7 @@ in
logsXsession = true; logsXsession = true;
}; };
security.pam.services = [ { name = "kde"; allowNullPassword = true; startSession = true; } ]; security.pam.services.kde = { allowNullPassword = true; startSession = true; };
users.extraUsers = singleton users.extraUsers = singleton
{ name = "kdm"; { name = "kdm";

View file

@ -102,10 +102,8 @@ in
services.dbus.enable = true; services.dbus.enable = true;
services.dbus.packages = [ lightdm ]; services.dbus.packages = [ lightdm ];
security.pam.services = [ security.pam.services.lightdm = { allowNullPassword = true; startSession = true; };
{ name = "lightdm"; allowNullPassword = true; startSession = true; } security.pam.services.lightdm-greeter = { allowNullPassword = true; startSession = true; };
{ name = "lightdm-greeter"; allowNullPassword = true; startSession = true; }
];
users.extraUsers.lightdm = { users.extraUsers.lightdm = {
createHome = true; createHome = true;

View file

@ -104,14 +104,12 @@ in
execCmd = "exec ${pkgs.slim}/bin/slim"; execCmd = "exec ${pkgs.slim}/bin/slim";
}; };
security.pam.services = # Allow null passwords so that the user can login as root on the
[ # Allow null passwords so that the user can login as root on the # installation CD.
# installation CD. security.pam.services.slim = { allowNullPassword = true; startSession = true; };
{ name = "slim"; allowNullPassword = true; startSession = true; }
# Allow slimlock to work. # Allow slimlock to work.
{ name = "slimlock"; } security.pam.services.slimlock = {};
];
environment.systemPackages = [ pkgs.slim ]; environment.systemPackages = [ pkgs.slim ];