SSH tunnel through HTTPS

Many people at corporate jobs find themselves behind a firewall which only allows outgoing traffic to destination ports 21 (ftp), 80 (http) and 443 (https). To access one’s network at home, the workaround is to run the SSH server at home on port 443 instead of the usual port 22, then use the SSH client to create a tunnel so that arbitrary traffic will be sent through your home machine instead of through the firewall.

Some people who are even unluckier find themselves behind a firewall which does layer 7 packet inspection, meaning that traffic outgoing to a destination port of 443 that does not look like HTTPS traffic will be dropped by the firewall.

Fortunately, PuTTY combined with proxytunnel will allow the passing of non-HTTPS traffic through this type of firewall. It is accomplished through a “triple-proxy” method, where a connection is made to your HTTPS proxy web server at home through your restrictive corporate proxy, then an SSH session is tunneled through the HTTPS connection, and then the SSH connection acts as a proxy for the network traffic that is not permitted to pass through the corporate network. All traffic is encrypted and completely unidentifiable by packet inspection as anything other than a normal encrypted HTTPS session.

You must configure an Apache web server at home to listen with HTTPS on port 443, and also enable mod_proxy to accept HTTP proxy commands within the encrypted HTTPS session. (You must restrict the proxy commands to only certain username/password combinations, or alternatively restrict them to connections initiated from the external IP address of your corporate network. Failing to do this will allow any malicious user on the Internet to pretend that your server is doing the bad things that they are up to.)

This document explains how to do the complex configuration, and so does this other document, but there will be at least two problems.

One is due to an error in the document. The command for proxytunnel must be of the following form: c:\proxytunnel\proxytunnel.exe -X -q -p workproxy:3128 -r -d %host:%port -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)"\nspecial care must be taken to place the “\n” newline AFTER the quoted User-Agent, not within the quotes.

Two is due to the reluctance of Apache developers to accept a bug fix for the behavior of mod_proxy when an HTTPS connection is proxied. You will have to download the patch in the bug tracker, apply it to an Apache source tree, and compile your own Apache.

Once you have done both of these things, and configured both the proxy and SSH tunnel settings in PuTTY, then enter the INTERNAL name of a chosen machine on your home network you wish to make an SSH connection to. You will connect with a normal SSH session, but while the SSH session is active, PuTTY will also act as a SOCKS4/SOCKS5 proxy server for the benefit of arbitrary applications on your local machine.

Then simply configure your network applications to connect via your PuTTY proxy. The traffic will be passed through PuTTY, through your home web server, through the SSH connection to the chosen machine on your internal network, and finally to the destination on your home network or the broader Internet.

It is likely that you will experience frequent tunnel disconnection when the tunnel is idle. At the expense of potentially making your innocent HTTPS connection look suspicious to your network administrators, you can enable a sending of a null packet every 60 seconds to keep the connection alive.

Case study: Subversion repository over HTTP

In ~/.subversion/servers, proxy servers can be configured for Subversion on a per-host basis. For example, if you want all connections to go through your proxy, you could enter the following:

allhosts = *
http-proxy-host =
http-proxy-port = 8118

But Subversion uses many strange HTTP methods for its WebDAV protocol. Of course, several of these methods are not supported by the corporate proxy server, returning a “400 Bad Request” error when a commit is attempted.

Strangely, connecting to a HTTPS Subversion repository through the same proxy does not display this problem. It is easy to understand why. With a HTTPS connection, all HTTP commands are performed through the encrypted tunnel, so the proxy does not need to know about the individual HTTP methods. But with a plain old HTTP connection, the proxy must forward each HTTP command, and when it does not know about a particular method such as a method the Subversion client happens to use, it refuses to process it.

So what to do, aside from changing our HTTP Subversion server to serve the repository over HTTPS instead, which we may not be able to do? Aha, we can use our PuTTY SOCKS proxy to work around this defect of the corporate proxy. Subversion’s strange HTTP requests will be routed through our proxy instead. But the problem we quickly find is that while Subversion supports setting HTTP proxy servers in $HOME/.subversion/servers, it does not natively support SOCKS proxies. On the other hand, SOCKS proxies can be supported by the backend file transfer library libneon that Subversion employs, but this requires several steps.

First, ensure that libneon is built with SOCKS support, and that the required Dante SOCKS library is installed. This is not the default in many Linux distributions.

Then, configure /etc/dante.conf such that DNS requests and all traffic is routed through the local SOCKS proxy.

For example:

resolveprotocol: fake  # Sends DNS requests to the proxy server
route {
  # Send all TCP/UDP traffic from SOCKS-enabled applications
  # to the SOCKS5 server at
  from: to: via port=1080
  protocol: tcp udp
  proxyprotocol: socks_v5
  method: none

You can enable the debug and logoutput options to get a closer look at what is going on. But this is all it should take. Now that the Subversion HTTP traffic is tunneled through your SOCKS proxy, it is not subject to the inadequacies of the corporate proxy server. You should now be able to issue a command such as svn co

Note that this method may not work with svnserve repositories (svn://) since the network access semantics are different.

There is an even better way to do this. Knowing that HTTPS traffic can go through the proxy, and our tunnel is only needed for HTTP traffic, we can configure things such that traffic to known HTTPS Subversion servers is passed to the web proxy and the tunnel need only be active for traffic to known HTTP Subversion servers.

First, ensure that ~/.subversion/servers is set up like this:

https_hosts = *   # for example
http-proxy-host =    # IP address of your local HTTP proxy server
http-proxy-port = 8118     # port of your local HTTP proxy server

(Note that it is very important to use an IP address for the proxy server, and not a hostname. Remember, DNS queries must go through the proxy server. If you use a hostname, Subversion will attempt to use the SOCKS proxy to look up the host’s IP address, which defeats the point of having separate treatment for HTTPS and HTTP servers, since the SOCKS tunnel would have to be active for this to work.)

Then, set up /etc/dante.conf like so:

resolveprotocol: fake  # send DNS requests through proxy

route {
  # Direct route to local net's HTTP proxy server
  # This allows Subversion's internal HTTP proxy logic to work.
  from:  to:  via: direct

route {
  # Traffic to server at known to have HTTP-only Subversion must go
  # through SOCKS proxy on localhost.  IP address, subnet, OR hostname can
  # be used here, since it's not being looked up through DNS yet -- only being
  # matched against the Subversion repository that is currently being accessed.
  from:  to:  via:  port = 1080
  protocol: tcp udp
  proxyprotocol: socks_v5
  method: none   # or username

Presto, now Subversion traffic to the HTTPS servers defined in the Subversion configuration will go through your HTTP proxy, and traffic to other Subversion servers, whether HTTP or HTTPS, will go through the SOCKS proxy.

Leave a Reply