Ubuntu · Accounts & authentication

Configure sudo to use a pseudo-terminal, a log file and a short credential cache

When it fails: sudo defaults allow terminal hijacking, keep no local log or cache credentials too long. Commands run with sudo can escape to the user's terminal, leave traces only in syslog, and a forgotten session stays privileged.

CB-UBU-AUTH-005mediumactiveautomatedautomated fixprofile core

As released in v0.1.1 (beta). Current state.

Description

Three sudo defaults, set with Defaults lines in /etc/sudoers or /etc/sudoers.d/:

  • use_pty: run each command in a new pseudo-terminal, so it cannot inject input into the user's terminal after sudo exits;
  • logfile: write every sudo command to a dedicated file, in addition to syslog;
  • timestamp_timeout: how many minutes a successful authentication is cached before sudo asks for the password again.

Ubuntu sets use_pty in /etc/sudoers since 22.04 (not on 20.04) and caches credentials for 15 minutes. With the original sudo, the effective values are read with sudo -V as root, which merges every sudoers file.

On Ubuntu 26.04 the sudo command is sudo-rs. It always runs commands in a pseudo-terminal unless !use_pty is set, sends every command to syslog (the journal), and has no logfile option: visudo rejects that line as an unknown setting. Its sudo -V only prints the version, so the checks read the sudoers files instead, and the logfile check is not applicable.

Rationale

Without use_pty, a malicious program run through sudo can keep running in the background and push keystrokes into the user's shell. A dedicated log file keeps a readable record of privileged commands even if syslog is lost or flooded. A short cache limits how long an unattended terminal can run privileged commands without a password.

Impact

Some interactive programs behave differently in a pseudo-terminal (rare). Users type their password again after {{ timestamp_timeout }} minutes of not using sudo.

Applies when

Detected: when: A sudo command is installed (sudo or sudo-rs) A scanner reports the control not applicable when one is false.

Setting

Location/etc/sudoers.d/60-cirsec
Default20.04: no use_pty; 22.04 and later: use_pty; no log file, 15 minutes
Recommendeduse_pty, logfile={{ log_file }} (not with sudo-rs), timestamp_timeout at most {{ timestamp_timeout }}

Parameters

ParameterCoreServerServer hardenedDesktopDesktop hardenedBounds
log_filestring. Path of the sudo log file. Not used by sudo-rs, which logs to syslog only./var/log/sudo.log/var/log/sudo.log/var/log/sudo.log/var/log/sudo.log/var/log/sudo.log
timestamp_timeoutinteger, minutes. Maximum number of minutes sudo caches credentials.151551550 to 15

The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.

Audit

With the original sudo, run sudo -V as root and check that:

  1. it prints Always run commands in a pseudo-tty;
  2. Path to log file: is set;
  3. Authentication timestamp timeout: is at most {{ timestamp_timeout }} minutes.

With sudo-rs (sudo --version starts with sudo-rs), read /etc/sudoers and /etc/sudoers.d/*: there is no Defaults !use_pty, and the last timestamp_timeout is at most {{ timestamp_timeout }} (15 when absent).

Automated: every check must pass. Scanners report each check by its id.

use-pty

sudo runs commands in a pseudo-terminal.

script: |
  if sudo --version 2>/dev/null | head -n 1 | grep -q '^sudo-rs'; then
    l=$(cat /etc/sudoers /etc/sudoers.d/* 2>/dev/null | grep -E '^[[:space:]]*Defaults.*![[:space:]]*use_pty')
    [ -z "$l" ] || { echo "sudo-rs: $l"; exit 1; }
    echo "sudo-rs: use_pty (default, not disabled)"
    exit 0
  fi
  sudo -V 2>/dev/null | grep -q 'Always run commands in a pseudo-tty' || { echo "use_pty not set"; exit 1; }
  echo "use_pty set"

logfile

sudo writes to a log file (not applicable to sudo-rs, which only logs to syslog).

script: |
  if sudo --version 2>/dev/null | head -n 1 | grep -q '^sudo-rs'; then
    echo "sudo-rs has no logfile option; commands are logged to syslog"
    exit 2
  fi
  f=$(sudo -V 2>/dev/null | sed -n 's/^Path to log file: //p')
  echo "logfile=${f:-none}"
  [ -n "$f" ]

timeout

sudo credential cache lifetime.

script: |
  if sudo --version 2>/dev/null | head -n 1 | grep -q '^sudo-rs'; then
    t=$(cat /etc/sudoers /etc/sudoers.d/* 2>/dev/null | grep -E '^[[:space:]]*Defaults' \
      | sed -n 's/.*timestamp_timeout[[:space:]]*=[[:space:]]*\([0-9-]*\).*/\1/p' | tail -n 1)
    t=${t:-15}
  else
    t=$(sudo -V 2>/dev/null | sed -n 's/^Authentication timestamp timeout: \([0-9-]*\).*/\1/p')
  fi
  echo "timestamp_timeout=${t:-unknown} minutes"
  [ -n "$t" ] || exit 3
  [ "$t" -ge 0 ] && [ "$t" -le "$CB_TIMESTAMP_TIMEOUT" ]

Remediation

Create /etc/sudoers.d/60-cirsec with visudo -f /etc/sudoers.d/60-cirsec:

Defaults use_pty
Defaults logfile="{{ log_file }}"
Defaults timestamp_timeout={{ timestamp_timeout }}

Leave out the logfile line with sudo-rs (Ubuntu 26.04): sudo-rs rejects it. Remove any timestamp_timeout with a higher value from other sudoers files.

Restartnone
Disruptionlow

Rollback

Delete /etc/sudoers.d/60-cirsec.

dropin

f=/etc/sudoers.d/60-cirsec
t=$(mktemp)
{
  echo '# Written by CIRSEC Baselines CB-UBU-AUTH-005'
  echo 'Defaults use_pty'
  sudo --version 2>/dev/null | head -n 1 | grep -q '^sudo-rs' || printf 'Defaults logfile="%s"\n' "$CB_LOG_FILE"
  echo "Defaults timestamp_timeout=$CB_TIMESTAMP_TIMEOUT"
} >"$t"
if ! visudo -cqf "$t" >/dev/null; then rm -f "$t"; echo "invalid sudoers drop-in" >&2; exit 1; fi
cb_backup "$f"
install -m 0440 -o root -g root "$t" "$f"
rm -f "$t"

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

FrameworkIDRelationNote
ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0R39partialR39 lists several sudo directives; this control covers use_pty, logging and the cache.
NIST SP 800-53 Rev. 55.1.1AU-2related

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. Fails on a fresh install (sudo 1.8.31, no use_pty, no log file); drop-in validated by visudo fixes it; sudo of the default user still works; 5 minutes in server-hardened; kept after reboot.
Verified2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. Fails on a fresh install (no log file); drop-in validated by visudo fixes it; sudo of the default user still works; 5 minutes in server-hardened; kept after reboot.
Verified2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. Fails on a fresh install (no log file); drop-in validated by visudo fixes it; sudo of the default user still works; 5 minutes in server-hardened; kept after reboot.
Verified2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. sudo-rs 0.2.13 - use_pty by default passes, logfile not applicable, timeout read from sudoers; drop-in without logfile accepted by visudo, sudo still works, 5 minutes in server-hardened; kept after reboot.
Introduced inv0.1.0
Created, updated2026-10-19, 2026-10-19
Translationsfr