When implementing chrooted SFTP environments, we often encounter a frustrating limitation: the chroot directory itself (typically /home/user
) must be owned by root with strict permissions (755), while users need a subdirectory for actual file operations. This creates the common workflow where users must cd
into a subfolder before uploading files.
The SSH daemon enforces these security constraints:
# Required permissions for chroot directory
drwxr-xr-x 5 root root 4096 May 4 11:24 /home/sftpuser
The root ownership prevents users from writing directly to the chroot root, hence the "Permission denied" error when attempting:
sftp> put testfile.txt
Uploading testfile.txt to /testfile.txt
remote open("/testfile.txt"): Permission denied
We can achieve direct root uploads while maintaining security through these steps:
- Create a writable directory elsewhere:
- Bind mount it to appear in chroot:
- Make the mount persistent (in /etc/fstab):
mkdir -p /srv/sftp/upload
chown sftpuser:sftpuser /srv/sftp/upload
mount --bind /srv/sftp/upload /home/sftpuser/upload
/srv/sftp/upload /home/sftpuser/upload none bind 0 0
For systems where bind mounts aren't feasible:
# Create a writable directory
mkdir -p /home/sftpuser/.writable-root
chown sftpuser:sftpuser /home/sftpuser/.writable-root
# Create symlink that appears as root directory
ln -s /home/sftpuser/.writable-root /home/sftpuser/upload_area
Enhance your sshd_config
with these parameters:
Match User sftpuser
ChrootDirectory /home/sftpuser
ForceCommand internal-sftp -u 0002
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
After implementation, test with:
sftp sftpuser@yourserver.com
sftp> put testfile.txt upload_area/
Uploading testfile.txt to /upload_area/testfile.txt
testfile.txt
- Keep the chroot directory owned by root
- Regularly audit mount points and permissions
- Consider using filesystem quotas
- Monitor for symlink attacks in shared environments
When setting up an SFTP server with chroot, we encounter a fundamental security constraint: The chroot directory (/home/sftpuser in this case) must be owned by root with strict permissions (typically 755). This creates a write-permission paradox - users can't write to their own chroot root directory despite it being their virtual filesystem root.
The existing setup shows all the proper security measures:
# /etc/ssh/sshd_config snippet
Match User sftpuser
ChrootDirectory /home/sftpuser
ForceCommand internal-sftp
Directory permissions:
/home/sftpuser - owned by root:root (drwxr-xr-x)
/home/sftpuser/sftp_share - owned by sftpuser:sftpuser (drwxrw-r-x)
Solution 1: Symlink Magic
Create a symlink from the chroot directory to the writable subdirectory:
sudo ln -s /home/sftpuser/sftp_share /home/sftpuser/uploads
Then modify authorized_keys:
command="cd /uploads && $SSH_ORIGINAL_COMMAND" ssh-rsa AAAAB3Nza...
Solution 2: Bind Mount Approach
Mount the writable directory at chroot level:
sudo mkdir /home/sftpuser/writable
sudo chown sftpuser:sftpuser /home/sftpuser/writable
sudo mount --bind /home/sftpuser/sftp_share /home/sftpuser/writable
Add to /etc/fstab for persistence:
/home/sftpuser/sftp_share /home/sftpuser/writable none bind 0 0
Solution 3: Alternative Directory Structure
Reorganize the entire hierarchy:
sudo mkdir -p /sftp_chroot/sftpuser_root
sudo chown root:root /sftp_chroot
sudo chmod 755 /sftp_chroot
sudo chown sftpuser:sftpuser /sftp_chroot/sftpuser_root
Update sshd_config:
Match User sftpuser
ChrootDirectory /sftp_chroot
Now the user's effective root is writable.
While Solution 3 provides the cleanest implementation, consider:
- Ensure no world-writable permissions exist
- Regularly audit directory ownership
- Implement disk quotas if needed
- Consider using filesystem ACLs for finer control
After implementing any solution, verify with:
sftp sftpuser@yourserver.com
sftp> put testfile.txt
sftp> ls -l
For automated testing, use this expect script:
#!/usr/bin/expect -f
spawn sftp -oPort=22 sftpuser@yourserver.com
expect "sftp>"
send "put testfile.txt\r"
expect "sftp>"
send "ls -l\r"
expect "sftp>"
send "quit\r"