Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template and copy modules failing when using su #7553

Closed
gezerk opened this issue May 26, 2014 · 13 comments · Fixed by #7609
Closed

Template and copy modules failing when using su #7553

gezerk opened this issue May 26, 2014 · 13 comments · Fixed by #7609
Labels
bug This issue/PR relates to a bug. P2 Priority 2 - Issue Blocks Release

Comments

@gezerk
Copy link

gezerk commented May 26, 2014

Issue Type:

Bug Report

Ansible Version:

ansible 1.7 (devel 981d56b) last updated 2014/5/21 12:29:12 (GMT -400)

Environment:

Running from RHEL 6.2
Managing RHEL 5.8, 5.10, 6.2

Summary:

Template and copy modules fail when using su with the message:

{"msg": "Could not replace file: /tmp/ansible-tmp-1400646411.03-30275482183570/source to /tmp/ans_test/gsdummy.txt: [Errno 13] Permission denied: '/tmp/ansible-tmp-1400646411.03-30275482183570/source'", "failed": true}

Steps To Reproduce:

Run playbook using the copy module as an su user.

ansible-playbook demo.yml -imy_hosts.yml -u jdoe --ask-su-pass -vvv


---
- name: Copy fail as su
  hosts: su_hosts
  su: yes
  su_user: a_user

  tasks:
    - name: Copy dummy file to /tmp
       copy:
               src=dummy.txt
               dest="/tmp/sufail"
               backup=yes
Expected Results:

Requested files copied

Actual Results:
SSH password:
su password:

PLAY [Copy fail as su] ********************************************************

TASK: [Copy dummy file to /tmp] ***********************************************

$ ESTABLISH CONNECTION FOR USER: username on PORT 22 TO hostname
$ EXEC /bin/sh -c 'rc=0; [ -r "/tmp/sufail" ] || rc=2; [ -f "/tmp/sufail" ] || rc=1; [ -d "/tmp/sufail" ] && echo 3 && exit 0; (/usr/bin/md5sum /tmp/sufail 2>/dev/null) || (/sbin/md5sum -q /tmp/sufail 2>/dev/null) || (/usr/bin/digest -a md5 /tmp/sufail 2>/dev/null) || (/sbin/md5 -q /tmp/sufail 2>/dev/null) || (/usr/bin/md5 -n /tmp/sufail 2>/dev/null) || (/bin/md5 -q /tmp/sufail 2>/dev/null) || (/usr/bin/csum -h MD5 /tmp/sufail 2>/dev/null) || (/bin/csum -h MD5 /tmp/sufail 2>/dev/null) || (echo "${rc} /tmp/sufail")'
$ EXEC /bin/sh -c 'rc=0; [ -r "/tmp/sufail/dummy.txt" ] || rc=2; [ -f "/tmp/sufail/dummy.txt" ] || rc=1; [ -d "/tmp/sufail/dummy.txt" ] && echo 3 && exit 0; (/usr/bin/md5sum /tmp/sufail/dummy.txt 2>/dev/null) || (/sbin/md5sum -q /tmp/sufail/dummy.txt 2>/dev/null) || (/usr/bin/digest -a md5 /tmp/sufail/dummy.txt 2>/dev/null) || (/sbin/md5 -q /tmp/sufail/dummy.txt 2>/dev/null) || (/usr/bin/md5 -n /tmp/sufail/dummy.txt 2>/dev/null) || (/bin/md5 -q /tmp/sufail/dummy.txt 2>/dev/null) || (/usr/bin/csum -h MD5 /tmp/sufail/dummy.txt 2>/dev/null) || (/bin/csum -h MD5 /tmp/sufail/dummy.txt 2>/dev/null) || (echo "${rc} /tmp/sufail/dummy.txt")'
$ EXEC /bin/sh -c 'mkdir -p /tmp/ansible-tmp-1401139201.48-207276402368289 && chmod a+rx /tmp/ansible-tmp-1401139201.48-207276402368289 && echo /tmp/ansible-tmp-1401139201.48-207276402368289'
$ PUT /fea-adtools-dev/common/home/a_jira/ansible/ansible/dummy.txt TO /tmp/ansible-tmp-1401139201.48-207276402368289/source
$ PUT /tmp/tmpHbFDfz TO /tmp/ansible-tmp-1401139201.48-207276402368289/copy
$ EXEC /bin/sh -c 'chmod a+r /tmp/ansible-tmp-1401139201.48-207276402368289/copy'
$ EXEC /bin/sh -c 'su a_jira /bin/sh -c '"'"'echo SUDO-SUCCESS-zsezsrmctmkiqbvzajgleqmisisjmduw; LC_CTYPE=C LANG=C /usr/bin/python /tmp/ansible-tmp-1401139201.48-207276402368289/copy; rm -rf /tmp/ansible-tmp-1401139201.48-207276402368289/ >/dev/null 2>&1'"'"''
$ EXEC /bin/sh -c 'rm -rf /tmp/ansible-tmp-1401139201.48-207276402368289/ >/dev/null 2>&1'
fatal: [hostname] => failed to parse:
SUDO-SUCCESS-zsezsrmctmkiqbvzajgleqmisisjmduw
{"msg": "Could not replace file: /tmp/ansible-tmp-1401139201.48-207276402368289/source to /tmp/sufail/dummy.txt: [Errno 13] Permission denied: '/tmp/ansible-tmp-1401139201.48-207276402368289/source'", "failed": true}
Exception OSError: (2, 'No such file or directory', '/tmp/sufail/.ansible_tmpSPdetRdummy.txt') in <bound method _TemporaryFileWrapper.__del__ of <closed file '<fdopen>', mode 'w+b' at 0x7faad6e78f60>> ignored


FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
Copy dummy file to /tmp ------------------------------------------------- 1.64s
Total elapsed time: ----------------------------------------------------- 1.64s
           to retry, use: --limit @/home/a_jira/su_copy_fail_demo.retry

hostname : ok=0    changed=0    unreachable=1    failed=0

@gezerk
Copy link
Author

gezerk commented May 26, 2014

The problem is in the basic.py atomic_move method. It is trying to move the source to dest using shutil.move. A move wont work for su since the source directory is owned by the remote user.

Line 1037 has a check for sudo(os,getenv("SUDO_USER")) and in the case of sudo does a shutil.copy2 instead of shutil.move.

I was able to get it working by always doing shutil.copy2.

I think the fix needs to be adding a check for su and using shutil.copy2 when su is true. I tried to do this, but it appears the environment variable for both SUDO_USER and SU_USER are 'None' at this point in the code.

@mpdehaan
Copy link
Contributor

Can you please share the playbook you are using and the output from running with "-vvv" ?

@gezerk
Copy link
Author

gezerk commented May 26, 2014

Updated with playbook and output

@jimi-c
Copy link
Member

jimi-c commented May 27, 2014

@gezerk I believe the following patch will address the issue, however I can't quite reproduce the issue you're seeing (tried with both root and a non-root remote user). So if you could test this, I'd appreciate it:

diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
index e06b5cf..bd43ede 100644
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -1020,6 +1020,7 @@ class AnsibleModule(object):
                 context = self.selinux_default_context(dest)
 
         creating = not os.path.exists(dest)
+        switched_user = os.getlogin() != pwd.getpwuid(os.getuid())[0]
 
         try:
             # Optimistically try a rename, solves some corner cases and can avoid useless work, throws exception if not atomic.
@@ -1035,7 +1036,7 @@ class AnsibleModule(object):
                 prefix=".ansible_tmp", dir=dest_dir, suffix=dest_file)
 
             try: # leaves tmp file behind when sudo and  not root
-                if os.getenv("SUDO_USER") and os.getuid() != 0:
+                if switched_user and os.getuid() != 0:
                     # cleanup will happen by 'rm' of tempdir
                     # copy2 will preserve some metadata
                     shutil.copy2(src, tmp_dest.name)
