pyinfra icon indicating copy to clipboard operation
pyinfra copied to clipboard

files.get with sudo fail with "Permission denied"

Open KarolBedkowski opened this issue 4 months ago • 2 comments

Hi,

Describe the bug

Using files.get(..., _sudo=True) fail with PermissionError; example:

--> Starting operation: download etc files | get  
[rockpi.home.arpa]  >>> sudo -H -n sh -c '! (test -e /etc/nftables.conf || test -L /etc/nftables.conf ) || ( stat -c '"'"'user=%U group=%G mode=%A atime=%X mtime=%Y ctime=%Z size=%s %N'"'"' /etc/nftables.conf 2> /dev/null || stat -f '"'"'user=%Su group=%Sg mode=%Sp atime=%a mtime=%m ctime=%c size=%z %N%SY'"'"' /etc/nftables.conf )'
[rockpi.home.arpa]  user=root group=adm mode=-rwxr-x--- atime=1712484978 mtime=1756193730 ctime=1756193730 size=8185 '/etc/nftables.conf'
    [rockpi.home.arpa]  Loaded fact files.File (path=/etc/nftables.conf)
[rockpi.home.arpa]  >>> sudo -H -n sh -c 'cp /etc/nftables.conf /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a && chmod +r /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a'
[rockpi.home.arpa]  >>> sudo -H -n sh -c 'rm -f /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a'
    [rockpi.home.arpa]  Command socket/SSH error: PermissionError(13, 'Permission denied')
    [rockpi.home.arpa]  Error: executed 0 commands

File is copied to tmp dir but instead of use tmp of user that is used for connection, is used root's tmp dir (/tmp/user/0/) and other users don't have access to it (all dirs in /tmp/user are 700).

On error local file is truncated.

Not sure how to fix it without breaking other things...

To Reproduce

files.get(
        name="get ",
        src="/etc/nftables.conf",
        dest="nftables.conf",
        force=True,
        _sudo=True,
    )

Target os: Debian 13.

Expected behavior

With sudo there should be possible to get any file.

Meta

  • Include output of pyinfra --support.
    If you are having issues with pyinfra or wish to make feature requests, please
    check out the GitHub issues at https://github.com/Fizzadar/pyinfra/issues .
    When adding an issue, be sure to include the following:
 
    System: Linux
      Platform: Linux-6.16.1-x64-k-xanmod1-x86_64-with-glibc2.41
      Release: 6.16.1-x64-k-xanmod1
      Machine: x86_64
    pyinfra: v3.4.1
      click: v8.1.8
      click: v8.1.8
      click: v8.1.8
      distro: v1.9.0
      gevent: v24.11.1
      jinja2: v3.1.6
      packaging: v24.2
      paramiko: v3.5.1
      python-dateutil: v2.9.0.post0
      pywinrm: v0.5.0
      typeguard: v4.4.2
      typing-extensions: v4.12.2
    Executable: /home/k/.local/bin/pyinfra
    Python: 3.13.7 (CPython, GCC 14.3.0)
  • How was pyinfra installed (source/pip)?

pip

KarolBedkowski avatar Aug 27 '25 09:08 KarolBedkowski

The error seems to be removing the file with sudo, based on this line:

[rockpi.home.arpa]  >>> sudo -H -n sh -c 'rm -f /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a'

The copy to that file worked fine on the line before, not sure why the same user would not now be allowed to delete it? The commands pyinfra is using look correct...


On error local file is truncated.

Separately, this seems like a big bug, if any of the remote cmds fail we should never touch the local file 😱

Fizzadar avatar Aug 28 '25 09:08 Fizzadar

I commented try-final section in connected/ssh.py in func get_file like this

            #try:
            self._get_file(temp_file, filename_or_io)

            # Ensure that, even if we encounter an error, we (attempt to) remove the
            # temporary copy of the file.
            #finally:
            #    remove_status, output = self.run_shell_command(
            #        StringCommand("rm", "-f", temp_file),
            #        print_output=print_output,
            #        print_input=print_input,
            #        **arguments,
            #    )

And still get error:

    [pyinfra.connectors.ssh] Running command on rockpi.home.arpa: (pty=False) sudo -H -n sh -c 'cp /etc/nftables.conf /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a && chmod +r /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a'
[rockpi.home.arpa]  >>> sudo -H -n sh -c 'cp /etc/nftables.conf /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a && chmod +r /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a'
    [pyinfra.connectors.ssh] Waiting for exit status...
    [pyinfra.connectors.ssh] Command exit status: 0
    [rockpi.home.arpa]  Command socket/SSH error: PermissionError(13, 'Permission denied')
    [rockpi.home.arpa]  Error: executed 0 commands

So this is not problem with delete file (before commented this out file was deleted from host).

File copied into tmp has:

-rwxr-xr-- 1 root root 8185 08-29 08:09 /tmp/user/0/pyinfra-a302f6cd8526066f2904619397ee54c2660d795a

chmod work, but root's tmp dir is:

drwx------ 3 root root 80 08-29 08:09 /tmp/user/

So user that i use to connect don't have permission to get it.

Maybe there should be used tmp dir of user that make connection - not root.

For test in operations/fiiles.py in get: I've added temp_directory parameter:

        yield FileDownloadCommand(src, dest, remote_temp_filename=host.get_temp_filename(dest, temp_directory="/tmp"))

And now it work without any errors.

Truncate local file: If I correct:

    def _get_file(self, remote_filename: str, filename_or_io):
        with get_file_io(filename_or_io, "wb") as file_io:
            sftp = self.get_sftp_connection()
            sftp.getfo(remote_filename, file_io)

get_file_io on enter open for write file so if getfo failed local file will be empty.

KarolBedkowski avatar Aug 29 '25 06:08 KarolBedkowski