How to restrict SSH users to SFTP only

SFTP-only accounts are useful when a partner, automation job, or internal team needs to exchange files but should not receive a shell on the SSH server. The restriction has to block interactive SSH sessions while still allowing the SFTP subsystem to read and write the intended upload directory.

OpenSSH handles this with a Match block in /etc/ssh/sshd_config. A matched group can be forced into internal-sftp, placed in a per-user chroot under /srv/sftp, and denied forwarding features that would otherwise keep extra SSH channels available.

The chroot boundary is strict about ownership. The directory named by ChrootDirectory and every parent in that path must be owned by root and not writable by group or others, while a separate child directory inside the jail can be owned by the SFTP user for uploads. Keep an existing admin session open while changing sshd so a bad edit can be rolled back from the current connection or a console.

Steps to restrict SSH users to SFTP only:

  1. Back up the current sshd configuration.
    $ sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

    Do not close your current admin session until the restricted account has been tested. A syntax error or wrong Match rule can block new SSH logins.

  2. Create a dedicated group for SFTP-only accounts.
    $ sudo groupadd --system sftpusers

    Using a separate group keeps the SFTP-only policy separate from normal SSH users.

  3. Create the restricted account in that group.
    $ sudo useradd --no-create-home --home-dir / --shell /usr/sbin/nologin --gid sftpusers sftpuser1

    The home directory is set to / because the account will be chrooted to /srv/sftp/sftpuser1 before internal-sftp starts.

  4. Set an authentication method for the restricted account.
    $ sudo passwd sftpuser1
    New password:
    Retype new password:
    passwd: password updated successfully

    If the server uses key-only authentication, install the user's public key through the same approved process used for other SSH accounts before testing SFTP.

  5. Create the chroot and upload directory path.
    $ sudo mkdir -p /srv/sftp/sftpuser1/upload
  6. Set root ownership on the chroot directories.
    $ sudo chown root:root /srv/sftp /srv/sftp/sftpuser1
  7. Keep the chroot directories writable only by root.
    $ sudo chmod 755 /srv/sftp /srv/sftp/sftpuser1

    sshd refuses the login if /srv/sftp or /srv/sftp/sftpuser1 is writable by sftpuser1 or by sftpusers. Put writable content below the chroot directory, not on the chroot directory itself.

  8. Set the upload directory ownership to the restricted user.
    $ sudo chown sftpuser1:sftpusers /srv/sftp/sftpuser1/upload
  9. Allow only the restricted user and group to enter the upload directory.
    $ sudo chmod 750 /srv/sftp/sftpuser1/upload
  10. Verify the chroot and upload directory ownership.
    $ sudo ls -ld /srv/sftp /srv/sftp/sftpuser1 /srv/sftp/sftpuser1/upload
    drwxr-xr-x 3 root      root      4096 Jun 13 11:48 /srv/sftp
    drwxr-xr-x 3 root      root      4096 Jun 13 11:48 /srv/sftp/sftpuser1
    drwxr-x--- 2 sftpuser1 sftpusers 4096 Jun 13 11:48 /srv/sftp/sftpuser1/upload
  11. Open /etc/ssh/sshd_config with sudo privileges.
    $ sudoedit /etc/ssh/sshd_config
  12. Set the SFTP subsystem to internal-sftp.
    Subsystem sftp internal-sftp

    Keep only one active Subsystem sftp line. Comment out an older line that points to an external sftp-server binary.

  13. Add the Match Group block after the global sshd settings.
    Match Group sftpusers
        ChrootDirectory /srv/sftp/%u
        ForceCommand internal-sftp
        DisableForwarding yes
        PermitTTY no

    The %u token expands to the login name, so sftpuser1 is jailed under /srv/sftp/sftpuser1. DisableForwarding blocks forwarding channels, and PermitTTY no prevents terminal allocation for matched users.

  14. Test the sshd configuration before applying it.
    $ sudo sshd -t

    No output means the configuration parsed successfully.

  15. Restart the SSH service to apply the SFTP-only rule.
    $ sudo systemctl restart ssh

    On RHEL, CentOS, Fedora, and similar systems, the service name is commonly sshd instead of ssh.

  16. Connect with SFTP and upload a test file into the writable directory.
    $ sftp sftpuser1@host.example.net
    sftp> pwd
    Remote working directory: /
    sftp> ls
    upload
    sftp> cd upload
    sftp> put local-file.txt local-file.txt
    Uploading local-file.txt to /upload/local-file.txt
    sftp> ls
    local-file.txt
    sftp> rm local-file.txt
    sftp> bye

    The restricted account starts at the chroot root and can write only inside /upload.

  17. Confirm the same account cannot open an SSH shell.
    $ ssh -T sftpuser1@host.example.net
    This service allows sftp connections only.

    The nonzero SSH exit and internal-sftp message confirm that the matched account is forced into SFTP instead of a shell or remote command session.