Polkit not logging any debugging information on Fedora?

published Mar 02, 2024

It's a factory configuration issue, easily solvable.

Polkit not logging any debugging information on Fedora?

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.