Repository: ansible/ansible

Issue:
Can't become unprivileged user without pipelining on Solaris
##### ISSUE TYPE
- Bug Report
##### COMPONENT NAME

`become`
##### ANSIBLE VERSION

```
ansible 2.2.0.0 (detached HEAD 44faad0593) last updated 2016/10/05 11:58:25 (GMT +100)
  lib/ansible/modules/core: (detached HEAD d66983b43e) last updated 2016/10/05 11:58:45 (GMT +100)
  lib/ansible/modules/extras: (detached HEAD 35132b892f) last updated 2016/10/05 11:58:51 (GMT +100)
  config file = /home/mavit/etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides

```
##### CONFIGURATION

`pipelining = False`
`remote_tmp = /var/tmp` or commented out.
##### OS / ENVIRONMENT

Solaris
##### SUMMARY

[Becoming an Unprivileged User](https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user) discusses using POSIX ACLs when both the remote user and become user are not `root`.  This doesn't work on Solaris, because `/tmp` is a `tmpfs` filesystem, which doesn't support POSIX ACLs.

Now, `remote_tmp = /var/tmp` could be set in `ansible.cfg`, but:
- You can't do `setfacl -m u:becomeuser:rx ...` on Solaris; you have to do `setfacl -m u:becomeuser:r-x ...` (note the padding of the permissions).  The latter works on Linux too.
- Recent systems use ZFS for `/var`, which doesn't support POSIX ACLs, but instead supports NFSv4 ACLs.  On Solaris, NFSv4 ACLs are set thus: `chmod "A+user:becomeuser:read_set:allow" ...`

For an example of this in action (unrelated to Ansible), see, e.g., https://github.com/mavit/xsudo/blob/master/xsudo#L47

I notice that the code also tries to `chmod` the files to the become user, but Solaris doesn't (without kernel tuning) allow unprivileged users to give away their files.
##### STEPS TO REPRODUCE

``` yaml
- hosts: localhost
  tasks:
    - command: id
      become: true
      become_user: becomeuser
```
##### EXPECTED RESULTS

Success!
##### ACTUAL RESULTS

```
TASK [command] *****************************************************************
task path: /home/mavit/etc/ansible/test.yml:3
Using module file /home/mavit/src/ansible/lib/ansible/modules/core/commands/command.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: mavit
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /var/tmp/ansible-tmp-1475750737.08-265275545330119 `" && echo ansible-tmp-1475750737.08-265275545330119="` echo /var/tmp/ansible-tmp-1475750737.08-265275545330119 `" ) && sleep 0'
<127.0.0.1> PUT /tmp/tmpRwSDDz TO /var/tmp/ansible-tmp-1475750737.08-265275545330119/command.py
<127.0.0.1> EXEC /bin/sh -c 'setfacl -m u:becomeuser:rx /var/tmp/ansible-tmp-1475750737.08-265275545330119/ /var/tmp/ansible-tmp-1475750737.08-265275545330119/command.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /var/tmp/ansible-tmp-1475750737.08-265275545330119/ /var/tmp/ansible-tmp-1475750737.08-265275545330119/command.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'chown becomeuser /var/tmp/ansible-tmp-1475750737.08-265275545330119/ /var/tmp/ansible-tmp-1475750737.08-265275545330119/command.py && sleep 0'
fatal: [localhost]: FAILED! => {
    "failed": true, 
    "msg": "Failed to set permissions on the temporary files Ansible needs to create when becoming an unprivileged user (rc: 2, err: chown: /var/tmp/ansible-tmp-1475750737.08-265275545330119/: Not owner\nchown: /var/tmp/ansible-tmp-1475750737.08-265275545330119/command.py: Not owner\n). For information on working around this, see https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user"
}
```



Discussion:
By the way, this isn't terribly bothersome for me, because I usually have pipelining enabled.  I only noticed it when investigating #17908.

try setting allow_world_readable_tmpfiles to true in ansible.cfg

Yeah, allow_world_readable_tmpfiles is probably the answer here.  We only support POSIX acls.  If you actually have a system which needs "r-x" I can modify the code to do that but since ZFS is so widespread, it may not be worth it otherwise... (Every time we touch this, there's some odd platform that doesn't understand the new implementation... So I hate to change it for Solaris's POSIX acls if no one running Solaris will ever take advantage of it.)

Assuming that allow_world_readable_tmpfiles works, I'll close this in a week or two.

> If you actually have a system which needs "r-x" I can modify the code to do that but since ZFS is so widespread, it may not be worth it otherwise... (Every time we touch this, there's some odd platform that doesn't understand the new implementation... So I hate to change it for Solaris's POSIX acls if no one running Solaris will ever take advantage of it.)

I'm confident that there are plenty of old Solaris systems in the wild that don't use ZFS; it wasn't available in the initial release of Solaris 10, and Solaris 10 is still supported until 2021.  However, although it would be nice for `become` to work without any additional configuration, I don't think there's anything preventing people with these systems from enabling pipelining.

> Assuming that allow_world_readable_tmpfiles works

It does.


Resolving patch:
diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py
--- a/lib/ansible/plugins/action/__init__.py
+++ b/lib/ansible/plugins/action/__init__.py
@@ -358,9 +358,12 @@ def _fixup_perms2(self, remote_paths, remote_user, execute=True):
             # Try to use file system acls to make the files readable for sudo'd
             # user
             if execute:
-                mode = 'rx'
+                mode = 'r-x'
             else:
-                mode = 'rX'
+                ### Note: this form fails silently on freebsd.  We currently
+                # never call _fixup_perms2() with execute=False but if we
+                # start to we'll have to fix this.
+                mode = 'r-X'
 
             res = self._remote_set_user_facl(remote_paths, self._play_context.become_user, mode)
             if res['rc'] != 0:


Test patch: