SSH tunneling is a technique to transfer arbitrary networking data using an encrypted SSH connection. It can be used to add encryption to not secure protocols such as HTTP and to bypassing firewalls. It is also named as port forwarding, there is a post related to C# implementation of SSH port forwarding.
Here is presented an example how to get access via SSH to blocked HTTP port plus transporting HTTP data in encrypted way. We have 2 machines A and B. The machine B has running HTTP and SSH servers. The machine A is Linux box which can connect to machine B via SSH but cannot sends HTTP requests to machine B because port 80 is blocked by firewall.
HTTP requests from A maching to B one are rejected:
# curl -I http://10.19.69.2 curl: (7) couldn’t connect to host |
Let us create tunnel on machine A to transfer HTTP data to machine A via SSH protocol:
# ssh -f -L 8888:localhost:80 root@10.19.69.2 -N root@10.19.69.2’s password: xxxxx |
Now machine A has listening 8888 ports for IPv4 and IPv6 loopback addresses: 127.0.0.1 and ::1:
# netstat -a -n -p | grep 8888 tcp 0 0 127.0.0.1:8888 0.0.0.0:* LISTEN 15981/ssh tcp 0 0 ::1:8888 :::* LISTEN 15981/ssh |
and this listening port was opened by process with PID=15981:
# ps -ef | grep 15981 root 15981 1 0 08:38 ? 00:00:00 ssh -f -L 8888:localhost:80 root@10.19.69.2 -N |
Now let us send HTTP HEAD request to local port 8888:
# curl -I http://127.0.0.1:8888 HTTP/1.1 200 OK Date: Tue, 28 Jan 2020 14:57:26 GMT Server: Apache/2.2.15 (CentOS) Last-Modified: Mon, 27 Jan 2020 16:49:16 GMT ETag: “160096-2c-59d21e3517f9e” Accept-Ranges: bytes Content-Length: 44 Connection: close Content-Type: text/html; charset=UTF-8 |
and it works.
This request also visible in Apache log file on machine B. However sshd server which receives request from machine A redirects in to IPv6 loopback address instead of IPv4 one:
# tail -n 1 /var/log/httpd/access_log ::1 – – [28/Jan/2020:10:07:26 -0500] “HEAD / HTTP/1.1” 200 – “-” “curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2” |
Let us send HTTP GET request to local port 8888:
# curl http://127.0.0.1:8888 <html> <body> <h1>Test</h1> </body> </html> |
It also works.
Form /var/log/httpd/access_log on B machine:
# tail -n 1 /var/log/httpd/access_log ::1 – – [28/Jan/2020:10:09:02 -0500] “GET / HTTP/1.1” 200 44 “-” “curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2” |
Now repeating the same for IPv6 loopback:
# curl -I -g -6 “http://[::1]:8888” HTTP/1.1 200 OK Date: Tue, 28 Jan 2020 16:09:23 GMT Server: Apache/2.2.15 (CentOS) Last-Modified: Mon, 27 Jan 2020 16:49:16 GMT ETag: “160096-2c-59d21e3517f9e” Accept-Ranges: bytes Content-Length: 44 Connection: close Content-Type: text/html; charset=UTF-8 |
curl -g -6 “http://[::1]:8888” <html> <body> <h1>Test</h1> </body> </html> |
Now lat us shut down the tunnel. From previous ps command output we now that ssh process with opened the tunnel has PID=15981. Let us kill this process and check 8888 port status:
# kill 15981 # netstat -a -n -p | grep 8888 # |
There is no listening 8888 port so HTTP requests to 127.0.0.1:8888 does not work anymore.
# curl -I http://127.0.0.1:8888 curl: (7) couldn’t connect to host |
If HTTP server was configured for virtual hosting and runs several name-based web sites for the same IP address, the Host header with site name should be added to the HTTP request.