When deploying console applications through SSH, we often face a security paradox: users need shell access to launch the application, but we must prevent unauthorized file system access via SFTP. This becomes critical when your application manages its own data access controls.
SSH provides multiple channel types:
1. shell (for interactive sessions)
2. exec (for single commands)
3. subsystem (for SFTP/SCP)
4. direct-tcpip (for port forwarding)
The solution lies in selectively disabling the SFTP subsystem while maintaining shell access.
Method 1: SSH Server Configuration
Edit your /etc/ssh/sshd_config:
# Enable shell access
AllowTcpForwarding no
X11Forwarding no
PermitTTY yes
# Disable SFTP explicitly
Subsystem sftp /bin/false
# Or alternatively:
#Subsystem sftp internal-sftp -u 0007
Method 2: Force Command Execution
In sshd_config or user's authorized_keys:
Match User appuser
ForceCommand /path/to/your/console_app
PermitTTY yes
X11Forwarding no
AllowTcpForwarding no
For more granular control, consider a custom shell wrapper:
#!/bin/bash
if [[ $SSH_ORIGINAL_COMMAND ]]; then
# Handle direct command execution
exec /path/to/console_app --cmd "$SSH_ORIGINAL_COMMAND"
else
# Handle interactive session
/path/to/console_app --interactive
exit # Important: terminate session after app exits
fi
After configuration, test with:
# Test shell access
ssh user@host
# Test SFTP access (should fail)
sftp user@host
- Set proper umask (e.g., 0027) for your application
- Consider using chroot for additional isolation
- Implement proper logging of all access attempts
For complex scenarios, consider rssh or custom restricted shells:
# Install rssh on Debian/Ubuntu
sudo apt install rssh
# Configure in /etc/rssh.conf
allowscp
allowsftp
allowcvs
When building console applications with restricted access requirements, we often encounter a specific security paradox: Users need SSH shell access to launch the application (which immediately executes and terminates the session), but must be prevented from establishing SFTP connections that could expose restricted directories. This creates a legitimate need for SSH-SFTP separation that goes against typical security recommendations.
OpenSSH handles SFTP through its internal-sftp subsystem. The key realization is that shell access and SFTP are separate capabilities controlled by different configuration mechanisms:
# Standard SSH server components
sshd → shell (/bin/bash)
→ sftp (/usr/lib/openssh/sftp-server)
In your /etc/ssh/sshd_config, implement these changes:
# 1. Disable SFTP subsystem completely
Subsystem sftp /bin/false
# 2. Force command execution for specific users
Match User restricted_user
ForceCommand /path/to/your_app
PermitTTY no
X11Forwarding no
AllowTcpForwarding no
PermitTunnel no
GatewayPorts no
For finer control, implement chrooted environments:
# In sshd_config
Match Group appusers
ChrootDirectory /var/restricted/%u
ForceCommand /opt/app/launcher
AllowAgentForwarding no
PermitTunnel no
Then set up the directory structure:
sudo mkdir -p /var/restricted/username/dev/null sudo mknod /var/restricted/username/dev/null c 1 3 sudo chown root:root /var/restricted/username sudo chmod 755 /var/restricted/username
Create a restricted shell that immediately launches your application:
#!/bin/sh # /bin/restricted_shell /path/to/your_app exit 0
Then assign this shell in /etc/passwd:
appuser:x:1001:1001::/home/appuser:/bin/restricted_shell
Test with these commands:
# Verify shell access works ssh appuser@server # Verify SFTP is blocked sftp appuser@server # Should return "Connection closed" or similar
Additional hardening measures:
- Set
PasswordAuthentication noand use SSH keys exclusively - Implement fail2ban for brute force protection
- Regularly audit user sessions with
lastandjournalctl -u sshd
If users report connection problems:
- Check
/var/log/auth.logfor SSH errors - Verify permissions on chroot directories (must be owned by root)
- Confirm SELinux/AppArmor isn't blocking operations