Skip to content

Privileged action containment: the daemon cannot force-kill a root child on timeout #142

Description

@vladimirrott

Summary

Follow-up to #140. The action-timeout fix contains non-privileged commands in a process group and signals the group on timeout. But the daemon runs as User=sysknife and elevates through sudo, and an unprivileged process cannot signal a process running as root. So for every privileged action the real work runs as a root child that the daemon's SIGTERM/SIGKILL cannot touch.

The #140 fix handles this honestly rather than pretending: when the process group cannot be confirmed gone (killpg returns EPERM, meaning members remain but are unsignalable), the executor returns ExecutorError::ActionNotStopped, the dispatcher skips the automatic rollback, and it holds the exclusion slot so no second mutating action can race the first. That is the safe response, but it is detection, not termination: a hung privileged action still runs to completion or until its own tool gives up.

What real termination needs

A privileged mechanism the daemon is allowed to invoke:

  1. systemd transient scope — run each privileged action under systemd-run --scope --collect --unit=sysknife-action-<id>, so systemd owns the cgroup and can systemctl kill it. This is the cleanest: cgroup kill catches every descendant regardless of uid, and it works for rpm-ostree too (a systemctl stop rpm-ostreed-adjacent story). Requires the daemon to have a sudoers grant for systemd-run/systemctl kill scoped to sysknife-action-* units.
  2. A sudoers-authorised kill helper — sysknife-kill <pgid> allowed via /etc/sudoers.d/, scoped so it can only signal groups the daemon spawned. More surface than option 1.

rpm-ostree needs its own care regardless: it is a thin D-Bus client to rpm-ostreed, so the transaction lives in a different cgroup and the correct cancel is rpm-ostree cancel, not a signal.

Acceptance

  • A privileged action that ignores signals is actually stopped on timeout, verified by a live-VM test (the unit tests can only cover the same-uid path).
  • ActionNotStopped becomes the genuine last resort (mechanism failed), not the expected path for every sudo action.

Provenance

Surfaced by the review of #141 (two independent reviewers) and a defensive red-team pass. The detection-and-veto half shipped in #141; this issue is the termination half.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions