This is a very common scenario: you want to setup SSH access for an untrusted user, but strictly limit his capabilities to SFTP (or scp).
Usually the requirements are just two:
- The user can only access your machine to run the SFTP command, no other uses of the SSH service will be allowed for this user
- The user can only access a very restricted environment and not break outside of it (so he cannot access files that he’s not supposed to access)
Depending on the degree of untrustyness, you may also want to avoid DoS attacks on the service, but for this, the best measure is the hardening of the whole SSH service.
Solution 1: SFTP chroot
Spoiler: this is way more complicated than solution 2
This method is the most common. The idea is to force users belonging to a group (or single user ids) to just run a specific command and be rooted to a specific directory. The command is the SSH subsystem for SFTP (internal-sftp). On Ubuntu, this may not be the default, and you may have to change this setting in the sshd_config.
Concisely, you can setup this solution as follows:
- Please ensure not to lock yourself out because of SSH misconfigurations that would crash the ssh daemon when it restarts (e.g., outdated SSH not supporting the options, typos, etc)
- Check your sshd configuration in /etc/ssh/sshd_config and use the internal-sftp subsystem for sftp. You may have to comment-out the line that sets the sftp-server (if it’s there) and add the new line for the internal-sftp (here is an excellent overview of internal-sftp vs sftp-server):
#Subsystem sftp /usr/lib/openssh/sftp-server Subsystem sftp internal-sftp
- Use a system group of your choice (e.g., sftponly) to apply the restriction (you can also do this on single users). Users belonging to this group will be “sftp-restricted”. Create the group:
- Define sshd directives for users belonging to this group. Add these lines at the very end of the sshd_config file or what will follow them will be interpreted as part of the block (and this may prevent the ssh service from (re-)starting):
Match group sftponly ChrootDirectory %h X11Forwarding no PermitTunnel no AllowTcpForwarding no ForceCommand internal-sftp
- Restart the ssh service
- From now, I will assume that the user you have to give sftp access to is named “username” on the system. Ensure that the home directory of the user is owned by root, but the shared directory is owned by the user, so he can upload files there (run these commands as root):
chown root:root /home/username chmod 0755 /home/username mkdir /home/username/shared chown username:username /home/username/shared
The .ssh folder and the .ssh/authorized_keys file should also be owned by root and only readable/traversable by the user. This is to avoid that the user uploads a new version of the authorized_keys file (run these commands as root, you may need to create the .ssh folder and authorized_keys files first):
chown root:root /home/username/.ssh chown root:root /home/username/.ssh/authorized_keys chmod 755 /home/username/.ssh chmod 744 /home/username/.ssh/authorized_keys
- Add the user to the group:
gpasswd -a username sftponly
With this setup, users belonging to the sftponly group will not be able to establish a “normal” ssh connection to the server:
$ ssh firstname.lastname@example.org This service allows sftp connections only. Connection to 127.0.0.1 closed.
Sftp connections will instead work. Users will land on a read-only environment (the chroot directory) and will have to move to the shared directory to be able to upload files (note that they will be able to download files from the chroot directory, that is, their home folder).
# User cannot write on the landing folder sftp> put Vagrantfile . Uploading Vagrantfile to /./Vagrantfile remote open("/./Vagrantfile"): Permission denied # Shared subfolder is writable sftp> put Vagrantfile shared Uploading Vagrantfile to /shared/Vagrantfile Vagrantfile
Solution 2: use a Docker container
The atmoz/sftp is an excellent docker image that will allow you to instantiate an isolated sftp environment similar to what is described in “Solution 1”, but in a couple of minutes.
A simple usage of the command to setup an sftp service for user username with password password and bind-mount a local directory at the shared path is:
docker run -v /var/sftp/:/home/username/shared -p 1022:22 -d atmoz/sftp username:password
User will be able to write the shared directory:
$ sftp -P 1022 username@n7 username@n7's password: Connected to n7. sftp> ls shared
It’s also easy to provide authorized keys or host keys for a known fingerprint. For example, public key files can be mounted to the /home/username/.ssh/keys/ directory: