Skip to content

Sentinel v0.12.0

Choose a tag to compare

@github-actions github-actions released this 27 Jun 21:18

Sentinel 0.12.0

A security-architecture release: the "remember" decision and its grant
store move out of the root PAM module into a sandboxed, unprivileged
broker; the remember window is reworked so every grant is bound to the
exact command; and the root unsafe surface is locked down.

If you remember one thing: a remembered grant now covers only the exact
command you approved — sudo pacman -Syu can never wave through
sudo pacman -U /tmp/evil, and pkexec id never covers pkexec rm.

Highlights

  • Per-command remember, on both the polkit and sudo/su paths. Grants
    are keyed by the whole elevated command, not the program name.
  • Privilege-separation broker. The root PAM module no longer holds any
    grant state; it relays to an unprivileged, in-memory, root-only daemon.
  • sudo's own credential cache is disabled by the installer, so
    Sentinel's per-command window is the single source of truth for sudo.
  • pam-sentinel is now #![deny(unsafe_code)] apart from two
    documented, narrowly-scoped sites.

Remember window

  • Shown by default on the polkit/GUI dialog ([general].remember_seconds
    defaults to 300). The box defaults unchecked — nothing is auto-allowed
    unless you tick it on that prompt. (#22)
  • Grants bind to the full command. This fixes a real bug: the polkit
    path previously keyed grants on (action, exe), command-blind, so one
    ticked pkexec could silently auto-allow any later pkexec command. The
    key is now the whole command on both paths, and pkexec is remembered
    per command like everything else (no blanket carve-out).
  • Per-service window. [services.<name>].remember_seconds is the knob;
    the compiled default is 300 for polkit-1 and 0 for terminal
    services, but the shipped config opts sudo/sudo-i/su into 300.
    Set a service to 0 to require confirmation every time. Unknown
    [services.*] keys are now a hard parse error.
  • Arbitrary-code gateways never qualify (both paths): shells,
    interpreters, and shell-escapers (editors, pagers, find, …) as the
    elevated command, plus bare-elevation root shells (sudo -s/-i/-v,
    su) on the terminal path, always re-prompt.

Privilege-separation broker

Following the pam_sss / OpenSSH-monitor model, the remember decision and
grant store now live in sentinel-broker — a long-lived, unprivileged
daemon (systemd DynamicUser=) that serves only root peers over a Unix
socket (SO_PEERCRED uid-0 gate) and holds grants in memory. There is
no on-disk artifact to forge or roll back, and grants evaporate when the
daemon stops. pam_sentinel relays over a typed, length-bounded postcard
protocol (sentinel-broker-proto), and is fail-closed: an unreachable
broker means "show the dialog", never "let in". The old root
/run/sentinel/ts timestamp store is removed. Both broker crates are
#![forbid(unsafe_code)]; the unit is hardened (seccomp @system-service,
no capabilities, ProtectSystem=strict, AF_UNIX-only).

Hardening

  • Root unsafe surface locked down. pam-sentinel is
    #![deny(unsafe_code)]; hand-rolled extern "C" syscall shims are
    replaced with safe nix wrappers, leaving only fork(2) and post-fork
    set_var behind SAFETY-documented #[allow(unsafe_code)].
  • Fuzz harnesses (cargo-fuzz) for the untrusted inputs that cross
    into the auth path — Verdict::from_str, strip_elevation_prefix,
    format_message, and the broker wire protocol — smoke-fuzzed in CI.

Installer

  • Ships and enables the broker (KDE installer), tearing it down on
    uninstall.
  • Disables sudo's own credential cache. sudo's timestamp_timeout
    (~5 min) let a back-to-back sudo skip the PAM stack entirely, bypassing
    Sentinel. The installer drops a Defaults timestamp_timeout=0 snippet so
    every sudo runs PAM and Sentinel's per-command window is the only
    cache (honored by classic sudo and sudo-rs). Detection covers
    /etc/sudoers, openSUSE's /usr/etc/sudoers, and sudo-rs's
    /etc/sudoers-rs, writing only into a drop-in dir that config actually
    includes (visudo-validated, recorded for uninstall, never a vendor
    dir). Skipped with a warning where there's no safe drop-in (e.g. NixOS);
    --no-sudo opts out.

Upgrading

  • Reinstall to pick up the broker and the updated config. The KDE
    installer's reinstall is in-place and reversible.
  • Terminal sudo/su now remember per command out of the box (shipped
    config). Set [services.<name>].remember_seconds = 0 to keep the strict
    "confirm every time" behavior for a service.
  • sudo's built-in 5-minute grace is turned off so Sentinel is the
    only sudo cache. Remove /etc/sudoers.d/sentinel-timestamp (or
    uninstall) to restore it.
  • polkit's own auth_admin_keep / auth_self_keep caching is unchanged —
    Sentinel does not override it. pkexec does not use keep, so it is
    re-confirmed every time.