Prerequisites

  • Rocky Linux installed with SSH server (openssh-server).
  • Root or sudo access.
  • The target folder exists (e.g., /var/www/mobile_proxy/tmp/test_folder/).

Step 1: Create the SFTP User

Create a dedicated user for SFTP access. Do not allow shell login for security.

What is "Create user without shell"?
This means creating a user account that cannot log in to a full shell (command line) on the server. By setting the shell to /sbin/nologin, the user can only use SFTP for file transfers, not execute commands or access the system interactively. This enhances security by limiting the user's capabilities.

# Create user without shell
sudo useradd -m -s /sbin/nologin ftpuser

# Set password
sudo passwd ftpuser

Step 2: Configure SSH for SFTP Chroot

Edit /etc/ssh/sshd_config to enable SFTP chroot for the user.

What is SFTP Chroot and why is it needed?
SFTP Chroot (Change Root) is a security feature that restricts the user to a specific directory (jail) upon login, preventing access to the rest of the filesystem. It's needed for security: without chroot, the user could navigate to sensitive areas like /etc or /home. For SFTP, it ensures the user can only upload/download files in their designated folder, reducing risks from compromised credentials.

# Backup config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

# Edit config
sudo nano /etc/ssh/sshd_config

Add or modify the following lines:

# Allow only specific users (add ftpuser)
# AllowUsers: Restricts SSH login to only listed users for security. Without it, any user with a password can connect.
# Replace 'another_user' with your admin username; 'ftpuser' is the SFTP user.
AllowUsers another_user ftpuser

# SFTP subsystem
# Subsystem: Defines how SFTP is handled. 'internal-sftp' uses SSH's built-in SFTP server (secure, no external dependencies).
# Alternative: External sftp-server, but internal is preferred for chroot.
Subsystem sftp internal-sftp

# Chroot for ftpuser
# Match User: Applies the following rules only to the specified user (ftpuser).
# ChrootDirectory: Sets the chroot jail root to /var/www/mobile_proxy/tmp/ (user sees this as /).
# ForceCommand: Forces the user to use only SFTP, no shell access (security measure).
# AllowTcpForwarding no: Disables TCP tunneling (prevents port forwarding exploits).
# X11Forwarding no: Disables X11 GUI forwarding (not needed for SFTP, reduces attack surface).
Match User ftpuser
    ChrootDirectory /var/www/mobile_proxy/tmp/
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no

Restart SSH:

sudo systemctl restart sshd
sudo systemctl status sshd

Step 3: Set Permissions for Chroot Directory

For chroot to work, all parent directories must be owned by root with restricted permissions (755). Only the final folder can be owned by the user.

What is Chroot Directory and why only the final folder can be owned by the user?
The ChrootDirectory is the root directory that the user sees after login—everything outside it is hidden. SSH requires all parent directories (up to the chroot point) to be owned by root and have permissions 755 (readable/executable by all, writable only by owner) for security reasons. If any parent is owned by another user or has looser permissions, SSH rejects the chroot to prevent privilege escalation. Only the final folder (where the user works) can be owned by the user, allowing them to read/write files there.

# Check current permissions
namei -l /var/www/mobile_proxy/tmp/test_folder/

# Fix ownership and permissions
sudo chown root:root /var/www/
sudo chmod 755 /var/www/

sudo chown root:root /var/www/mobile_proxy/
sudo chmod 755 /var/www/mobile_proxy/

sudo chown root:root /var/www/mobile_proxy/tmp/
sudo chmod 755 /var/www/mobile_proxy/tmp/

# Final folder for user
sudo chown ftpuser:ftpuser /var/www/mobile_proxy/tmp/test_folder/
sudo chmod 755 /var/www/mobile_proxy/tmp/test_folder/

Step 4: Test SFTP Connection

Use an SFTP client (e.g., FileZilla, WinSCP) or command line:

# From another machine
sftp ftpuser@your-server-ip
# Enter password
# You should be chrooted to /var/www/mobile_proxy/tmp/ and see only test_folder/

Diagnostics for Typical Problems

1. Authentication Denied ("Access denied")

  • Cause: User not in AllowUsers or password incorrect.
  • Check:
    bash sudo grep -i "AllowUsers" /etc/ssh/sshd_config
  • Fix: Add user to AllowUsers and restart SSH.
    bash sudo nano /etc/ssh/sshd_config # Add: AllowUsers youradmin ftpuser sudo systemctl restart sshd

2. Chroot Directory Ownership/Modes Error

  • Cause: Parent directories not owned by root or permissions too open.
  • Check:
    bash sudo journalctl -u sshd -f # Look for "bad ownership or modes" namei -l /var/www/mobile_proxy/tmp/test_folder/
  • Fix: Ensure all parents are root:root 755, as in Step 3.

3. Connection Fails or Times Out

  • Cause: Firewall, SSH not listening, or config syntax error.
  • Check:
    bash sudo systemctl status sshd sudo netstat -tlnp | grep :22 sudo sshd -t # Test config syntax
  • Fix: Open port 22 in firewall (sudo firewall-cmd --permanent --add-service=ssh; sudo firewall-cmd --reload).

4. User Can Access Outside Chroot

  • Cause: Incorrect ChrootDirectory or permissions.
  • Check:
    bash sudo grep -A5 "Match User ftpuser" /etc/ssh/sshd_config
  • Fix: Verify ChrootDirectory points to the parent of the user's folder.

5. Permission Denied on File Operations

  • Cause: Final folder permissions or SELinux.
  • Check:
    bash ls -la /var/www/mobile_proxy/tmp/test_folder/ sudo getenforce # Check SELinux
  • Fix: Adjust permissions or disable SELinux if needed (sudo setenforce 0 for testing).

6. Logs for Debugging

  • Cause: General troubleshooting.
  • Check:
    bash sudo journalctl -u sshd -f
  • Fix: Review logs for errors and apply fixes based on messages.