In modern network security architectures, the jump host (or bastion host) pattern is widely adopted for controlled access to internal resources. The typical flow is:
Internet → Bastion → Internal Network
In this specific implementation, we're enforcing strict user-to-machine mapping:
- User1 → Machine1
- User2 → Machine2
- ... (and so on)
The core configuration in /etc/ssh/sshd_config
on the bastion host uses Match blocks with ForceCommand:
Match User User1
ForceCommand ssh User1@Machine1 $SSH_ORIGINAL_COMMAND
Let's examine the security aspects of this approach:
Access Control
The configuration effectively creates a "pass-through" authentication where:
- Users cannot get an interactive shell on the bastion
- All commands are immediately forwarded to the target machine
- The $SSH_ORIGINAL_COMMAND preserves the original command intent
Potential Weaknesses
While generally secure, consider these edge cases:
# File transfer scenarios:
scp -P 22 user1@bastion:/tmp/testfile .
# This would actually attempt to transfer from Machine1
# but might confuse auditing systems
For production environments, consider adding these security measures:
Match User User1
ForceCommand ssh -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/dev/null \
-o LogLevel=VERBOSE -i /etc/ssh/jump_host_keys/user1 user1@Machine1 $SSH_ORIGINAL_COMMAND
PermitTTY no
X11Forwarding no
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
Other methods to consider:
Method | Pros | Cons |
---|---|---|
ForceCommand | Simple, no client changes | Limited control over destination |
ProxyCommand | More flexible routing | Requires client config |
SSH Certificates | Fine-grained access control | Complex PKI setup |
Ensure proper logging is configured on both bastion and target machines:
# In sshd_config on bastion:
Match User User1
ForceCommand logger -p authpriv.info "SSH Forward: %u to Machine1"; \
ssh user1@Machine1 $SSH_ORIGINAL_COMMAND
- Use separate SSH key pairs for bastion and target machine access
- Implement command restrictions on target machines using authorized_keys options
- Regularly audit ForceCommand rules and user assignments
- Consider pairing with TCP wrappers for additional filtering
The typical jump host (bastion host) setup involves three logical segments:
Internet → Bastion → Internal Network
In this scenario, we're implementing granular access control where each external user gets tunneled to exactly one internal machine. The critical security requirement is preventing any direct access to the bastion itself.
The configuration shown in the original question achieves this through OpenSSH's Match blocks:
Match User User1
ForceCommand ssh -t User1@Machine1 $SSH_ORIGINAL_COMMAND
Match User User2
ForceCommand ssh -t User2@Machine2 $SSH_ORIGINAL_COMMAND
Key security aspects:
- The
-t
flag forces pseudo-terminal allocation for interactive sessions $SSH_ORIGINAL_COMMAND
preserves the original command structure- No shell access is granted on the bastion
To test the effectiveness of this setup:
# Attempt to execute local commands on bastion
ssh User1@bastion 'ls /tmp'
# Result: Command gets forwarded to Machine1 instead
# Actual output shows Machine1's /tmp contents
For enhanced security, consider these additions:
Match User *
# Prevent any shell access
PermitTTY no
X11Forwarding no
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
Match User User1
ForceCommand ssh -o StrictHostKeyChecking=yes -i /path/to/restricted_key User1@Machine1 $SSH_ORIGINAL_COMMAND
AuthenticationMethods publickey
PubkeyAuthentication yes
PasswordAuthentication no
While generally secure, be aware of:
- Environment variable leakage (mitigate with
PermitUserEnvironment no
) - SSH protocol weaknesses (enforce modern protocols with
Protocol 2
) - Command injection through
$SSH_ORIGINAL_COMMAND
(validate commands if possible)
For more control, replace ForceCommand with a restricted shell script:
Match User User1
ForceCommand /usr/local/bin/restricted_ssh_wrapper.sh
# Content of restricted_ssh_wrapper.sh:
#!/bin/bash
# Validate command patterns
case "$SSH_ORIGINAL_COMMAND" in
"scp"*|"rsync"*|"git-"*)
ssh -q -i /path/to/key User1@Machine1 "$SSH_ORIGINAL_COMMAND"
;;
*)
echo "Command not allowed"
exit 1
;;
esac
Essential logging configuration for the bastion host:
# In /etc/ssh/sshd_config
SyslogFacility AUTH
LogLevel VERBOSE
# Example log entry pattern:
# Accepted publickey for User1 from 1.2.3.4 port 12345 ssh2: forced-command "ssh User1@Machine1"