Forwarding ports, transferring files, and creating encrypted tunnels with OpenSSH
While OpenSSH is best known for providing secure remote login (ssh, slogin) and file transfer (scp, sftp) capabilities, it is capable of doing a lot more. In this post, we will discuss using the OpenSSH client to create proxies and forward ports (over encrypted tunnels).
For a deeper dive into functionality, explore the ssh man page.
Local Forwarded Ports
Imagine the following scenario: You’re at home and want to connect to a MySQL database at work. Let’s further assume that the network access is controlled through a bastion host running only SSH. Unfortunately, the database server is not exposed to the Internet. You’re in luck. You can connect to the bastion server over SSH, and then set up a locally forwarded port (which will be forwarded through the encrypted connection) to the database server.
For this example, let’s assume the bastion server has the IP address 1.2.3.4 and the database server has the address 10.0.1.1.
On your laptop you would execute the following command: ssh -N -f -T -C -L 3306:10.0.1.1:3306 user@1.2.3.4
Let’s break down the command a bit:
-N
specifies not to execute any commands, just allocate a channel-f
specifies that ssh should fork into the background after execution-T
specifies NOT to allocate a pseudo-tty (aka pts or pty depending on the OS). This has the interesting side effect of not adding a login record to wtmp or utmp, so you won’t show up in commands like “last
”, “lastlog
”, “w
”, “who
”, etc.-C
specifies to use zlib compression on the connection. This is useful when you expect to transfer larger chunks of data. This should only be used when you’re confident that the remote server has zlib support (basically every OpenSSH server will. Some others like ssh.com’s SSH, SunSSH or Dropbear might not).-L
sets up the locally forwarded port. The syntax here islocalport:remotehost:remoteport
. So this reads that port 3306 (on the localhost’s loopback interface) should be forwarded to 10.0.1.1 port 3306.
At this point if you run “mysql 127.0.0.1” on your laptop, the connection would be forwarded from your localhost to 10.0.1.1 through the encrypted connection to 1.2.3.4. Running something like “netstat -anp | grep 3306” will reveal that the listener is listening on the loopback interface (127.0.0.1) only.
If you wanted to allow any host on your LAN to connect to your laptop on port 3306, and have it be forwarded, we have to tweak the syntax slightly. First, we have to change the “-L 3306:10.0.1.1:3306
” to “-L 0.0.0.0:3306:10.0.1.1:3306
” and add the “-g” option to the ssh command. The “-g” option allows remote hosts to connect to local forwarded ports.
Dynamic Forwarded Ports
Locally forwarded ports are cool and all, but they only allow you to forward to a single static destination. Sometimes you might want a tunnel that can go to multiple places. OpenSSH supports this as well.
Imagine the following scenario: You’re on a public wifi and want to browse the Internet, but don’t want other people to be able to monitor your traffic. Let’s also assume we have an EC2 node with OpenSSH running at 1.2.3.4.
Execute the following command on your laptop: ssh -N -f -T -C -D 9999 user@1.2.3.4
Let’s break down this command. The “-N”, “-f”, “-T”
and “-C
” options were covered above. The only new option is the “-D 9999
” option. This option tells the ssh client to create a SOCKS5 proxy on the specified port (9999 in this example). Now if you go into your browser’s proxy settings and configure your browser to use a SOCKS5 proxy on 127.0.0.1:9999, all your web traffic will first go through the encrypted channel to 1.2.3.4, and from there go to the Internet.
If you want the SOCKS5 proxy to listen on a different interface, you can change the “-D 9999
” option to “-D 0.0.0.0:9999
” and add the “-g
” option to the ssh command. This is VERY dangerous as it allows anyone to potentially abuse your proxy server, and should only be done when you have a good reason to do so.
Remote Forwarded Posts
Not only can SSHforward ports out through a tunnel to a remote host, it can also configure the server to open a port and forward traffic back to the client. This could be useful for bypassing NAT. Say you want to show a friend a new website you’ve created, but it’s only available on your laptop (on port 8888), which is behind a NAT device you don’t control. Let’s also assume you have an EC2 server set up running OpenSSH with the IP address 1.2.3.4.
First, ssh into the EC2 server (“ssh user@1.2.3.4”) and execute something like the following: echo GatewayPorts clientspecified | sudo tee -a /etc/ssh/sshd_config; sudo service ssh restart
This command modifies the server’s sshd_config file to allow for GetwayPorts that allow any client to connect to a remotely forwarded port. The three options for it are “no” (the default; bind all ports to the loopback interface only), “yes” (bind to 0.0.0.0, or all listening interfaces), and “clientspecified”, which allows the client to specify to which interfaces the port should be bound. After making the configuration change, the service is restarted.
Now on your laptop, you can run: ssh -N -f -T -C -R 0.0.0.0:8888:127.0.0.1:8888 user@1.2.3.4
The only new option here is “-R 0.0.0.0:8888:127.0.0.1:8888
”. The syntax is “bind address:port:host:hostport
”. So the remote server (1.2.3.4) will listen on port 8888, and forward connections back to your laptop on port 8888 over the encrypted channel. Now when your friend connects to 1.2.3.4:8888, they will be able to see your website.
The “-R
” flag can also do other things, including creating SOCKS4/5 proxies. See the man page for more information.
Using Jump Hosts
Jump hosts are also common, and can be annoying to deal with. The old method of dealing with them was to SSH to the jump host, and then from there SSH into your target. More recent versions of the OpenSSH ssh client have an option to do this. To SSH using a jump host, you’d use a command like “ssh -J user@1.2.3.4 user@192.168.1.101
”. Multiple jump hosts can be specified by separating the addresses with commas, eg: “ssh -J user@1.2.3.4,user@4.5.6.7:2222,root@6.7.8.9 user@192.168.1.101
”. The addition of the “-J
” option is super handy.
Forwarding stdin and stdout
The OpenSSH client can also forward stdin and stdout over a secure channel. This allows you to use it like netcat. For example, the command “ssh -W apache.org:80 user@1.2.3.4
” would establish a secure connection to 1.2.3.4, and then forward stdin and stdout to apache.org:80, using the secure channel. From here you can type raw HTTP requests, like you would with netcat.
Creating an Ad-hoc VPN
OpenSSH also has the ability to forward tun devices. This allows you to create encrypted layer 3 tunnels, much like OpenVPN. In order to create one, we first need to reconfigure the server by executing the following commands:
sudo ip tuntap add mode tun
sudo ip link set tun0 up
sudo ip addr add 192.168.253.1/24 dev tun0
echo PermitTunnel yes | sudo tee -a /etc/ssh/sshd_config
sudo service ssh restart
sudo sysctl net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -s 192.168.253.0/24 -o eth0 -j MASQUERADE
This creates a new tun interface (tun0) on the server and assigns it the IP address 192.168.253.1/24. It then modifies the SSHD configuration to allow tunnels, and restarts the sshd. Finally, routing is enabled and iptables is configured to do NAT translation on the 192.168.253.0/24 network for clients.
Now we can configure our client machine in a similar manner:
sudo ip tuntap add mode tun
sudo ip link set tun0 up
sudo ip addr add 192.168.253.2/24 dev tun0
This gives our client the a new tun interface (tun0) with the IP address 182.168.253.2/24. Now we can establish the tunnel by running: “ssh -N -f -T -C -w 0:0 user@1.2.3.4
”. The “-N
”, “-f
”, “-T
” and “-C
” options should be familiar by now. The “-w 0:0
” option tells SSH to create a tunnel and bind it to local interface tun0 and forward it to remote interface tun0.
Now we just have to use the tunnel. Say we want to forward all traffic for host 4.5.6.7 through the tunnel. On the client, we’d execute the following: “sudo ip route add 4.5.6.7 dev tun0
”. After running this command, all traffic sent to 4.5.6.7 will automatically be proxied through the tunnel that was created to 1.2.3.4. Since this is a tun interface, all IP-based traffic can be forwarded over the tunnel (ICMP, TCP, UDP, etc).
SSH is a powerful multi-faceted tool capable of more than just secure remote logins and file transfers.