By following this tutorial, you will be able to setup a Linux server with the following restrictions:

  • Users can connect to your server only via SFTP (SSH shell access is disabled).
  • Users can access only a specific file location on your server (Irrelevant directories are hidden using Chroot Jail).
  • Users have limited read-write file permissions (File permissions are strictly managed).
  • Users must have a private key to connect to your server (Password-based login is disabled).
  • Users can use only general SFTP commands (Interactive shell access is disabled).
  • Users cannot have elevated privileges (No sudo commands).

1. Create SFTP users

1.1 Create a common user group for SFTP only users.

$ sudo groupadd sftp_users

1.2 Create a new user (say pfops_user) with preferred values for the below flags.

$ sudo useradd -g sftp_users -m -d /home/pfops_user -c "Platform Ops User (SFTP Only)" -s /sbin/nologin pfops_user

If the user is already existing in the system, use usermod with the below flags.

$ sudo usermod -aG sftp_users -s /sbin/nologin pfops_user
  • -g : specify user’s initial login group
  • -m : with useradd, create user’s home directory if it does not exist; with usermod, move the current home content to new location
  • -d : specify user’s home directory
  • -s : specify user’s login shell
  • -c : specify meaningful comment
  • -aG: append user to another group (no harm to existing supplementary group memberships)
  • -G : specify user’s supplementary groups (existing supplementary group memberships will be lost)
  • -e : specify an expire date (format: YYYY-MM-DD) on which the user account will be disabled

1.3 Verify user creation.

$ id pfops_user
uid=1001(pfops_user) gid=1001(sftp_users) groups=1001(sftp_users)

2. Create SFTP locations

2.1 Create a separate root-level directory for storing SFTP data (Benefit: you can move content in this directory to a separate disk mount at any time. This is a best practice to compensate when servers run out of disk space in the future).

$ sudo mkdir /sftp_data/

NOTE: My suggestion is to create multiple directories specific to each SFTP user inside /sftp_data directory, so that you can manage them easily.

$ tree /sftp_data -d -L 2
├── pfops_user
│   ├── archive
│   ├── docker_images
│   ├── kubernetes_configs
│   └── releases
├── pfops2
│   ├── archive
│   ├── bash_scripts
│   ├── maven_dependencies
│   ├── releases
│── pfops3
│   ├── ...
│   └── .....

2.2 Create a SFTP location for the user.

$ sudo mkdir -p /sftp_data/pfops_user
  • -p : no error if existing, make parent directories as needed

2.3 Make sure that this location is owned by root.

$ sudo chown root:root /sftp_data/pfops_user

2.4 Create sub directories as you wish.

$ sudo mkdir -p /sftp_data/pfops_user/releases

NOTE: User can only read these content since the owner is set to root:root.

In case if you need to provide some write permissions (say user needs to upload or modify files in your server via SFTP), you need to create a separate directory and transfer its ownership to user.

$ sudo mkdir -p /sftp_data/pfops_user/uploads
$ sudo chown pfops_user /sftp_data/pfops_user/uploads

3. Setup Passwordless Login with SSH Keys

3.1 On client machine, generate RSA private and public key pairs.

$ ssh-keygen -t rsa -b 4096 -N '' -C "pfops_user@SFTP_HOST" -f id_rsa_pfops_user
$ mv id_rsa_pfops_user ~/.ssh/
$ chmod 644 ~/.ssh/id_rsa_pfops_user

3.2 On client machine, extract the public key that starts with ssh-rsa AAAA….

$ cat

3.3 On SFTP Server, append that extracted public key to /home/pfops_user/.ssh/authorized_keys file.

$ sudo vi /home/pfops_user/.ssh/authorized_keys
$ sudo cat /home/pfops_user/.ssh/authorized_keys
ssh-rsa AAAA....... pfops_user@SFTP_HOST

3.4 Modify permissions.

$ sudo chown root:root /home/pfops_user
$ sudo chown pfops_user:sftp_users /home/pfops_user/.ssh
$ sudo chown pfops_user:sftp_users /home/pfops_user/.ssh/authorized_keys
$ sudo chmod 700 /home/pfops_user/.ssh
$ sudo chmod 600 /home/pfops_user/.ssh/authorized_keys

4. Setup SFTP Server

4.1 Install OpenSSH Server.

// for RHEL/CentOS
$ sudo yum install -y openssh-server openssh-clients

