How to Configure Polkit (PolicyKit) Permissions on Fedora

Configure Polkit permissions on Fedora by creating a custom XML policy file in /usr/share/polkit-1/actions/ and restarting the polkit service.

When a custom script hits a privilege wall

You wrote a helper script to manage a custom network bridge, restart a niche daemon, or mount a specialized filesystem. You run it from your desktop session and get a generic denial. The terminal prints polkit: Error: Action org.example.custom.action is not registered or the GUI shows a grayed-out button. You know the command works with sudo, but you want it to integrate cleanly with the desktop session and prompt for a password only when necessary. Polkit is the bridge between your user session and system-level operations. Configuring it correctly stops the denial and keeps your system secure.

What Polkit actually does

Polkit, historically called PolicyKit, is a D-Bus authorization broker. It does not execute commands. It answers a single question: is this user allowed to perform this specific action right now. Applications send a D-Bus message requesting an action ID. Polkit checks the policy, evaluates the user's context, and returns a verdict. The application then handles the prompt or the denial.

Think of it like a building security desk. The master key (sudo) opens every door and logs every entry. Polkit is the access card system. You get a card that only opens the server room during business hours. The card tracks who you are, where you are, and what you are trying to open. This separation keeps desktop applications from demanding root access for everything while still allowing precise administrative control.

Fedora ships with a baseline set of policies in /usr/share/polkit-1/actions/. These cover standard operations like power management, network configuration, and disk mounting. When you need something outside that baseline, you write a custom action file. The system reads it automatically. No service restart is strictly required for new action definitions, but reloading the daemon ensures a clean state and clears any cached verdicts.

Define the action before you test. Polkit will silently ignore malformed XML and fall back to denial.

How to write a custom policy

Create a new XML file in /etc/polkit-1/actions/. Using /etc/ keeps your changes separate from package-managed files in /usr/share/. Package updates will never overwrite your custom policy. The filename must end in .policy.

Here is how to structure a minimal policy that requires an administrator password for an active user session.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!-- Root element wraps all action definitions -->
<policyconfig>
  <!-- Unique identifier for your custom operation -->
  <action id="org.example.custom.network.action">
    <!-- Icon shown in desktop authentication prompts -->
    <icon_name>network-workgroup</icon_name>
    <!-- Short title for the prompt header -->
    <short_context>Configure custom network bridge</short_context>
    <!-- Detailed explanation shown when the user clicks "Why?" -->
    <description>Allow users to manage the custom network bridge interface</description>
    <!-- The actual message displayed in the password dialog -->
    <message>Authentication is required to modify the network bridge configuration</message>
    <!-- Default authorization rules for different user states -->
    <defaults>
      <!-- Users not logged in anywhere -->
      <allow_any>no</allow_any>
      <!-- Users logged in remotely or via SSH -->
      <allow_inactive>no</allow_inactive>
      <!-- Users at an active local console or desktop session -->
      <allow_active>auth_admin</allow_active>
    </defaults>
    <!-- Optional: tells polkit which executable backs this action -->
    <annotate key="org.freedesktop.policykit.exec.path">/usr/local/bin/manage-bridge.sh</annotate>
  </action>
</policyconfig>

The id attribute must be unique across the entire system. Reverse domain notation prevents collisions with upstream packages. The defaults block controls access levels. auth_admin requires a password from a user in the wheel group. auth_self accepts the current user's password. yes grants immediate access without a prompt. no denies access permanently.

Place the file in /etc/polkit-1/actions/. Polkit scans this directory on startup and watches for changes. You can force a reload by restarting the daemon, but the D-Bus bus usually picks up new action definitions within seconds.

# Copy your draft to the correct directory with root privileges
sudo cp custom-bridge.policy /etc/polkit-1/actions/
# Restart polkit to clear any cached policy state
sudo systemctl restart polkit

Keep the XML strictly valid. A missing closing tag breaks the entire policy file and drops all custom actions.

