Restricting SSH users to SFTP-only access reduces exposure to interactive shells while still providing a convenient way to exchange files. Limiting certain accounts to file transfer service simplifies delegation for partners, automation jobs, and internal teams that do not require full shell privileges.
OpenSSH implements SFTP using the internal-sftp subsystem inside the sshd daemon, controlled by directives in /etc/ssh/sshd_config. Configuration can select specific accounts or groups using Match blocks and place them inside a chroot jail via ChrootDirectory. Within that environment, sshd exposes a restricted filesystem view and runs only the SFTP protocol without spawning a user shell.
In a chroot-based SFTP setup, the directory tree referenced by ChrootDirectory and all of its parents must be owned by root and remain non-writable for ordinary users, otherwise sshd refuses login for safety. Incorrect permissions or syntax errors in the configuration may break access for restricted accounts or prevent the daemon from starting. On typical Linux servers running OpenSSH with systemd, maintaining a backup of the current configuration and an alternative access path, such as console or out-of-band management, reduces the risk of lockouts when reloading sshd.
Related: How to copy files remotely using SSH
Related: How to apply conditional SSH settings
Related: How to disable SSH TCP forwarding
Steps to restrict SSH users to SFTP only:
- Open a terminal session on the SSH server using an account with sudo privileges.
$ whoami admin $ id uid=1000(admin) gid=1000(admin) groups=1000(admin),27(sudo)
- Create a dedicated sftpusers group for SFTP-only accounts.
$ sudo groupadd --system sftpusers
Using a separate sftpusers group keeps SFTP-only policy isolated from other SSH users.
- Create an SFTP-only account in the sftpusers group.
$ sudo useradd -M -s /usr/sbin/nologin -g sftpusers sftpuser1 $ sudo passwd sftpuser1 New password: Retype new password: passwd: password updated successfully
The -M option skips home directory creation because the account uses a dedicated chroot under /srv/sftp.
- Prepare the shared SFTP chroot base directory owned by root.
$ sudo mkdir -p /srv/sftp $ sudo chmod 755 /srv/sftp $ sudo chown root:root /srv/sftp $ ls -ld /srv/sftp drwxr-xr-x 3 root root 4096 May 13 10:00 /srv/sftp
- Create a per-user directory inside the chroot containing a writable upload folder.
$ sudo mkdir -p /srv/sftp/sftpuser1/upload $ sudo chown root:root /srv/sftp/sftpuser1 $ sudo chmod 755 /srv/sftp/sftpuser1 $ sudo chown sftpuser1:sftpusers /srv/sftp/sftpuser1/upload $ sudo chmod 750 /srv/sftp/sftpuser1/upload $ ls -ld /srv/sftp/sftpuser1 /srv/sftp/sftpuser1/upload drwxr-xr-x 3 root root 4096 May 13 10:02 /srv/sftp/sftpuser1 drwxr-x--- 2 sftpuser1 sftpusers 4096 May 13 10:02 /srv/sftp/sftpuser1/upload
If /srv/sftp or the ChrootDirectory path is owned or made writable by the SFTP user or its group, sshd rejects SFTP logins for that account.
- Open /etc/ssh/sshd_config with an editor using sudo.
$ sudo nano /etc/ssh/sshd_config
Editing /etc/ssh/sshd_config incorrectly can disrupt all SSH access; keeping a backup of the original file allows quick rollback.
- Configure the sftp subsystem to use internal-sftp.
Subsystem sftp internal-sftp
Ensure only one active Subsystem sftp line exists so sshd does not use an older external sftp-server binary.
- Add a Match Group block for sftpusers that enforces chroot with SFTP-only access.
Match Group sftpusers ChrootDirectory /srv/sftp/%u ForceCommand internal-sftp AllowTCPForwarding no X11Forwarding noThe %u token in ChrootDirectory expands to the login name so each account is jailed in its own directory under /srv/sftp.
- Validate the sshd configuration syntax before reloading the service.
$ sudo sshd -t
No output indicates that /etc/ssh/sshd_config parsed successfully.
- Restart the SSH service so the new SFTP-only rules take effect.
$ sudo systemctl restart ssh
On RHEL, CentOS, and similar systems, the service name is often sshd instead of ssh.
- Confirm that the SSH service is active after the restart.
$ sudo systemctl status ssh ● ssh.service - OpenBSD Secure Shell server Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2024-05-13 10:15:01 UTC; 5s ago ##### snipped ##### - Verify SFTP-only access for the restricted user from a client machine.
$ sftp sftpuser1@server.example.com sftpuser1@server.example.com's password: Connected to server.example.com. sftp> pwd Remote working directory: / sftp> ls upload sftp> cd upload sftp> put local-file.txt Uploading local-file.txt to /upload/local-file.txt local-file.txt 100% 123KB 1.2MB/s 00:00 sftp> exit
Interactive ssh login for the account is blocked by the nologin shell and ForceCommand internal-sftp, while SFTP sessions succeed.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
Comment anonymously. Login not required.
