Last week I was explaining the important distinction between authentication and authorization, and how Linux handles them in PAM and polkit, respectively.
The PolicyKit Authentication Framework or polkit controls how subjects or unprivileged programs (such as a user’s shell) can be allowed to run mechanisms or privileged programs (such as normally root
-only programs like mount
or reboot
) after authenticating simply as the current user or as a user who knows the root
password.
Use the pkaction
command to list the set of possible polkit actions. There’s a lot of output, let’s see those possibly related to rebooting and shutting down as configured by Red Hat on RHEL 7. The ones we’re really interested in are highlighted here:
# pkaction | egrep 'power|shutdown|reboot|halt' org.freedesktop.login1.inhibit-block-shutdown org.freedesktop.login1.inhibit-delay-shutdown org.freedesktop.login1.inhibit-handle-power-key org.freedesktop.login1.power-off org.freedesktop.login1.power-off-ignore-inhibit org.freedesktop.login1.power-off-multiple-sessions org.freedesktop.login1.reboot org.freedesktop.login1.reboot-ignore-inhibit org.freedesktop.login1.reboot-multiple-sessions org.freedesktop.udisks2.ata-check-power org.freedesktop.udisks2.power-off-drive org.freedesktop.udisks2.power-off-drive-other-seat org.freedesktop.udisks2.power-off-drive-system org.freedesktop.upower.hibernate org.freedesktop.upower.qos.cancel-request org.freedesktop.upower.qos.request-latency org.freedesktop.upower.qos.request-latency-persistent org.freedesktop.upower.qos.set-minimum-latency org.freedesktop.upower.suspend org.gnome.settings-daemon.plugins.power.backlight-helper
Mechanisms define the set of possible actions in XML files named /usr/share/polkit-1/actions/*.policy
. Based on the above output, we want to change to the /usr/share/polkit-1/actions
directory and examine the contents of the file org.freedsktop.login1.policy
.
Within that file we will look for blocks such as:
<action id="org.freedesktop.login1.reboot">
Notice that three defaults have been defined:
<allow_any>auth_admin_keep</allow_any>
This means that any client can do this if they can provide the root password. If they have done this recently (e.g., 5 minutes but this of course is also configurable), authentication doesn’t have to be re-done.
<allow_inactive>auth_admin_keep</allow_inactive>
This means the same thing for a client in an inactive session on a local console.
<allow_active>yes</allow_active>
This means that a client in an active session on a local console can simply do this without any authentication. This is the line that allows random users to reboot.
We mustn’t edit any of these policy files, because future updates to the polkit packages would overwrite our changes. We must change the behavior with rules.
Rules applying to those policies about actions and the authentication methods are written in Java and are stored in /usr/share/polkit-1/rules.d/*.rules
and /etc/polkit-1/rules.d/*.rules
. The polkitd
daemon watches both directories so it should notice your changes. Packages may put rule files into either location.
Make your changes by adding new files named /etc/polkit-1/rules.d/*.rules
.
Rules are sorted into lexical order by the file names, with files in /etc
before those in /usr
if you have two files with the same name in both areas. Start your file names with 2-digit numbers as that’s the environment already in place. As the polkit(8)
manual page explains, the following order would be used for these four files:
/etc/polkit-1/rules.d/10-auth.rules
/usr/share/polkit-1/rules.d/10-auth.rules
/etc/polkit-1/rules.d/15-auth.rules
/usr/share/polkit-1/rules.d/20-auth.rules
Let’s try this! Let’s make the following policy change:
In order to shut down or reboot, even if an application has asked for those actions to be delayed until it is finished, and even if there are others logged in, you must enter the root
password.
Create /etc/polkit-1/rules.d/10-shutdown.rules
with this content:
polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.login1.reboot" || action.id == "org.freedesktop.login1.reboot-ignore-inhibit" || action.id == "org.freedesktop.login1.reboot-multiple-sessions" || action.id == "org.freedesktop.login1.power-off" || action.id == "org.freedesktop.login1.power-off-ignore-inhibit" || action.id == "org.freedesktop.login1.power-off-multiple-sessions") { return polkit.Result.AUTH_ADMIN; } });
Or maybe you would prefer to allow all members of group wheel
to do this without further authentication, while everyone else will be asked for the root
password. Use this:
polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.login1.reboot" || action.id == "org.freedesktop.login1.reboot-ignore-inhibit" || action.id == "org.freedesktop.login1.reboot-multiple-sessions" || action.id == "org.freedesktop.login1.power-off" || action.id == "org.freedesktop.login1.power-off-ignore-inhibit" || action.id == "org.freedesktop.login1.power-off-multiple-sessions") { if (subject.isInGroup("wheel")) { return polkit.Result.YES; } else { return polkit.Result.AUTH_ADMIN; } } });
You have to get the Java syntax correct, and it may not be obvious that you have an extra or missing parenthesis or curly bracket. If you wonder why your new rule isn’t doing what you expect, try the following and look for syntax error messages:
# grep polkit /var/log/messages | tail
Not everyone is ready to start experimenting with polkit. One of the exercises in Learning Tree’s Linux server administration course has a bonus section that leads more advanced attendees through this. Come to the course and experiment with this on a non-critical system.