[PYTHON] Set PATH equivalent to "sudo su-" using Ansible environment

■ What is Ansible?

■ Introduction

Starting with Ansible 1.9,'become' has been added to run with other user privileges (eg root privileges).

If you want to manage a server that is prohibited from direct access as root, you should log in as a general user and then use sudo su - to become root. When executing a command with become: ture in Ansible, and when executing a command directly from the command line after sudo su -, the command execution result may differ because the PATH environment variable is different. Yes, you need to be careful.

Until now, I used this area without much awareness, so the command that was confirmed by directly executing the command sometimes failed in PlayBook, so I had a little trouble, so about become and the environment variable PATH, I checked it again.

Stumble point

View of Ansible head family

I investigated the issues on GitHub, the official website of Ansible.

Ansible official website (link)

Only one method may be enabled per host

Methods cannot be chained. You cannot use sudo /bin/su - to become a user, you need to have privileges to run the command as that user in sudo or be able to su directly to it.

Free translation:

sudo and su cannot be used at the same time, so if you want to use sudo su -, execute the command directly.

Ansible GitHub issues It seems that the discussion at the head family has not reached a conclusion (the status remains Open).   GitHub issues#12686

■ Operation verification

First, I investigated what the PATH would look like. Survey environment: Amazon Linux AMI 2016.03

remote_user: ec2-user

1. become: false(ec2-user)   /usr/local/bin:/bin:/usr/bin:/opt/aws/bin 2. become: true(root)   /sbin:/bin:/usr/sbin:/usr/bin 3. lookup('pipe', 'echo $PATH')   /usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:   /sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin 4. lookup('env', 'PATH')   /usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:   /sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin 5. ec2-user   /usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:   /sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin 6. sudo su -   /usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:   /opt/aws/bin:/root/bin 7. sudo su   /sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin

It's splendidly disjointed ^^;

■ Set the PATH equivalent to "sudo su-"

A Google search will show you how to read .bashrc and .bash_profile with command and shell, but doing so makes it difficult to guarantee idempotency. Therefore, I will introduce how to set the PATH in the environment.

** State before setting **

First, let's see what happens if nothing is done.

playbook.yml


- hosts: all
  remote_user: ec2-user
  tasks:
    - command: echo $PATH
      register: become_path
      become: true
      changed_when: False

    - name: become
      debug: var=become_path.stdout

    - command: echo $PATH
      register: not_become_path
      changed_when: False

    - name: not become
      debug: var=not_become_path.stdout

I will try it.

$ ansible-playbook env_path.yml

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [target03]

TASK [command] *****************************************************************
ok: [target03]

TASK [become] ******************************************************************
ok: [target03] => {
    "become_path.stdout": "/sbin:/bin:/usr/sbin:/usr/bin"
}

TASK [command] *****************************************************************
ok: [target03]

TASK [not become] **************************************************************
ok: [target03] => {
    "not_become_path.stdout": "/usr/local/bin:/bin:/usr/bin:/opt/aws/bin"
}

PLAY RECAP *********************************************************************
target03                   : ok=5    changed=0    unreachable=0    failed=0

General user PATH: / usr / local / bin: / bin: / usr / bin: / opt / aws / bin root PATH: / sbin: / bin: / usr / sbin: / usr / bin

It has become. It seems that you are not logged in.

If you use lookup, it will be as follows.

playbook.yml


---
- hosts: all
  remote_user: ec2-user
  vars:
    pipe_path: "{{ lookup('pipe', 'echo $PATH') }}"
    env_path: "{{ lookup('env', 'PATH') }}"
  tasks:
    - name: pipe path
      debug: msg={{ pipe_path }}

    - name: env path
      debug: msg={{ env_path }}

The execution result when using lookup is as follows.

$ ansible-playbook playbook.yml

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [target01]

TASK [pipe path]] **************************************************************
ok: [target01] => {
    "msg": "/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin"
}

TASK [env path]] ***************************************************************
ok: [target01] => {
    "msg": "/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin"
}

PLAY RECAP *********************************************************************
target01                   : ok=3    changed=0    unreachable=0    failed=0

Here, I was logged in as ec2-user.

** Try setting PATH in environment **

Try setting PATH in environment.

In advance, do sudo su - and check ʻecho $ PATH`.

$ sudo su -
# echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin

The PlayBook looks like this:

playbook.yml


- hosts: all
  remote_user: ec2-user
  environment:
    PATH: "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"
  tasks:
    - command: echo $PATH
      register: become_path
      become: true
      changed_when: False

    - name: become
      debug: var=become_path.stdout

    - command: echo $PATH
      register: not_become_path
      changed_when: False

    - name: not become
      debug: var=not_become_path.stdout

Let's run it.

$ ansible-playbook playbook.yml

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [target01]

TASK [command] *****************************************************************
ok: [target01]

TASK [become] ******************************************************************
ok: [target01] => {
    "become_path.stdout": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"
}

TASK [command] *****************************************************************
ok: [target01]

TASK [not become] **************************************************************
ok: [target01] => {
    "not_become_path.stdout": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"
}

PLAY RECAP *********************************************************************
target01                   : ok=5    changed=0    unreachable=0    failed=0

Since ʻenvironment:is set for PlayBook, both whenbecome: true` and not, it is the set PATH.

By the way, ʻenvironment:` can also be written in the task. If you want to set it for each task, you need to specify it for each task. The PlayBook looks like this:

playbook.yml


- hosts: all
  remote_user: ec2-user
  tasks:
    - command: echo $PATH
      register: become_path
      become: true
      changed_when: False
      environment:
        PATH: "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"

    - name: become
      debug: var=become_path.stdout

    - command: echo $PATH
      register: not_become_path
      changed_when: False
      environment:
        PATH: "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"

    - name: not become
      debug: var=not_become_path.stdout

The execution result will be the same as when ʻenvironment:` is set for the entire PlayBook.

** environment scope **

The scope of ʻenvironment:` set for PlayBook and Task is the set range. For example, if you set it for a PlayBook, it will not affect another PlayBook. If you enumerate the commands yourself and execute them with / bin / sh, you also have to write a command to restore the set PATH, but Ansible's PlayBook is convenient because it will do that automatically. ..

■ Conclusion

This time, I introduced how to set PATH using environment.

The PATH setting method is as described, but PATH-dependent PlayBooks can get stuck in unexpected pitfalls. When using the shell and command modules in PlayBook, I think it's safe to make a habit of writing with the full path.

When using a configuration management tool like Ansible, it's a good idea to try to be able to operate with maintainability and robustness in mind, rather than simply operating.

Recommended Posts

Set PATH equivalent to "sudo su-" using Ansible environment
How to set up a Python environment using pyenv
How to set up SVM using Optuna
How to set xg boost using Optuna
How to set up Random forest using Optuna
How to set up Random forest using Optuna
Procedure to set hydrogen of atom (virtual environment)