Repository: ansible/ansible

Issue:
ansible_ssh_user: root breaks become
When `ansible_ssh_user` is set to `root` for a host, trying to run a local action in combination with `become` fails to escalate privileges. Setting it to any other user (even a non-existant one) causes the problem to go away. It looks as if ansible assumes it already is `root` and skips the privilege escalation.

Thanks to @larsks for help in debugging this.

Playbook to reproduce:

```
- hosts: localhost
  tasks:
  - name: test local sudo
    local_action: command whoami
    become: true
    become_user: root
    become_method: sudo
    register: test
  - debug: msg={{ test.stdout }}

- hosts: localhost
  tasks:
  - name: test local sudo
    local_action: command whoami
    become: true
    become_user: root
    become_method: sudo
    register: test
  - debug: msg={{ test.stdout }}
  vars:
  - ansible_ssh_user: root
```

If I run this playbook, the `debug` task for the first play shows `root`, but the task for the second play shows the username of the acount that ran ansible:

```
Using /home/rene/.ansible.cfg as config file
 [WARNING]: provided hosts list is empty, only localhost is available

2 plays in test.yml
Loaded callback default of type stdout, v2.0

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ESTABLISH LOCAL CONNECTION FOR USER: rene
127.0.0.1 EXEC (umask 22 && mkdir -p "$HOME/.ansible/tmp/ansible-tmp-1445006236.71-242733813443796" && echo "$HOME/.ansible/tmp/ansible-tmp-1445006236.71-242733813443796")
127.0.0.1 PUT /tmp/tmpjL9LvS TO /home/rene/.ansible/tmp/ansible-tmp-1445006236.71-242733813443796/setup
127.0.0.1 EXEC LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /home/rene/.virtualenvs/ansible/bin/python2 /home/rene/.ansible/tmp/ansible-tmp-1445006236.71-242733813443796/setup; rm -rf "/home/rene/.ansible/tmp/ansible-tmp-1445006236.71-242733813443796/" > /dev/null 2>&1
ok: [localhost]

TASK [test local sudo] *********************************************************
ESTABLISH LOCAL CONNECTION FOR USER: rene
127.0.0.1 EXEC (umask 22 && mkdir -p "$HOME/.ansible/tmp/ansible-tmp-1445006237.45-179622536179136" && echo "$HOME/.ansible/tmp/ansible-tmp-1445006237.45-179622536179136")
127.0.0.1 PUT /tmp/tmptU8RiJ TO /home/rene/.ansible/tmp/ansible-tmp-1445006237.45-179622536179136/command
127.0.0.1 EXEC /bin/sh -c 'sudo -H -n -S -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-bgtvxiqeakugpfwzmvnhapsarztrdmcm; LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /home/rene/.virtualenvs/ansible/bin/python2 /home/rene/.ansible/tmp/ansible-tmp-1445006237.45-179622536179136/command; rm -rf "/home/rene/.ansible/tmp/ansible-tmp-1445006237.45-179622536179136/" > /dev/null 2>&1'"'"''
changed: [localhost] => {"changed": true, "cmd": ["whoami"], "delta": "0:00:00.008745", "end": "2015-10-16 16:37:17.528133", "rc": 0, "start": "2015-10-16 16:37:17.519388", "stderr": "", "stdout": "root", "stdout_lines": ["root"], "warnings": []}

TASK [debug msg={{ test.stdout }}] *********************************************
ok: [localhost] => {
    "changed": false, 
    "msg": "root"
}

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ESTABLISH LOCAL CONNECTION FOR USER: root
127.0.0.1 EXEC mkdir -p "$HOME/.ansible/tmp/ansible-tmp-1445006237.65-234743956558974" && echo "$HOME/.ansible/tmp/ansible-tmp-1445006237.65-234743956558974"
127.0.0.1 PUT /tmp/tmpJLdmn9 TO /home/rene/.ansible/tmp/ansible-tmp-1445006237.65-234743956558974/setup
127.0.0.1 EXEC LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /home/rene/.virtualenvs/ansible/bin/python2 /home/rene/.ansible/tmp/ansible-tmp-1445006237.65-234743956558974/setup; rm -rf "/home/rene/.ansible/tmp/ansible-tmp-1445006237.65-234743956558974/" > /dev/null 2>&1
ok: [localhost]

TASK [test local sudo] *********************************************************
ESTABLISH LOCAL CONNECTION FOR USER: root
127.0.0.1 EXEC mkdir -p "$HOME/.ansible/tmp/ansible-tmp-1445006238.38-130229664208850" && echo "$HOME/.ansible/tmp/ansible-tmp-1445006238.38-130229664208850"
127.0.0.1 PUT /tmp/tmpLLKRHa TO /home/rene/.ansible/tmp/ansible-tmp-1445006238.38-130229664208850/command
127.0.0.1 EXEC LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /home/rene/.virtualenvs/ansible/bin/python2 /home/rene/.ansible/tmp/ansible-tmp-1445006238.38-130229664208850/command; rm -rf "/home/rene/.ansible/tmp/ansible-tmp-1445006238.38-130229664208850/" > /dev/null 2>&1
changed: [localhost] => {"changed": true, "cmd": ["whoami"], "delta": "0:00:00.008726", "end": "2015-10-16 16:37:18.450391", "rc": 0, "start": "2015-10-16 16:37:18.441665", "stderr": "", "stdout": "rene", "stdout_lines": ["rene"], "warnings": []}

TASK [debug msg={{ test.stdout }}] *********************************************
ok: [localhost] => {
    "changed": false, 
    "msg": "rene"
}

PLAY RECAP *********************************************************************
localhost                  : ok=6    changed=2    unreachable=0    failed=0   
```



Discussion:
So, here, I think, is the problem, in `lib/ansible/plugins/action/__init__.py`:

```
allow_same_user = C.BECOME_ALLOW_SAME_USER                              
same_user = self._play_context.become_user == self._play_context.remote_user
if sudoable and self._play_context.become and (allow_same_user or not same_user):
    self._display.debug("using become for this command")                
    cmd = self._play_context.make_become_cmd(cmd, executable=executable)
```

If `become_user` == `remote_user`, ansible will not attempt any sort
of privilege escalation.


Resolving patch:
diff --git a/lib/ansible/plugins/connection/local.py b/lib/ansible/plugins/connection/local.py
--- a/lib/ansible/plugins/connection/local.py
+++ b/lib/ansible/plugins/connection/local.py
@@ -24,6 +24,7 @@
 import subprocess
 import select
 import fcntl
+import getpass
 
 import ansible.constants as C
 
@@ -41,6 +42,11 @@ def transport(self):
     def _connect(self, port=None):
         ''' connect to the local host; nothing to do here '''
 
+        # Because we haven't made any remote connection we're running as
+        # the local user, rather than as whatever is configured in
+        # remote_user.
+        self._play_context.remote_user = getpass.getuser()
+
         if not self._connected:
             self._display.vvv("ESTABLISH LOCAL CONNECTION FOR USER: {0}".format(self._play_context.remote_user, host=self._play_context.remote_addr))
             self._connected = True


Test patch: