Skip to main content
RunBook Academy

AnsibleXIX · Privilege EscalationPrivilege escalation

What become actually does

Intermediate⏱ ~18 minansible-playbookansible-docansible-config

What you'll learn

  • Separate the connection user from the become user and say which is which
  • Name the five become keywords and the connection variables that mirror them
  • State the defaults for become_method, become_user and the sudo become_flags
  • Explain why escalation cannot be limited to specific commands from the Ansible side
  • Read the command Ansible constructs on the target and recognise its parts

Prerequisites

Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11

Not yet marked complete on this device.

Ansible connects to a managed node as one identity and may execute as another. Those are two separate steps, in that order, and almost every confusing become failure comes from treating them as one.

The connection happens first. SSH authenticates ansible_user against the host. If that fails, the host is unreachable and nothing has run.

Escalation happens second, on the target. Ansible wraps the command it wants to run in an escalation command — by default sudo — and the target evaluates whether that is allowed. If that fails, the host is failed, not unreachable, because the connection worked fine.

The documentation puts the distinction in the become_user description itself: it is “the user you become, NOT the user you login as”.

The five keywords

become is controlled by play, block, role and task keywords, each mirrored by a connection variable you can set per host or group.

KeywordVariableDefaultWhat it does
becomeansible_becomefalseTurn escalation on
become_methodansible_become_methodsudoWhich escalation plugin
become_useransible_become_userrootThe identity to become
become_flagsansible_become_flagsplugin-specificFlags for the escalation binary
become_exeansible_become_exeplugin-specificThe escalation binary itself

Those defaults are not folklore. They are readable from the running installation:

Read-only / Safethe become defaults, from the installation itself
$ ansible-config list | grep -A4 'DEFAULT_BECOME_METHOD:\\|DEFAULT_BECOME_USER:\\|^DEFAULT_BECOME:'
DEFAULT_BECOME:
default: false
description: Toggles the use of privilege escalation, allowing you to 'become' another
  user after login.
DEFAULT_BECOME_METHOD:
default: sudo
description: Privilege escalation method to use when `become` is enabled.
DEFAULT_BECOME_USER:
default: root
description: The user your login/remote user 'becomes' when using privilege escalation,
  most systems will use 'root' when no user is specified.

Scope: where you put become decides how much runs as root

The keyword is inheritable, and where it sits is a design decision, not a formatting one.

Configuration changethree scopes, three blast radii
# Widest: every task in the play runs as root, including gather_facts.
- name: Configure web servers
hosts: webservers
become: true
tasks: []

# Narrower: a block of related privileged work.
- name: Configure web servers
hosts: webservers
tasks:
  - name: Install and configure nginx
    become: true
    block:
      - ansible.builtin.package:
          name: nginx
          state: present
      - ansible.builtin.template:
          src: nginx.conf.j2
          dest: /etc/nginx/nginx.conf
          mode: '0644'

# Narrowest, and the one a reviewer can audit: per task, with the
# identity named.
- name: Configure web servers
hosts: webservers
tasks:
  - name: Read the current config for comparison
    ansible.builtin.stat:
      path: /etc/nginx/nginx.conf

  - name: Install nginx
    ansible.builtin.package:
      name: nginx
      state: present
    become: true
    become_user: root

  - name: Warm the application cache
    ansible.builtin.command: /opt/app/bin/warm-cache
    become: true
    become_user: appsvc
    changed_when: false

The middle form is the practical compromise for a role: a block with become: true around the tasks that genuinely need it, and the read-only tasks outside it. The point is not asceticism. It is that a reviewer can see which operations require privilege, and a role that declares become at the play level has told them nothing.

become_flags and become_exe

These are the per-plugin escape hatches, and the sudo plugin’s defaults are worth knowing because they explain a common error message.

Read-only / Safethe sudo become plugin defaults
$ ansible-doc -t become sudo | grep -A3 'become_flags\\|become_user  User'
   become_flags  Options to pass to sudo
      default: -H -S -n

 become_user  User you 'become' to execute the task
      default: root

Those three flags each do something specific:

FlagMeaningWhy it matters
-HSet HOME to the target user’s homeCommands that write dotfiles land in the right place
-SRead the password from standard inputLets Ansible supply a password without a tty
-nNon-interactive: fail rather than promptThis is why a missing password is an instant failure, not a hang

-n is the one that shapes the error you will see. With it, sudo does not wait for a password it cannot get; it exits immediately with sudo: a password is required. That is a feature — a fleet-wide run that hangs on forty invisible password prompts is much worse — and it is why the fix is to supply the password rather than to increase a timeout.

The constraint that shapes every sudoers rule

The documentation lists it among the risks, and it is the single most consequential fact in this part:

Become must be general; it cannot be limited to specific commands.

From Ansible’s side there is no way to say “escalate only for the package task”. Every escalated task on that host runs through the same mechanism, wrapping a Python interpreter invocation against a randomly-named temporary file. You cannot write a sudoers rule listing the module names, because the target never sees a module name.

This does not mean least privilege is unavailable — it means it is expressed on the managed node, in the sudoers rule, in terms the target can evaluate: which user, from which host, as which target user, running which binary. Lesson 4 of this part designs that grant. What matters here is understanding why it has to be designed there rather than in the playbook.

A second documented restriction belongs with it:

You cannot chain become methods.

One method per host. You cannot sudo to an intermediate account and then su to the final one within a single task. If your environment requires that, the intermediate hop belongs in the connection — a bastion, covered in Part XX — not in become.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A run reports failed=1 with a sudo error on twelve hosts and unreachable=0 everywhere. What does that combination tell you?

  2. Q2. A sudoers rule cannot restrict Ansible to specific modules, because what sudo evaluates is a shell invoking a Python interpreter against a temporary file, not a module name.

  3. Q3. Why does a missing become password produce an immediate failure rather than a hung run?

  4. Q4. Which are true of putting become: true at the play level rather than per task? Select all that apply.

Passing score: 75%. Answers are checked in this browser.