Abstract
| Goal |
Control and optimize the execution of tasks by Ansible Playbooks. |
| Objectives |
|
| Sections |
|
| Lab |
|
Ansible Playbooks can achieve privilege escalation at many levels.
Ansible uses directives or connection variables for the privilege escalation level you intend to control.
For plays, roles, blocks, and tasks you need to use the privilege escalation directives.
The directives are become, become_user, become_method, and become_flags.
If you have the become Boolean set to true (or yes) in the privilege_escalation section of your Ansible configuration file, then all plays in your playbook use privilege escalation by default.
Those plays use the current value of become_method to change to the user specified by the become_user directive when running on the managed hosts.
You can also override the configuration file and specify privilege escalation settings when you run the playbook by using command-line options. The following table compares configuration directives and command-line options:
Table 6.1. Privilege Escalation Directives and Options
| Configuration directive | Command-line option |
|---|---|
become
|
--become or -b
|
become_method
|
--become-method=
|
become_user
|
--become-user=
|
become_password
|
--ask-become-pass or -K
|
If the Ansible configuration file specifies become: false, but the command line includes the -b option, then Ansible ignores the configuration file and uses privilege escalation by default.
You can get additional information about the precedence rules in Controlling How Ansible Behaves: Precedence Rules at https://docs.ansible.com/ansible/6/reference_appendices/general_precedence.html#general-precedence-rules.
When you write a playbook, you might need privilege escalation for some plays but not for all plays. You can specify which plays should use privilege escalation. If the play does not specify to use privilege escalation, the default setting from the configuration file or the command line is used.
The following playbook contains three plays.
The first play uses become: true to use privilege escalation no matter what the configuration file or command-line options specify.
The second play uses become: false to not use privilege escalation, even if the configuration file or command-line options are specified.
The third play does not have a become directive and uses privilege escalation based on the default settings from the Ansible configuration file or the command line.
The ansible_user_id variable displays the name of the user on the managed host running the current play.
--- - name: Become the user "manager" hosts: webserversbecome: truetasks: - name: Show the user used by this play ansible.builtin.debug: var: ansible_user_id - name: Do not use privilege escalation hosts: webserversbecome: falsetasks: - name: Show the user used by this play ansible.builtin.debug: var: ansible_user_id - name: Use privilege escalation based on defaults hosts: webservers tasks: - name: Show the user used by this play ansible.builtin.debug: var: ansible_user_id
To ensure that privilege escalation is used correctly by your play, specify the setting explicitly in the playbook. Specifying the escalation method or privileged user in configuration settings or inventory variables might be necessary depending on the task or the hosts in question.
You can enable or disable privilege escalation for specific tasks in a play.
To do this, add the appropriate become directive to the task.
---
- name: Play with two tasks, one uses privilege escalation
hosts: all
become: false
tasks:
- name: This task needs privileges
ansible.builtin.yum:
name: perl
state: installed
become: true
- name: This task does not need privileges
ansible.builtin.shell: perl -v
register: perlcheck
failed_when: perlcheck.rc != 0In the preceding example, the play has privilege escalation disabled by default, but the first task uses privilege escalation.
If you have a subset of tasks in your play that require (or do not require) privilege escalation, you can set become on a block.
All tasks in the block share the same privilege escalation setting, which overrides the setting made at the play level.
The following examples illustrate things you can do with this mechanism:
Enable privilege escalation for a subset of tasks in your play.
Disable privilege escalation for a subset of tasks in your play.
Add the become_user directive to use privilege escalation to perform a subset of tasks as some other regular user used by your application (such as a database user) instead of root.
To use this option, add the necessary directives to the block dictionary.
In the following example, the playbook contains one play that has privilege escalation turned off by default.
The play consists of a block (containing two tasks) and a separate task.
The tasks in the block use privilege escalation, even though it is off by default for the play.
The final task does not use privilege escalation because it is outside of the block.
---
- name: Deploy web services
hosts: webservers
become: false
tasks:
- name: Tasks that need privilege escalation
block:
- name: Ensure httpd is installed
ansible.builtin.yum:
name: httpd
state: installed
- name: Start and enable webserver
ansible.builtin.service:
name: httpd
state: started
enabled: true
become: true
- name: Test website from itself, do not become
ansible.builtin.uri:
url: http://{{ ansible_host }}
return_content: true
register: webpage
failed_when: webpage.status != 200You can use either of the following methods to perform privilege escalation:
The first option is for the role itself to have privilege escalation variables set inside it or on its tasks.
The documentation for the role might specify whether you have to set other variables, such as become_method, to use the role.
You can also specify this information yourself in the Ansible configuration or playbook.
Alternatively, you can set the privilege escalation settings on the play that calls the role. You can also adjust these settings on individual roles, just like you can on individual tasks or blocks:
- name: Example play with one role
hosts: localhost
roles:
- role: role-name
become: trueYou can also use connection variables to configure privilege escalation. Apply these connection variables as inventory variables on groups or individual hosts. The following table compares playbook and configuration directives with connection variable names:
Table 6.2. Privilege Escalation Directives and Connection Variables
| Configuration or playbook directive | Connection variable |
|---|---|
become
|
ansible_become
|
become_method
|
ansible_become_method
|
become_user
|
ansible_become_user
|
become_password
|
ansible_become_pass
|
You can use host-based or group-based connection variables such as ansible_become, ansible_become_method, ansible_become_user, and ansible_become_pass to override privilege escalation settings for individual hosts or groups that you originally set as configuration or playbook directives such as become, become_method, become_user, and become_pass.
Connection variables override the become settings in the configuration file, as well as in plays, tasks, blocks, and roles.
Be cautious when using connection variables to configure privilege escalation because of their high precedence compared to the configuration and playbook directives.
The following example demonstrates configuring privilege escalation variables in a YAML inventory for a group:
webservers:
hosts:
servera.lab.example.com:
serverb.lab.example.com:
vars:
ansible_become: trueYou can set connection variables for privilege escalation at the host level. Doing this on a per-host basis is tedious, but can be helpful if one host in a larger group needs specific connection options. The following example demonstrates configuring privilege escalation by using connection variables at the host level:
webservers:
hosts:
servera.lab.example.com:
ansible_become: true
serverb.lab.example.com:You can also set connection variables in the playbook; for example, on the play itself.
This overrides the inventory variables (through normal variable precedence) and the setting of any become directives.
---
- name: Example play using connection variables
hosts: webservers
vars:
ansible_become: true
tasks:
- name: Play will use privilege escalation even if inventory says no
ansible.builtin.yum:
name: perl
state: installedAs you can see, Ansible provides many ways to control privilege escalation. Consider the following competing needs when choosing how to control privilege escalation:
Run tasks with the least privilege when possible. This avoids inadvertent compromises and damage to managed hosts through playbook errors.
Keep your playbook simple. This makes it easier for you to understand the playbook and whether it is running with elevated privileges.
Some users run their playbooks with privilege escalation always turned on.
This approach is simple, and often the tasks that you are performing must run as root.
Tasks that do not need to run as root also run with elevated privileges, increasing risk.
Some users connect to the root account to avoid privilege escalation.
This is generally not a good practice.
Anyone running the playbook must have root credentials on the managed hosts.
This can also make it hard to determine which administrator performed which playbook run.
A good practice is to control which plays or tasks need privilege escalation selectively.
For example, if the apache user can start the httpd server, there is no need to run the task as the root user.
Ideally, you should configure privilege escalation in as simple a way as possible, and it should be clear to you if needed for a task.
For example, you could use become: true to enable privilege escalation for a play and then use become: false to selectively disable privilege escalation for tasks that do not require it.
Alternatively, you could group tasks that need privilege escalation into one play and tasks that do not into another play (if compatible with the workflow). Setting privilege escalation in the configuration file might be necessary if a playbook requires privilege escalation, but you cannot edit that playbook for some reason.
Generally, if the playbook requires privilege escalation, it makes more sense to state that requirement.
The biggest challenge is when different hosts that use a playbook require other privilege escalation methods to function correctly.
In that case, it can make sense to set inventory variables such as ansible_become_method for those hosts or their group when enabling privilege escalation with become in the playbook.