AnsibleXIX · Privilege EscalationPrivilege escalation
What become actually does
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
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.
| Keyword | Variable | Default | What it does |
|---|---|---|---|
become | ansible_become | false | Turn escalation on |
become_method | ansible_become_method | sudo | Which escalation plugin |
become_user | ansible_become_user | root | The identity to become |
become_flags | ansible_become_flags | plugin-specific | Flags for the escalation binary |
become_exe | ansible_become_exe | plugin-specific | The escalation binary itself |
Those defaults are not folklore. They are readable from the running installation:
$ 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.
# 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: falseThe 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.
$ 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: rootThose three flags each do something specific:
| Flag | Meaning | Why it matters |
|---|---|---|
-H | Set HOME to the target user’s home | Commands that write dotfiles land in the right place |
-S | Read the password from standard input | Lets Ansible supply a password without a tty |
-n | Non-interactive: fail rather than prompt | This 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
Q1. A run reports failed=1 with a sudo error on twelve hosts and unreachable=0 everywhere. What does that combination tell you?
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.
Q3. Why does a missing become password produce an immediate failure rather than a hung run?
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.