Skip to content

Ansible Privilege Escalation and Variable Precedence

When orchestrating bare-metal nodes, managing execution environments and user privileges is critical. This document details the mechanics of Privilege Escalation within Ansible and how Variable Precedence (Ansible) can lead to subtle bugs.

Variable Precedence and the remote_user Trap

Ansible allows you to define variables in multiple places: playbooks, inventory files, command-line arguments, etc. However, understanding the order of precedence is vital to avoid silent failures.

The Apt Lock Bug

A common scenario during initial node provisioning:

  1. You define the standard SSH user in the inventory file:
all:
  vars:
    ansible_user: leva
  1. You attempt to force a specific playbook to run entirely as root:
- name: Bootstrap Node
  hosts: all
  remote_user: root

Symptom: Tasks that require root access (like apt install) fail with Failed to lock apt for exclusive operation.

Cause: Inventory variables (ansible_user) have a higher precedence than playbook variables (remote_user). Ansible silently ignores the remote_user: root directive and logs in as leva. Because leva does not yet have sudo privileges on a raw installation, the apt module executes as a standard user and fails.

Solution: Do not mix ansible_user and remote_user to attempt privilege escalation. If you need root execution, use Ansible's native become system.

The become System: su vs sudo

Ansible relies heavily on sudo for privilege escalation. However, fresh minimal OS installations (like Debian netinst) do not include the sudo package by default.

To bootstrap sudo itself, you must use an alternative become_method, such as su.

Escalating with su

- name: Install sudo
  apt:
    name: sudo
    state: present
  become: true
  become_method: su
  become_user: root

The Missing $PATH Gotcha

When Ansible escalates via become_method: su, it executes a non-login shell. Unlike su -, a non-login shell does not execute the root user's profile scripts (/etc/profile, .bashrc).

Consequently, system paths like /usr/sbin/ and /sbin/ are omitted from the $PATH environment variable.

Symptom:

No such file or directory: b'visudo'

Solution: When executing commands via raw su that rely on system binaries, you must provide the absolute path.

- name: Authorize passwordless sudo
  lineinfile:
    path: /etc/sudoers.d/99-homelab
    line: "leva ALL=(ALL) NOPASSWD:ALL"
    create: yes
    validate: /usr/sbin/visudo -cf %s