// for Ubuntu/Debian
$ sudo apt update -y && sudo apt install openssh-server -y

4.2 Start sshd daemon.

$ systemctl start sshd
$ systemctl enable sshd
$ systemctl status sshd

4.3 Configure SFTP settings.

$ cp /etc/ssh/sshd_config ~/sshd_config_BACKUP
$ sudo vi /etc/ssh/sshd_config

// comment out current `Subsystem` setting and add internal-sftp to enable SFTP (see below)
#Subsystem      sftp    /usr/lib/openssh/sftp-server
Subsystem sftp internal-sftp

// append group/user specific settings at the end of file (this will override default settings)
// [option 1] to apply for the entire group
Match Group sftp_users
        X11Forwarding no
        AllowTcpForwarding no
        ChrootDirectory /sftp_data/%u
        ForceCommand internal-sftp
        PasswordAuthentication no
// [option 2] to apply only for the user
Match User pfops_user
        X11Forwarding no
        AllowTcpForwarding no
        ChrootDirectory /sftp_data/%u
        ForceCommand internal-sftp
        PasswordAuthentication no
  • X11Forwarding : specify whether to enable X11
  • AllowTcpForwarding : specify whether to allow TCP forwarding
  • ChrootDirectory : specify a custom location to chroot (change root level) after authentication. (%u: username, %h: user’s home directory)
  • ForceCommand : force to use commands supplied by a specific service, ignoring any command supplied by the client and ~/.ssh/rc if present
  • PasswordAuthentication : specify whether to allow password-based authentication (set no to disable it and enforce key-based authentication at all times)

4.4 Restart SSH service for changes to take place.

$ sudo systemctl restart ssh

5. Verify SFTP Access

Do these steps in your client machine.

5.1 Try SSH login. This must fail.

$ ssh -i ~/.ssh/id_rsa_pfops_user pfops_user@SFTP_HOST

5.2 Try SFTP login. This must be successful.

$ sftp -i ~/.ssh/id_rsa_pfops_user pfops_user@SFTP_HOST

5.3 If specifying private key every time seems troublesome, add private key location to local SSH configs, so that you don’t need to mention it again.

$ vi ~/.ssh/config

Host server SFTP_HOST
    Port 22
    User pfops_user
    HostName SFTP_HOST
    IdentityFile ~/.ssh/id_rsa_pfops_user
    LogLevel QUIET
  • LogLevel : set log levels only if you need to troubleshoot connection issues (possible values: DEBUG1, DEBUG2, DEBUG4)
$ sftp SFTP_HOST

Troubleshooting Guide

⚠️ If it fails to connect over SFTP, run the command with -vvv to output verbose logs.

$ sftp -vvv -i ~/.ssh/id_rsa_pfops_user pfops_user@SFTP_HOST

For permission issues

⚠️ Verify that chroot location is owned by root:root and permissions in user’s .ssh directory is correctly set.

For network issues

⚠️ If it’s a remote server, check whether its public IP is correctly set. If it’s in your corporate network, check the static IP assigned to it. If your server and client machines are located in different subnets, double-check whether there are strict rules avoiding connections between those subnets. Set firewall rules in your cloud servers (in general, cloud firewall rules must be changed using web control panel, not via terminal) and corporate networks throughout the entire network route from client to server.

⚠️ If you have OS firewalls enabled in your server, you may need to open ports to allow incoming traffic.

// for RHEL/CentOS
$ sudo systemctl start firewalld
$ sudo systemctl enable firewalld
$ sudo systemctl status firewalld
$ sudo firewall-cmd --zone=public --add-service=ssh --add-port=22/tcp --permanent
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-all-zones | grep ssh

// for Ubuntu/Debian
$ sudo ufw allow 22/tcp
$ sudo ufw disable
$ sudo ufw enable

⚠️ On client machine, run the below commands to run a port scan on your server.

$ nmap SFTP_HOST

⚠️ If you want to try pinging to your server from client, verify that an ICMP service is running on your server. Also you can even try curl more conveniently.

ping SFTP_HOST -vvv
curl SFTP_HOST:22 -vvv

✅ Tested OS's : RHEL 7+, CentOS 7+, Ubuntu 18.04+, Debian 8+
✅ Tested Gear : Cloud (AWS EC2), On-Prem (Bare Metal)

👉 Any questions? Please comment below.

Leave a comment