Group based Permissions using polkit

Date: , Updated: — Topic: — Lang: — by Slatian

Access things like NetworkManager based on UNIX groups.

What and why?

I'm the kind of creature that really likes NetworkManager and avoids systemd when possible.

The problem arises that, when one isn't using (e)logind things like being able to configure networks don't work anymore. Reason being that polkit is configured to only allow these actions for logged in sessions, but nothing told it that a session is active.

Luckily those rules are not set in stone and one can reconfigure polkit however they like.

Note: NetworkManager only supports polkit for configuring who can do what. This is okay because it is supposed to manage networks, not permissions.

For my purposes I want to be able to send basic commands to NetworkManager because I'm in the wheel (aka. sudo) group, independent of me being logged in.

Update on 2024-04-18: Previously I forgot to include the org.freedesktop.NetworkManager.settings.modify.system action which prevented connecting to new wireless networks.

Understanding polkit

This was the first time I messed with polkit which is very well documented in the polkit(8) manpage.

Polkit is a service with its only task being to answer one question

Is subject allowed to do action?

The subject is roughly equivalent to a user.

The action is a more or less rough description of what the subject wants to do identified by an id.

Actions are described by XML-Files in /usr/share/polkit-1/actions/. These contain localised names, icon-names and some sane default permissions for each action.

In addition to actions and the default permissions, polkit supports rules written in JavaScript. These live in files in /etc/polkit-1/rules.d/.

Note: There is also /usr/share/polkit-1/rules.d/ but that one is intended for packages. As an admin /etc is the preferred place.

NetworkManager rules

Before writing the rule I need the action ids that are used to configure NetworkManager. These can be found by searching the /usr/share/polkit-1/actions/org.freedesktop.NetworkManager.policy file for all rules that work without any extra authentication given an active session.

The results are:

Note on inactive sessions: Even if an action is allowed for inactive sessions it isn't allowed when that session state can't be determined which is the case when (e)logind is missing.

Polkit debug mode

To save you some frustration with debugging: Polkit has a debug mode which is probably turned off when launched through your service manager of choice using the --no-debug option.

To get the debug output.

  1. Stop the polkit service
  2. Run /usr/lib/polkit-1/polkitd as root in a terminal
  3. Start the service again when done with debugging

Note: Polkit will drop privileges automatically after setting itself up.

Adding a rule

According to the manual I should be able to just place a .rules file in /etc/polkit/rules.d/ and polkit will pick it up automatically. If you have polkit in debug mode you'll even get feedback whether your rules are valid JavaScript or not.

For the NetworkManager scenario my script adds a rule function that first checks if the action id is in an array of allowed actions, then checks if the subject is in the wheel group and if so returns that the action is allowed.

If the rule doesn't return a result polkit will fall back to other rules and eventually the defaults.

Translated to JavaScript the result is the following.

polkit.addRule(function(action, subject) { 
	var allowed_actions = [
		"org.freedesktop.NetworkManager.enable-disable-network",
		"org.freedesktop.NetworkManager.enable-disable-wifi",
		"org.freedesktop.NetworkManager.enable-disable-wimax",
		"org.freedesktop.NetworkManager.enable-disable-wwan",
		"org.freedesktop.NetworkManager.network-control",
		"org.freedesktop.NetworkManager.settings.modify.own",
		"org.freedesktop.NetworkManager.settings.modify.system",
		"org.freedesktop.NetworkManager.wifi.scan",
		"org.freedesktop.NetworkManager.wifi.share.open",
		"org.freedesktop.NetworkManager.wifi.share.protected"
	]
	if (allowed_actions.indexOf(action.id) >= 0) {
		if (subject.isInGroup("wheel")) {
			return polkit.Result.YES;
		}
	}
})

Other Things polkit Rules could do

By testing for an action id prefix and (non-)membership of a group you could also deny access to a service by returning polkit.Result.NO.

Polkit can also call external commands and make a decision based on success or failure of a command.

The manual has very good examples.

Bye

I hope that was a useful bit of information about a thing that mostly stays out of the way and comes with an extra bit of suprise when it doesn't work.