When configuring OpenSSH for secure file transfers, we often face a dilemma: how to allow SCP operations while maintaining strict security through chroot jails and preventing interactive shell access. The standard internal-sftp
solution works perfectly for SFTP but leaves SCP functionality in the cold.
SCP's fundamental architecture requires shell access because:
# This is how SCP works under the hood
client -> sshd -> /usr/bin/scp -> user's shell
Unlike SFTP which can use the direct internal-sftp
subsystem, SCP needs to execute the scp
binary through the user's login shell.
Here's a proven configuration that maintains security while allowing SCP:
Match group sftponly
ChrootDirectory /sftp/%u
X11Forwarding no
AllowTcpForwarding no
ForceCommand /usr/bin/rbash
PermitEmptyPasswords no
AllowAgentForwarding no
Create a minimal environment that supports both SCP and SFTP:
# Create the chroot directory
mkdir -p /sftp/username/{bin,lib64,usr/lib}
# Copy required binaries
cp /bin/bash /sftp/username/bin/
cp /usr/bin/scp /sftp/username/bin/
cp /usr/lib/openssh/sftp-server /sftp/username/usr/lib/
# Copy required libraries
ldd /bin/bash | awk '/=>/ {print $3}' | xargs -I {} cp {} /sftp/username/lib64/
ldd /usr/bin/scp | awk '/=>/ {print $3}' | xargs -I {} cp {} /sftp/username/lib64/
Configure a restricted shell that only allows SCP:
# Create restricted shell symlink
ln -s /bin/bash /sftp/username/bin/rbash
# Create .bash_profile
echo 'scp "$@"' > /sftp/username/.bash_profile
echo 'exit 0' >> /sftp/username/.bash_profile
chmod 555 /sftp/username/.bash_profile
Verify both protocols work as expected:
# Test SFTP
sftp -P 22 username@yourserver.com
# Test SCP
scp -P 22 testfile.txt username@yourserver.com:/upload/
This approach maintains security by:
- Keeping the chroot environment minimal
- Preventing shell escape through rbash
- Maintaining file ownership and permissions
- Logging all access attempts
When configuring OpenSSH for secure file transfers, many administrators face a dilemma: how to support both SFTP and SCP protocols while maintaining security through chroot jails. The standard internal-sftp
solution works perfectly for SFTP but breaks SCP functionality.
The fundamental issue lies in how OpenSSH handles different protocols:
# Current configuration that breaks SCP
Match group sftponly
ChrootDirectory /sftp/%u
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp
SCP requires shell access to spawn the scp
process, which conflicts with ForceCommand
. Unlike SFTP which has a dedicated internal subsystem, SCP relies on the user's shell environment.
While there's no direct equivalent to internal-sftp
for SCP, we can implement a secure solution using OpenSSH's built-in capabilities:
Match group sftponly
ChrootDirectory /sftp/%u
X11Forwarding no
AllowTcpForwarding no
ForceCommand /usr/lib/openssh/sftp-server
AuthorizedKeysCommand /usr/local/bin/validate-scp
AuthorizedKeysCommandUser nobody
Create /usr/local/bin/validate-scp
:
#!/bin/bash
# Allow SCP commands only when specific patterns are detected
if [[ "$SSH_ORIGINAL_COMMAND" =~ ^scp\ [-fr]\ .*$ ]]; then
exec /usr/lib/openssh/sftp-server
else
echo "Command not allowed"
exit 1
fi
Make it executable:
chmod +x /usr/local/bin/validate-scp
For systems where AuthorizedKeysCommand isn't desirable:
Match group sftponly
ChrootDirectory /sftp/%u
X11Forwarding no
AllowTcpForwarding no
ForceCommand /usr/local/bin/scp-wrapper
Then create /usr/local/bin/scp-wrapper
:
#!/bin/bash
case "$SSH_ORIGINAL_COMMAND" in
scp*)
/usr/lib/openssh/sftp-server
;;
*)
echo "This account is restricted to SCP/SFTP only."
exit 1
;;
esac
When implementing this solution:
- Ensure all scripts are owned by root and not writable by others
- Regularly audit your chroot environment
- Consider SELinux/AppArmor policies for additional protection
- Test thoroughly with various SCP clients
Test your setup with:
ssh -vT user@host
scp -v file user@host:
sftp user@host
Check logs for any unexpected behavior:
tail -f /var/log/auth.log