
SSH forwarding methods
By David WORMS
Nov 11, 2024
- Categories
- DevOps & SRE
- Tags
- Bash
- DevOps
Never miss our publications about Open Source, big data and distributed systems, low frequency of one email every two months.
For teaching purposes at Adaltas, we provide isolated container for our students. Students are provided with a common SSH connection and are redirected to a dédicated container running inside one of our servers accessible from the internet. For example, the gollum user connect with SSH to the middle.land.org public server and ends up inside the misty-mountains container.
There are multiple methods to forward the SSH connection from one host to another. But after researching for some instructions, we noticed that there were surprisingly few pages covering this subject.
Four different methods of SSH forwarding are presented to enable secure remote access through an intermediary server: direct SSH chaining, sshd configuration, Bash startup files, and SSH tunneling.
Network scheme
In the article, we will consider a network scheme where the client connects from a local host to a target host in a private network via a remote host exposed to the internet.
Below, we use the REMOTE_USER, REMOTE_HOST, TARGET_USER, and TARGET_HOST variables for the remote and the target user and host accordingly. It is also assumed, the SSH identity keys are correctly configured on all the hosts.
REMOTE_USER="<to_be_completed>"
REMOTE_HOST="<to_be_completed>"
TARGET_USER="<to_be_completed>"
TARGET_HOST="<to_be_completed>"Method 1 - Running SSH over SSH in a single command
The ssh command supports the optional command argument. When provided, the command is executed on the remote host instead of prompting a login shell. To run SSH over SSH, simply run the command like this:
ssh -t $REMOTE_USER@$REMOTE_HOST ssh $TARGET_USER@$TARGET_HOSTWhen an SSH connection to the remote host is established, it executes the next ssh command. The -t flag forces pseudo-tty allocation. In case you don’t specify it, you will be prompted with the warning Pseudo-terminal will not be allocated because stdin is not a terminal. and your terminal will not be working properly.
This command is equivalent to running ssh commands sequentially.
ssh $REMOTE_USER@$REMOTE_HOST
ssh $TARGET_USER@$TARGET_HOSTYou can even make a chain of multiple SSH forwardings. Every ssh command besides the last one must be called with the -t flag:
ssh \
-t $REMOTE_USER@$REMOTE_HOST ssh \
-t $TARGET_USER@$TARGET_HOST ssh \
$NEXT_TARGET_USER@$NEXT_TARGET_HOSTPros and cons
This is the most straightforward method to redirect the SSH connection. For the end-user, it requires knowledge of the internal network and providing it to the command.
Method 2 - Running SSH over SSH configuring sshd_config
sshd_config is sshd server system-wide configuration file. Usually, it is located in the /etc/ssh/ directory.
The ForceCommand keyword forces execution of a command in the argument. It is executed on the remote host after the SSH authentication. To apply this rule only to a specific user, you can use the Match keyword to introduce a conditional block with the User criteria.
On the remote host, enrich the /etc/ssh/sshd_config configuration or create a new file inside the /etc/ssh/sshd_config.d/ directory. Restart the sshd service to apply the configuration:
# Configuration
cat <<CONF | sudo tee /etc/ssh/sshd_config.d/forwarding.conf
Match User $REMOTE_USER
ForceCommand ssh $TARGET_USER@$TARGET_HOST
CONF
# Changes application
sudo systemctl restart sshdNow, to access to the target host from the local one, you can simply run the basic SSH command.
ssh $REMOTE_USER@$REMOTE_HOSTPros and cons
Using this method allows simplifying the command for the end-user to connect to the target host. However, it requires modifications of the global configuration file, which might be overwhelming when you configure such rules for multiple users and connecting them to different target hosts.
Method 3 - Running SSH over SSH configuring Bash startup files
When Bash is invoked as a login shell, it first reads and executes commands from the Bash startup files. The order of these files execution is described in the documentation manual.
The ~/.bash_login file is one of those Bash startup files. It can be used to proceed with an SSH connection to the target host, after SSH login to the remote host. You can create it and put the ssh command like:
ssh $TARGET_USER@$TARGET_HOSTTo access to the target host, like in the previous method, you only need to run on your local host the basic ssh command.
ssh $REMOTE_USER@$REMOTE_HOSTPros and cons
Comparing to others, this method is easier to use and manage. All the settings are made in the user’s home directories on the remote host. This involves less managing efforts when you provision and de-provision access for multiple users.
It also gives automation capabilities. For any provisioning purpose, you can extend the script in a Bash startup file. For example, you can create a Docker container on the fly and then enter inside it. The example bash script in ~/.bash_login can look like this:
# Export container name
export CONTAINER=$USER-container
if [ ! "$(docker ps -aqf name=$CONTAINER)" ]
then
# Run container if not exists
docker run -it --name $CONTAINER IMAGE
else
# Start container if not running
docker start -i $CONTAINER
fi
# Exit SSH session on container exit
exitHere, we are constructing a container name basing on the name of the user exported by $USER. Then, we are checking if a container with the given name already exists. In case of not existing, we are creating, starting, and entering inside it using the -it options to allocate pseudo-TTY and to attach STDIN. Otherwise, we are just starting it by attaching STDIN with the -i option.
Finally, when the end-user connects to the remote host via SSH, he is forwarded to an isolated virtual environment running in a Docker container.
Method 4 - SSH local port forwarding (also known as SSH tunneling)
SSH local port forwarding (also referred to as SSH tunneling) allows you to forward a port on the local host to a port on the remote host, which is then forwarded to a port on the target host.
To create a local port forwarding, pass the -L option to the ssh command:
ssh -L [$LOCAL_HOST:]$LOCAL_PORT:$TARGET_HOST:$TARGET_PORT $REMOTE_USER@$REMOTE_HOSTIn addition to TARGET_HOST, REMOTE_USER, REMOTE_HOST introduced above, the options used are as follows:
[LOCAL_HOST:]LOCAL_PORT: the local IP address and the port number. WhenLOCAL_HOSTis omitted, the defaultlocalhostvalue is used. Any port number greater than1024asLOCAL_PORTcan be used. Ports numbers less than1024are privileged ports and can be used only byroot.TARGET_PORT: the port of the target host, by default is22.
Additionally, it is useful to add the following options to the ssh command:
-N: not to execute a remote command when connecting toREMOTE_HOSTwhich is a login shell.-f: tells thesshcommand to run in the background to don’t lock the terminal window.
The complete command will look like this:
ssh -N -f -L \
[$LOCAL_HOST:]$LOCAL_PORT:$TARGET_HOST:$TARGET_PORT \
$REMOTE_USER@$REMOTE_HOSTAfter the SSH local port forwarding is created, you can access the target host running ssh with the -p option with the LOCAL_PORT specified above:
ssh $TARGET_USER@localhost -p $LOCAL_PORTPros and cons
This method is way more verbose and difficult to use. The end-user must run non-trivial commands and keep the additional process of SSH tunnel running on his local host.
Conclusion
This article presents 4 different methods to forward SSH connection. Some of them are convenient to use as ad hoc commands, others are suitable for automation.