sftp: remote open("/file"): Permission denied

I login correctly to container with the command sftp -P 2222 user@ip_container To sftp shell i get this error:

sftp> put file.sh 
Uploading file.sh to /file.sh
remote open("/file.sh"): Permission denied

I have run the container with docker-compose:

sftp:
 image: atmoz/sftp
 volumes:
  - /host/sftpdata:/home/sftp/share
 ports:
  - "2222:22"
 command: sftp:password:1001

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 2
  • Comments: 24 (2 by maintainers)

Most upvoted comments

Here is a way to configure automatically the creation & permissions for a share folder: it will create a share folder for every users with a home folder and change ownership to the proper user.

docker-compose.yml

  sftp:
    image: atmoz/sftp
    volumes:
      - ./users.conf:/etc/sftp-users.conf:ro
      - ./init.sh:/etc/sftp.d/init.sh:ro

init.sh

#!/bin/bash
for user_home in /home/* ; do
  if [ -d "$user_home" ]; then
    username=`basename $user_home`
    echo "Setup $user_home/share folder for $username"
    mkdir -p $user_home/share
    chown -R $username:users $user_home/share
  fi
done

I ran into this also. For me, it was failing to pass in the subdirectory name as an argument to the container COMMAND.

In the above examples I see malnes:randompassword:1001 Which does not reference the subdirectory.

Here’s the line which sorted out the permissions issue for me. command: foo:pass:::upload

I’m using a shared volume, so have omitted the group identification mapping.

For reference, here’s my volume line from the sftp service declaration:

 volumes:
        - data-volume:/home/foo/upload

I will allow myself to note that part about permissions is yet to be clarified in main README.md. From perspective of semi-noob as a devops it easy to fail on that front I would suggest modyfing following paragraph of README.md:

Let’s mount a directory and set UID:

docker run \
    -v /host/upload:/home/foo/upload \
    -p 2222:22 -d atmoz/sftp \
    foo:pass:1001

Into:

Let’s mount a directory and set UID:

docker run \
    -v /host/upload:/home/foo/upload \
    -p 2222:22 -d atmoz/sftp \
    foo:pass:1001

NOTE: If upload fails due to “No such file or director” or “Permission denied” make sure that /host/upload is owned by same user as docker container is run by. Common scenario is that this directory is owned by root while docker is run by current $USER.

With sentence like that it will save a noob or two out there. 😉

Each user is jailed in their home directory (for security). That means root is owning the home directory, and the user can not modify it’s content. The user can only do changes inside sub-directories.

You have to create a directory (i.e. mount a volume) inside the user’s home directory, and then you can upload files there. In your case, this will be the “share” directory.

This is the nature of file permissions and volumes. They have to match if you want it to work as you expect. I will have a closer look at this challenge soon, and will add more to the documentation, so fewer people fall into this pit.

I’m having the same problem. I find the documentation a bit lacking. Why is this not enough:

docker run \
    -v /home/SFTP/malnes/share:/home/malnes/share \
    -p 2222:22 -d atmoz/sftp \
    malnes:randompassword:1001

Logging into the SFTP account, the user is not allowed to put files in either / or /share. Shouldn’t this work out of the box?

Should the /home/SFTP/malnes/share folder be created on host? (I have, with 666 permissions.)

Intuition fails me here.

None of the above worked for me. Here’s what I had to do:

services:
  sftp:
    image: atmoz/sftp
    volumes:
      - ./data:/home/user
    ports:
        - "2222:22"
    command: user:password:::folder

The gist of it is that data is empty, so folder is created inside it, with the appropriate permissions, when the service is started. Here’s how I was trying to do it:

services:
  sftp:
    image: atmoz/sftp
    volumes:
      - ./data/folder:/home/user/folder
    ports:
        - "2222:22"
    command: user:password:::folder

Ensuring the UID and GID matched did nothing for me in this case.

To anyone who is still experience the problem. Provided that the directory or volume exists. I modified the command line from the docker-compose example with foo as username.

command: foo:pass:1001:::upload

No additional permissions or scripts required to run (other than ensuring that Docker has got access on Mac etc).

so the Docker version of it on cli.

docker run \
    -v /host/upload:/home/foo/upload \
    -p 2222:22 -d atmoz/sftp \
    foo:pass:1001:::upload

FWIW I was able to get this to work just by making the /host/upload folder owned by the same uid as the docker user. In my case it was 1001.

chown 1001:1001 /host/upload

How to resovle this issue under Windows.

FWIW I was able to get this to work just by making the /host/upload folder owned by the same uid as the docker user. In my case it was 1001.

chown 1001:1001 /host/upload

I am completely stuck on this issue and I have tried everything in this thread.

Docker compose config:

version: "3.9"
services:
  web:
    build: .
    ports:
      - "6666:8080"
  sftp:
    image: atmoz/sftp
    volumes:
        - /tmp/upload:/home/testuser/upload
        - ./ssh_host_ed25519_key:/etc/ssh/ssh_host_ed25519_key
        - ./ssh_host_rsa_key:/etc/ssh/ssh_host_rsa_key
    ports:
        - "22:22"
    hostname: myftpserver
    # https://github.com/atmoz/sftp/issues/16
    command: testuser:abc123:::upload

just as @2hoursleep suggested.

In my code, I use:

with pysftp.Connection(host=hostname, username=username, port=port, cnopts=cnopts, **kwargs) as sftp:
        subdir = "/home/testuser/upload"
        logger.debug(subdir)
        for filename in filenames:
            _head, tail = os.path.split(filename)
            remote_filepath = os.path.join(subdir, tail)
            pwd = sftp.pwd
            logger.debug(f"SFTPing {filename} to {remote_filepath}; remote server pwd is {pwd}")
            sftp.put(filename, remotepath=remote_filepath, confirm=confirm)  # , preserve_mtime=True)

which produces the logs:

366a55245513_python-dissemination-api_sftp_1 | Accepted password for testuser from 172.18.0.3 port 40168 ssh2
web_1   | [2021-04-28 13:35:20,429] [dissapi.sftp] [DEBUG   ] /home/testuser/upload
web_1   | [2021-04-28 13:35:20,674] [dissapi.sftp] [DEBUG   ] SFTPing /tmp/test.txt to /home/testuser/upload/test.txt; remote server pwd is /
web_1   | [2021-04-28 13:35:20,677] ERROR in app: Exception on /sftp-upload [POST]
web_1   | Traceback (most recent call last):
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/flask/app.py", line 2447, in wsgi_app
web_1   |     response = self.full_dispatch_request()
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/flask/app.py", line 1952, in full_dispatch_request
web_1   |     rv = self.handle_user_exception(e)
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/flask/app.py", line 1821, in handle_user_exception
web_1   |     reraise(exc_type, exc_value, tb)
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/flask/_compat.py", line 39, in reraise
web_1   |     raise value
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/flask/app.py", line 1950, in full_dispatch_request
web_1   |     rv = self.dispatch_request()
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/flask/app.py", line 1936, in dispatch_request
web_1   |     return self.view_functions[rule.endpoint](**req.view_args)
web_1   |   File "dissapi/app.py", line 80, in sftp_upload
web_1   |     sftp_files(fpaths, body["destination_hostname"], body["username"], **kwargs)
web_1   |   File "./dissapi/sftp.py", line 37, in sftp_files
web_1   |     sftp.put(filename, remotepath=remote_filepath, confirm=confirm)  # , preserve_mtime=True)
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/pysftp/__init__.py", line 363, in put
web_1   |     sftpattrs = self._sftp.put(localpath, remotepath, callback=callback,
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/paramiko/sftp_client.py", line 759, in put
web_1   |     return self.putfo(fl, remotepath, file_size, callback, confirm)
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/paramiko/sftp_client.py", line 714, in putfo
web_1   |     with self.file(remotepath, "wb") as fr:
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/paramiko/sftp_client.py", line 372, in open
web_1   |     t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/paramiko/sftp_client.py", line 813, in _request
web_1   |     return self._read_response(num)
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/paramiko/sftp_client.py", line 865, in _read_response
web_1   |     self._convert_status(msg)
web_1   |   File "/home/dissapi/.local/lib/python3.9/site-packages/paramiko/sftp_client.py", line 894, in _convert_status
web_1   |     raise IOError(errno.ENOENT, text)
web_1   | FileNotFoundError: [Errno 2] No such file
web_1   | [pid: 7|app: 0|req: 1/1] 172.18.0.1 () {36 vars in 493 bytes} [Wed Apr 28 13:35:20 2021] POST /sftp-upload => generated 290 bytes in 335 msecs (HTTP/1.1 500) 2 headers in 99 bytes (1 switches on core 0)

However, it’s not true!! if I docker exec into the container:

de 366                                                 
root@myftpserver:/# cd /home/testuser/upload/
root@myftpserver:/home/testuser/upload# ls
root@myftpserver:/home/testuser/upload# pwd
/home/testuser/upload
root@myftpserver:/home/testuser/upload#

That directory definitely exists

I have also tried sftp.cd to change to /home/testuser/upload but same problem

For those still having issues:

1- Get UID of user with:

$ id -u

2- Use correct path for home dir when mounting volume + use previous UID:

For $USER:pass:$UID volume => /your/whatever/path:/home/$USER/upload