Polkit not logging any debugging information on Fedora?
It's a factory configuration issue, easily solvable.
Earlier today, I was attempting to set up the a polkit (formerly PolicyKit) policy on a system of mine, which I use as a media center and therefore I must be able to control remotely. Part of the work I was doing used polkit.log()
to dump some policy information to the system log; polkit is supposed to print to the log whenever polkit.log()
is used, but this wasn't working.
Here's the policy in question:
/* Allow Home Assistant to shut down, lock, suspend or log anyone off the system without authentication. */
polkit.addRule(function(action, subject) {
if (subject.user == "assistantsh") {
polkit.log("Subject " + subject + " attempting action " + action);
if (
action.id == "org.freedesktop.login1.power-off"
||
action.id == "org.freedesktop.login1.power-off-multiple-sessions"
) {
polkit.log("Forced power off permitted for subject" + subject);
return polkit.Result.YES;
}
if (
action.id == "org.freedesktop.login1.suspend"
||
action.id == "org.freedesktop.login1.suspend-multiple-sessions"
) {
polkit.log("Suspend permitted for subject" + subject);
return polkit.Result.YES;
}
if (
action.id == "org.freedesktop.login1.manage"
||
action.id == "org.freedesktop.login1.manage-multiple-sessions"
) {
polkit.log("Manage sessions permitted for subject" + subject);
return polkit.Result.YES;
}
if (
action.id == "org.freedesktop.login1.lock-sessions"
) {
polkit.log("Lock sessions sessions permitted for subject" + subject);
return polkit.Result.YES;
}
}
});
I was having trouble getting it to work, and the polkit.log()
stanzas were not producing anything in the journalctl -f
journal (as they should, because it's documented behavior).
Curious, I looked into /usr/lib/systemd/system/polkit.service
and I found the following:
[Service]
...
ExecStart=/usr/lib/polkit-1/polkitd --no-debug
...
Whaaat! By default, polkit.log()
is suppressed on Fedora, because of that flag!
The fix was easy. I removed the --no-debug
from the configuration file, and issued systemctl daemon-reload ; service polkit restart
on the console.
Bam! My polkit.log()
messages began showing up on the console.
Sure, the removed flag will reappear on that file next time polkit is updated, but I don't care about that — I only needed to see the messages today.
For anyone who wants to know, here is the shell program that specific system uses to let a remote system manage various aspects of it via SSH using polkit policies instead of sudoers
root access. The program below is set as a shell of the local user designated for remote management.
#!/usr/bin/python3 import os, shlex, subprocess, sys testing = False os.close(1) os.close(2) logger = subprocess.Popen(["logger", "-t", "assistantsh"], stdin=subprocess.PIPE) os.dup(logger.stdin.fileno()) logger.stdin.close() def report(msg, *args): if args: msg = msg %args print(msg) if not sys.argv[1:] or sys.argv[1] != "-c": report("rejected command line %s", sys.argv[1:]) sys.exit(8) args = sys.argv[2:] if args == ["poweroff"]: report("powering off") if testing: sys.exit(0) {% if is_vm %} os.execv( "/usr/bin/qrexec-client-vm", [ "/usr/bin/qrexec-client-vm", "dom0", "admin.dom0.Poweroff", ] ) {% else %} ret = subprocess.run( [ "/usr/bin/systemctl", "poweroff", "-i", "--no-ask-password", ], capture_output=True, text=True, check=False, ) if ret.returncode: report(f"failed to power off ({ret.returncode}): {ret.stderr}") else: report("successfully powered off") sys.exit(ret.returncode) {% endif %} elif args == ["loginctl lock-sessions"]: report("evicting and locking sessions") {% if is_vm %} if testing: sys.exit(0) os.execv( "/usr/bin/qrexec-client-vm", [ "/usr/bin/qrexec-client-vm", "dom0", "admin.dom0.Lock", ] ) {% else %} ret = subprocess.run( [ "/usr/bin/loginctl", "terminate-seat", "seat0", "--no-ask-password", ], capture_output=True, text=True, check=False, ) if ret.returncode: report(f"failed to lock sessions ({ret.returncode}): {ret.stderr}") sys.exit(ret.returncode) else: report("successfully evicted sessions") ret = subprocess.run( [ "/usr/bin/loginctl", "lock-sessions", "--no-ask-password", ], capture_output=True, text=True, check=False, ) if ret.returncode: report(f"failed to lock sessions ({ret.returncode}): {ret.stderr}") else: report("successfully locked sessions") sys.exit(ret.returncode) {% endif %} elif args == ["systemctl suspend"]: report("suspending system") {% if is_vm %} if testing: sys.exit(0) os.execv( "/usr/bin/qrexec-client-vm", [ "/usr/bin/qrexec-client-vm", "dom0", "admin.dom0.Suspend", ] ) {% else %} ret = subprocess.run( [ "/usr/bin/systemctl", "suspend", "--no-ask-password", "-i", ], capture_output=True, text=True, check=False, ) if ret.returncode: report(f"failed to suspend system ({ret.returncode}): {ret.stderr}") else: report("successfully suspended system") sys.exit(ret.returncode) {% endif %} report("rejected command %s" % args) sys.exit(16)
This shell program is invoked (via passwordless SSH, using an SSH key for authentication) by Home Assistant on the remote system. Here's a relevant snippet of the Home Assistant configuration that will call upon this program on the machine to be managed (in this sample it's called roxanne
):
# Provide the necessary commands to run actions on the machine.
shell_command:
lock_roxanne: /usr/bin/ssh -o "BatchMode yes" -o "ConnectTimeout 30" assistantsh@roxanne loginctl lock-sessions
suspend_roxanne: /usr/bin/ssh -o "BatchMode yes" -o "ConnectTimeout 30" assistantsh@roxanne systemctl suspend
turn_off_roxanne: /usr/bin/ssh -o "BatchMode yes" -o "ConnectTimeout 30" assistantsh@roxanne poweroff
# Quality of life enhancement: buttons to suspend and lock / log out.
template:
- button:
- name: Suspend roxanne
press:
service: shell_command.suspend_roxanne
unique_id: suspend_roxanne
- name: Lock roxanne
press:
service: shell_command.lock_roxanne
unique_id: lock_roxanne
# Standard Wake-on-LAN switch with a command to turn off the machine.
switch:
- platform: wake_on_lan
mac: th-em-ac-ad-dr-es
broadcast_address: th.eip.add.res
name: roxanne
host: roxanne
turn_off: {
"service": "shell_command.turn_off_roxanne"
}
And that is all.