@@ -1058,7 +1059,7 @@ class AnsibleModule(object):
             umask = os.umask(0)
             os.umask(umask)
             os.chmod(dest, 0666 ^ umask)
-            if os.getenv("SUDO_USER"):
+            if switched_user:
                 os.chown(dest, os.getuid(), os.getgid())
 
         if self.selinux_enabled():

The getlogin() call returns the original owner of the controlling process, essentially what the logname command returns. So if that does not equal the username of the current effective UID, then the user has either switched credentials via sudo or su.

jimi-c added a commit to jimi-c/ansible that referenced this issue May 27, 2014
@gezerk
Copy link
Author

gezerk commented May 28, 2014

I will give it a try as soon as possible, tomorrow at the latest. Nice
solution. Much cleaner than anything I was considering.

On Tue, May 27, 2014 at 3:48 PM, James Cammarata
notifications@github.comwrote:

@gezerk https://github.com/gezerk I believe the following patch will
address the issue, however I can't quite reproduce the issue you're seeing
(tried with both root and a non-root remote user). So if you could test
this, I'd appreciate it:

diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
index e06b5cf..dc45d8e 100644
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -1020,6 +1020,7 @@ class AnsibleModule(object):
context = self.selinux_default_context(dest)

     creating = not os.path.exists(dest)
  •    switched_user = os.getlogin() != pwd.getpwuid(os.getuid())[0]
    
     try:
         # Optimistically try a rename, solves some corner cases and can avoid useless work, throws exception if not atomic.
    

    @@ -1035,7 +1036,9 @@ class AnsibleModule(object):
    prefix=".ansible_tmp", dir=dest_dir, suffix=dest_file)

         try: # leaves tmp file behind when sudo and  not root
    
  •            if os.getenv("SUDO_USER") and os.getuid() != 0:
    
  •            # if the original user is not the same as the current user,
    
  •            # then the user has switched credentials via sudo or su
    
  •            if switched_user and os.getuid() != 0:
                 # cleanup will happen by 'rm' of tempdir
                 # copy2 will preserve some metadata
                 shutil.copy2(src, tmp_dest.name)
    

    @@ -1058,7 +1061,7 @@ class AnsibleModule(object):
    umask = os.umask(0)
    os.umask(umask)
    os.chmod(dest, 0666 ^ umask)

  •        if os.getenv("SUDO_USER"):
    
  •        if switched_user:
             os.chown(dest, os.getuid(), os.getgid())
    
     if self.selinux_enabled():
    

The getlogin() call returns the original owner of the controlling
process, essentially what the logname command returns. So if that does
not equal the username of the current effective UID, then the user has
either switched credentials via sudo or su.


Reply to this email directly or view it on GitHubhttps://github.com//issues/7553#issuecomment-44333161
.

George Simpson

@gezerk
Copy link
Author

gezerk commented May 30, 2014

I tested the patch, works great. Thanks again for the fix! Let me know if you ever need me to test the su functionality. That's the primary way we use ansible. I am happy to test anytime you need it.

@jimi-c
Copy link
Member

jimi-c commented May 30, 2014

Great, I've merged that in. Please let us know if you run into any further problems regarding this. Thanks!

@perhallstroem
Copy link

I ran into a problem using the latest revision (6bc056e):

failed: [localhost] => {"failed": true, "item": "", "parsed": false}
invalid output was: Traceback (most recent call last):
  File "/home/vagrant/.ansible/tmp/ansible-tmp-1401777405.0-124943507447833/lineinfile", line 1577, in <module>
    main()
  File "/home/vagrant/.ansible/tmp/ansible-tmp-1401777405.0-124943507447833/lineinfile", line 361, in main
    ins_aft, ins_bef, create, backup, backrefs)
  File "/home/vagrant/.ansible/tmp/ansible-tmp-1401777405.0-124943507447833/lineinfile", line 270, in present
    write_changes(module, lines, dest)
  File "/home/vagrant/.ansible/tmp/ansible-tmp-1401777405.0-124943507447833/lineinfile", line 165, in write_changes
    module.atomic_move(tmpfile, dest)
  File "/home/vagrant/.ansible/tmp/ansible-tmp-1401777405.0-124943507447833/lineinfile", line 1391, in atomic_move
    switched_user = os.getlogin() != pwd.getpwuid(os.getuid())[0]
