How to use SSH tunnels to cross network boundaries
The Secure Shell protocol authenticates and encrypts network connections. Find out how it's used to build tunnels while crossing private networks and even firewalls.
The Secure Shell protocol defines a mechanism for securely connecting to a remote host. Originally, SSH was intended to replace Telnet, the original internet application for running remote login sessions. But SSH has become much more than a tool for terminal emulation, in large part because it provides the means to securely route network traffic through ports and host systems defined by the user. These connections are called tunnels because they provide a connection through which secure network data can be transmitted over an unsecured network.
An SSH tunnel is a secure connection between an SSH client and an SSH server. Network traffic from the local machine is routed from an arbitrary specified port on the localhost through the SSH connection to a specified port on the remote machine.
For the most basic use case, SSH is used to initiate a terminal session with a remote SSH server. Once the connection is made, the user can issue remote commands on that server. When the secure connection uses remote port forwarding, however, the SSH tunnel provides a means to tunnel through private networks and the firewalls that protect them.
This tutorial shows how to use SSH tunnels for three specific purposes:
- Access a restricted internet resource from inside a firewall using SSH port forwarding.
- Set up a SOCKS proxy server for the local machine.
- Set up an SSH reverse tunnel to bypass firewall protection to access an SSH server inside a private network.
SSH is an important tool used by networking and information security professionals -- as well as by hackers and other attackers. Before experimenting with SSH on an organization's network, be sure to check in with the IT department to determine whether SSH tunneling is permitted -- and to make sure that the experiments do not trigger cybersecurity alarms.
Preparing to set up an SSH tunnel
This tutorial focuses on using the OpenSSH implementation of the SSH protocol at the command-line interface (CLI). Although GUI implementations of SSH, such as PuTTY for Windows, exist, modern OSes almost universally include CLI implementations of SSH, including OpenSSH on Windows versions 10 and later, macOS and Linux.
Before attempting to open an SSH tunnel, take the following steps:
- Verify that SSH is installed and running on the local machine, as well as on the remote SSH server. On Windows systems or Unix-based systems, this can be done by opening a PowerShell window (Windows) or a shell session (Unix-based systems) and entering the ssh Without any parameters, the command returns a message indicating correct usage and available parameters.
Also, verify that the SSH server sshd is running on whichever host is going to be used for SSH access. If the host does not have a daemon (server program) for the SSH server running and listening for requests to open an SSH connection, it needs to be started before a tunnel can be created. One way to verify this is to initiate an SSH connection with the server in question. If the connection is accepted, then it is safe to assume that sshd is running.
This is also a good time to verify that the user account being used on the SSH servers has the correct permissions. At a minimum, the user account should be authorized to issue superuser commands -- using the sudo command -- but with limitations to prevent the account from being hijacked and used against the organization.
- Get or verify the hostname or the IP address of the remote server. The server IP address is always sufficient, although having the domain name can make entering SSH commands easier.
On Windows systems, the IP address of the system can be determined through the Settings application, under the Network & Internet option, by viewing the Properties of the network interface that is currently connected to the internet.
Commands for discovering the IP address using the command line are available for all OSes:- On Windows systems at the command line, the IP address of the system can be determined by entering the PowerShell command ipconfig that displays the network configuration, including IP address, for all network interfaces on the system.
- On Linux systems, such as Unix-based OSes, which includes macOS, the IP address can be determined by entering the Unix command ifconfig that displays the network configuration, including IP address, for all network interfaces on the system.
- Determine the port numbers to be used for redirection with SSH port forwarding. For ports assigned for use with specific protocols, also known as well-known ports, refer to the Internet Assigned Numbers Authority (IANA) Service Name and Transport Protocol Port Number Registry. The most commonly used IANA-registered ports include port 80 (HTTP), 443 (HTTPS) and 22, which is the default port for using SSH.
In general, any non-well-known port, in the range from 1024 to 65535, can be used -- but port 1080 is the registered port for SOCKS and should be used when setting up a dynamic port forwarding tunnel to be used as a SOCKS proxy.
When setting up a tunnel for local port forwarding, for example, from a web server on the internal network to an external server, the source port and destination port could both be set to 80 for HTTP transmissions. When setting up a tunnel on a local server that is running both as an SSH server and a web server, the inbound port could be set to 8080 to differentiate tunnel traffic from ordinary web traffic.
- Review the planned tunnel before implementation, and make sure all hosts involved are reachable from relevant systems. Things to check before deploying the tunnel include the following:
- Are the hosts reachable?
- Do all hosts have up-to-date and interoperable versions of SSH installed?
- Do hosts acting as SSH servers have the SSH server software installed?
- Does the user have adequate permissions on relevant systems?
Once tunnel endpoints are defined, taking a step-by step approach can help simplify troubleshooting since each component of the tunnel can be tested as it is turned on.
- Initialize the SSH daemon on any system acting as a server. This may require a separate installation on some OSes. For Windows -- version 10 and later -- a separate OpenSSH server executable must be installed and initiated by the operator using a PowerShell command line running with Administrator permissions. The following command can be used to initialize an installed OpenSSH server daemon from the PowerShell command line:
PS C:\Users\peter> Start-Service sshd
The following command can be used on Ubuntu systems to start the OpenSSH server:
$ sudo service ssh start
The sudo command requires entering an authorized password to gain superuser access to run the server service.
Once connectivity is established to all endpoint systems and permissions for access to SSH programs are confirmed, setting up the tunnel is usually straightforward. Some SSH implementations may have different options available, however. To determine which version of SSH is installed on Windows, open a PowerShell window, and enter the following command:
PS C:\Users\userID> ssh -V
To verify the version of SSH running on Linux, enter the following command:
$ sudo service ssh start
How to set up a SOCKS proxy server
One of the most practical applications of SSH tunneling, a SOCKS proxy is a tunnel that enables a host inside a private network, protected by a firewall, to freely access network resources on the global public internet. SOCKS proxies offer a straightforward way to protect network communications without a VPN.
When used with SSH, SOCKS enables routing all network traffic from the local system through the secure SSH tunnel. This type of tunnel is sometimes called dynamic port forwarding because it relies on SOCKS' ability to direct network traffic from any port on the local system to any port on any remote system.
Use the following command to initiate a SOCKS proxy over an SSH tunnel on a Linux system using the SSH server ssh-server.example.com and user account peter:
$ ssh -D 1080 [email protected]
The -D option -- for dynamic application port forwarding -- enables SSH port forwarding for all ports, in and out. In this example, the -D option identifies port 1080 as the port on the local host to be used to listen for tunnel traffic. It also enables SSH to manage assignment of application ports, so as connections are established through the tunnel, SSH operates as a SOCKS proxy server. Any network application being tunneled through the SOCKS proxy must be configured to communicate on this port, rather than on the well-known ports used for the application.
SOCKS proxy configuration in web browsers is well documented and included in the browser configuration settings, as shown in this screenshot. Other applications may require changes to a config file to direct the application program to access the proxy. Alternatively, SOCKS proxies can be configured at the OS level -- for example, in Windows Settings -- so all network applications use the proxy.
The localhost serves as the proxy: It accepts network traffic to be tunneled at the specified port -- 1080 for SOCKS -- and the SOCKS host as localhost.
The command listed above can be refined. For example, the following command uses the same port, user account and remote host but adds options:
$ ssh -D 1080 -C -N [email protected]
The -C option enables compression for better performance on a slow network -- although, on fast networks, this option may reduce performance.
The -N option specifies that remote commands should not be run from the session. This enables a local operator to initiate port forwarding without enabling any commands to be run on the remote server.
Once the dynamic port forwarding tunnel is opened and the local computer is configured to use a SOCKS proxy, tunneled network traffic is forwarded to the public internet through the tunnel.
How to set up an SSH port forwarding tunnel
Local port forwarding enables the local operator to bypass any firewalls or filters to access an external server from within a private network. With local port forwarding, the local machine does not have direct internet access, and the SSH server, which is directly reachable by the local system, does have internet access.
The connection between the local system and the SSH server is secured with authentication upon creating the SSH session, and officially designated SSH servers used for port forwarding are usually deployed with additional security measures for a hardened server configuration.
The SSH circuit connects the SSH process running on the local computer with a remote process. The remote process is the server or resource on the external host, outside the protected network. The SSH client software listens locally for attempts to access the port associated with the remote resource. When the SSH client software receives data on that port, it forwards it through the encrypted and authenticated circuit to the SSH server. The SSH server then forwards that traffic to the intended destination.
The command to create a local port forwarding tunnel uses the following syntax:
$ ssh -L local_port:remote_destination:remote_port user@ssh_server
The -L option is used to bind a port on the local machine with a remote port at the remote destination IP address. The port is bound through the connection to the user account at the ssh_server.
This diagram shows a host on the private network sending network traffic to the local port 1020. This is where the SSH client software is listening for outgoing traffic. The SSH client then encapsulates that traffic to be forwarded through the existing SSH connection with the jump server.
The jump server is an SSH server that has been designated for "jumping" from within the private network to the internet. Jump servers are usually hardened and are usually configured to require an SSH key file for authenticating each user.
This tunnel can be created for the user account peter on the SSH server named ssh_server.example.com with the following command:
$ ssh -L 1020:www.example.net:80 peter@ssh_server.example.com
The jump server forwards network traffic from the SSH client to the remote resource being requested. Replies from the remote resources reverse the traffic through the internet to the jump server, which then forwards the replies to the local operator.
How to set up an SSH reverse tunnel
Reverse tunneling, also known as remote port forwarding, opens a tunnel through which an operator with access to an SSH server can also access a host inside a private network. Reverse tunnels are often used to bypass firewalls to gain access to network resources inside private networks -- for example, by attackers and penetration testers.
The command to create a local port forwarding tunnel uses the following syntax:
$ ssh -R remote_port:localhost:local_port user@ssh_server
The -R option is used to bind a port on the remote machine with a port on the localhost. The port is bound through the connection to the user account at the ssh_server.
A reverse tunnel can be created with the user account peter on the SSH server named ssh_server.example.com with the following command:
$ ssh -R 8080:localhost:1020 peter@ssh_server.example.com
In this tunnel, the SSH server ssh_server.example.com forwards any network traffic it receives on port 8080 to the localhost process listening for port 1020. This means anyone with access to the SSH server can connect to the local machine.