Verify the policy loaded correctly

Polkit provides a command-line tool to inspect loaded actions. Run pkaction to list available policies and filter for your custom ID.

# Query polkit for your specific action and show verbose details
pkaction --verbose --action-id org.example.custom.network.action

The output will show the action ID, the vendor, the description, and the default authorization values. If the command returns No such action, check the XML syntax and verify the file is in /etc/polkit-1/actions/. Polkit does not log parsing errors to the journal by default. It simply skips malformed files.

Test the actual authorization flow by running the associated command or D-Bus method. If you are using a GUI application, trigger the action and watch the authentication dialog. If you are using a CLI tool that integrates with Polkit, check the journal for the authorization result.

# Watch polkit logs in real time while you trigger the action
journalctl -xeu polkit --follow

Look for lines containing polkitd and your action ID. A successful authorization prints Action org.example.custom.network.action was authorized. A denial prints Action ... was not authorized. The log output tells you exactly which rule triggered the verdict.

Run pkaction before you debug the application. Half the time the problem is a typo in the action ID, not the application itself.

Common pitfalls and what the error looks like

Polkit denials are notoriously quiet. The application usually just says permission denied or shows a disabled button. You need to look at the right logs to find the real cause.

The pkaction command will refuse to show your policy and print No such action if the XML fails validation. The DTD reference in the header must match exactly. A missing namespace or a malformed tag causes the parser to discard the file. Validate your XML before deploying it.

# Example output when polkit cannot find your action
$ pkaction --action-id org.example.custom.network.action
No such action

SELinux frequently blocks custom Polkit executables. If your policy points to a script in /usr/local/bin/ or /opt/, SELinux will deny the D-Bus activation or the file execution. Check the audit logs before disabling SELinux.

# Check for SELinux denials related to polkit or your custom script
journalctl -t setroubleshoot | grep -i polkit

The one-line summary will tell you if polkitd was denied execute or search permissions on your path. Generate a local policy module or move the script to a standard system path with the correct context.

Another common issue is confusing auth_admin with auth_self. auth_admin requires a user in the wheel group. If you run the command as a standard user without wheel membership, Polkit will prompt for a password but then reject it. The application shows Authentication failed even if you typed the correct password. Switch to auth_self if you want any logged-in user to perform the action with their own password.

Remote sessions behave differently than local ones. The allow_inactive rule applies to SSH connections, VNC sessions, and cron jobs. If you set allow_inactive to no, remote users will be blocked even if they are in the wheel group. This is intentional. Remote privilege escalation should require explicit configuration or a separate policy.

Check journalctl -t setroubleshoot before you blame Polkit. SELinux denials masquerade as policy failures.

When to use Polkit versus alternatives

Fedora provides multiple ways to handle privilege escalation. Choosing the right tool depends on how the application runs and who needs access.

Use Polkit when you are writing a desktop application or a D-Bus service that needs fine-grained authorization. Use sudo when you need to run a one-off command from the terminal with full root privileges. Use systemd-run --user when you want to run a temporary elevated task without modifying system-wide policies. Use pklocalauthority overrides when you need to change the default authorization level for an existing system policy without editing XML files. Stay on the upstream Polkit defaults if you only need standard power management or network configuration.

The pklocalauthority tool lets you modify existing policies from the command line. It writes overrides to /etc/polkit-1/localauthority/. This is faster than writing XML when you just want to change auth_admin to yes for a specific action.

# Override an existing policy to allow active users without a password
sudo pklocalauthority --enable --any-user --active org.freedesktop.network-manager.enable

This command creates a machine override that persists across reboots. It takes precedence over the default policy file. You can revert it by running the same command with --disable or --reset. The local authority directory also supports JavaScript-based rules for complex conditional logic, but XML policies cover ninety percent of use cases.

Trust the package manager for system policies. Manual file edits drift, local authority overrides stay.

Where to go next