Set up secure SFTP access
SFTP runs over SSH and is a better choice than FTP, which sends credentials and files in clear text. For a contractor or colleague who needs to drop files, create a dedicated account, without a shell, confined to a specific folder. The main pitfall is the chroot: sshd refuses the connection when permissions are not exact.
Create a dedicated account
sudo groupadd sftpusers
sudo adduser --shell /usr/sbin/nologin --ingroup sftpusers usernologin blocks the interactive shell but not SFTP. The user has no sudo access. If you prefer a key, install it in /home/user/.ssh/authorized_keys before disabling the password.
Configure sshd
Open /etc/ssh/sshd_config with sudoedit and add this block at the end:
Match Group sftpusers
ChrootDirectory /srv/sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
PermitTunnel noThe Match block must be at the end of the configuration. On Debian, files in /etc/ssh/sshd_config.d/ are included at the top of the main file: a Match placed there would make the following directives conditional. ChrootDirectory confines the user to their folder; ForceCommand internal-sftp ignores any shell command. %u is replaced by the account name. Validate before reloading:
sudo sshd -t
sudo systemctl reload sshOn Debian, the service is called ssh. Keep a root session open during the test: a configuration error can cut SSH off.
Chroot permissions
The chroot directory and all its parents must be owned by root and must not be writable by the user, otherwise sshd refuses the connection.
sudo mkdir -p /srv/sftp/user/uploads
sudo chown root:root /srv/sftp /srv/sftp/user
sudo chmod 755 /srv/sftp /srv/sftp/user
sudo chown user:sftpusers /srv/sftp/user/uploads
sudo chmod 750 /srv/sftp/user/uploadsThe user writes only in uploads. 777 permissions would break the separation and expose the files. Do not store a private key or sensitive configuration in this folder.
Test
sftp [email protected]
pwd
put file.txt
ls -lpwd should show /. Try cd ..: the result must stay inside the chroot. Check journalctl -u ssh -n 50 on Broken pipe or an immediate close. Permission denied usually comes from the shell, password or chroot rights. Write failed almost always means a parent directory writable by the user.
To publish to a website, sync from uploads to /var/www with an rsync script run by a deployment account. Do not give the SFTP user direct access to the web root: they could replace code executed by PHP.
Advertisement
Was this article helpful?
Read next
- Install Python and a virtual environmentCreate a Python virtual environment, install dependencies and run the application with systemd.
- Back up a VPS properlyDecide what to back up on a VPS, produce verified archives and apply the 3-2-1 rule.
- Deploy with Git on a VPSDeploy from a Git repository with a read-only deploy key and predictable updates.