OSError: [Errno 22] Invalid argument

After a bit of digging around I had a look at the recent commits and saw this issue mentioned in a pull request, so I went back to commit 2fda9bc (the commit before the pull request referencing this issue was merged in), at which point "lineinfile" worked again. Don't know if it helps to know:

$ uname -a
Linux precise64 3.2.0-23-generic #36-Ubuntu SMP Tue Apr 10 20:39:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

@jimi-c
Copy link
Member

jimi-c commented Jun 3, 2014

@perhallstroem could you please open a new issue for this? Thanks!

@indrgun
Copy link

indrgun commented Jan 18, 2016

This is still not working. I am using ansible 2.0.0.2:

fatal: [sjc-nmtgci-15]: FAILED! => {"changed": true, "failed": true, "invocation": {"module_args": {"dest": "/opt/nmtgre-tools/jenkins/config/jenkins", "group": "eng", "mode": "0600", "owner": "px-build", "src": "default-jenkins.j2"}, "module_name": "template"}, "module_stderr": "", "module_stdout": "\r\nBECOME-SUCCESS-vtgcupyzjtojsbtboeyttvmiwyunzkwc\r\n{"msg": "Could not replace file: /users/ingunawa/.ansible/tmp/ansible-tmp-1453075333.7-198301965511122/source to /opt/nmtgre-tools/jenkins/config/jenkins: [Errno 13] Permission denied: '/users/ingunawa/.ansible/tmp/ansible-tmp-1453075333.7-198301965511122/source'", "failed": true, "invocation": {"module_args": {"src": "/users/ingunawa/.ansible/tmp/ansible-tmp-1453075333.7-198301965511122/source", "directory_mode": null, "force": true, "follow": true, "remote_src": null, "dest": "/opt/nmtgre-tools/jenkins/config/jenkins", "selevel": null, "seuser": null, "serole": null, "content": null, "setype": null, "original_basename": "default-jenkins.j2", "delimiter": null, "mode": "0600", "regexp": null, "owner": "px-build", "group": "eng", "validate": null, "backup": false}}}\r\nException exceptions.OSError: (2, 'No such file or directory', '/opt/nmtgre-tools/jenkins/config/.ansible_tmpFDjmX5jenkins') in <bound method _TemporaryFileWrapper.del of <closed file '', mode 'w+b' at 0x16019a80>> ignored\r\n", "msg": "MODULE FAILURE", "parsed": false}

@indrgun
Copy link

indrgun commented Jan 18, 2016

The full error:

PUT /tmp/tmpUjjIrF TO /users/ingunawa/.ansible/tmp/ansible-tmp-1453075333.7-198301965511122/source
EXEC ( umask 22 && mkdir -p "$( echo $HOME/.ansible/tmp/ansible-tmp-1453075334.26-42896703240882 )" &amp;&amp; echo "$( echo $HOME/.ansible/tmp/ansible-tmp-1453075334.26-42896703240882 )" )
PUT /tmp/tmpcz5OYl TO /users/ingunawa/.ansible/tmp/ansible-tmp-1453075334.26-42896703240882/copy
EXEC /bin/sh -c 'sudo -H -S -p "[sudo via ansible, key=vtgcupyzjtojsbtboeyttvmiwyunzkwc] password: " -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-vtgcupyzjtojsbtboeyttvmiwyunzkwc; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /users/ingunawa/.ansible/tmp/ansible-tmp-1453075334.26-42896703240882/copy; rm -rf "/users/ingunawa/.ansible/tmp/ansible-tmp-1453075334.26-42896703240882/" > /dev/null 2>&1'"'"''
fatal: [sjc-nmtgci-15]: FAILED! => {"changed": true, "failed": true, "invocation": {"module_args": {"dest": "/opt/nmtgre-tools/jenkins/config/jenkins", "group": "eng", "mode": "0600", "owner": "px-build", "src": "default-jenkins.j2"}, "module_name": "template"}, "module_stderr": "", "module_stdout": "\r\nBECOME-SUCCESS-vtgcupyzjtojsbtboeyttvmiwyunzkwc\r\n{"msg": "Could not replace file: /users/ingunawa/.ansible/tmp/ansible-tmp-1453075333.7-198301965511122/source to /opt/nmtgre-tools/jenkins/config/jenkins: [Errno 13] Permission denied: '/users/ingunawa/.ansible/tmp/ansible-tmp-1453075333.7-198301965511122/source'", "failed": true, "invocation": {"module_args": {"src": "/users/ingunawa/.ansible/tmp/ansible-tmp-1453075333.7-198301965511122/source", "directory_mode": null, "force": true, "follow": true, "remote_src": null, "dest": "/opt/nmtgre-tools/jenkins/config/jenkins", "selevel": null, "seuser": null, "serole": null, "content": null, "setype": null, "original_basename": "default-jenkins.j2", "delimiter": null, "mode": "0600", "regexp": null, "owner": "px-build", "group": "eng", "validate": null, "backup": false}}}\r\nException exceptions.OSError: (2, 'No such file or directory', '/opt/nmtgre-tools/jenkins/config/.ansible_tmpFDjmX5jenkins') in <bound method _TemporaryFileWrapper.del of <closed file '', mode 'w+b' at 0x16019a80>> ignored\r\n", "msg": "MODULE FAILURE", "parsed": false}

@viper233
Copy link
Contributor

viper233 commented Jun 3, 2016

Was a new issues created for this? @perhallstroem @jimi-c ? Noticing this issue with ansible 2.0.0.2 on ubuntu 16.04 and the latest devel, ansible 2.2.0 (devel 844b415).

- name: "applying {{ syslog_ng_mode }} mode configuration if the generated syntax is OK"
  when: syslog_ng_check_syntax_before_reload == true
  template:
    src="{{syslog_ng_mode}}.j2"
    dest="{{syslog_ng_config_file}}"
    owner="{{syslog_ng_user}}"
    group="{{syslog_ng_group}}"
    validate='syslog-ng --syntax-only --cfgfile %s'
  notify:
  - reload syslog-ng

The validate command is failing

fatal: [*.*.*.*]: FAILED! => {"changed": true, "failed": true, "invocation": {"module_args": {"backup": false, "content": null, "delimiter": null, "dest": "/etc/syslog-ng/syslog-ng.conf", "directory_mode": null, "follow": true, "force": true, "group": "root", "mode": null, "original_basename": "server.j2", "owner": "root", "regexp": null, "remote_src": null, "selevel": null, "serole": null, "setype": null, "seuser": null, "src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1464985868.64-53342080025062/source", "validate": "syslog-ng --syntax-only --cfgfile %s"}}, "msg": "failed to validate: rc:1 error:Error opening configuration file; filename='/home/ubuntu/.ansible/tmp/ansible-tmp-1464985868.64-53342080025062/source', error='Permission denied (13)'\n"}

implementing ihrwein/ansible-syslog-ng role

@jimi-c
Copy link
Member

jimi-c commented Jun 5, 2016

@viper233 if so, please open a new issue for that. Thanks!

@ansibot ansibot added bug This issue/PR relates to a bug. and removed bug_report labels Mar 6, 2018
@ansible ansible locked and limited conversation to collaborators Apr 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug This issue/PR relates to a bug. P2 Priority 2 - Issue Blocks Release
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants