How to do a SSH connection through a Web Proxy

When a company is allowing their employees to access the internet it is often restricted to a web proxyserver (e.g. Squid). This means there is no connection to the internet behind a firewall, but only http access through that proxy.

But what can you do if you need an SSH login on an external machine? The solution is a so called proxytunnel via SSH. Many proxyservers will only allow connections with port 80 (http) and port 443 (https), so additionally to running proxytunnel on your client machine you will need an SSH server responding on one of those two ports. I chose port 443, because I wanted to keep port 80 for other tasks (see below, under Possible Extensions). Configuring SSH for port 443 on a NetBSD system works like this:


1. You need a separate directory for the port 443 SSH server's keys and configuration. Create a directory ssh443 in /etc/ssh, then copy the original sshd_config to it and modify it:

# cd /etc/ssh
# mkdir ssh443
# cp sshd_config ssh443/
# vi ssh43/sshd_config

Change the default settings for Port from 22 to 443 and for PidFile from /var/run/sshd.pid to /var/run/sshd443.pid.

--- sshd_config 2006-10-17 00:02:32.000000000 +0200
+++ ssh443/sshd_config  2008-07-04 14:32:51.000000000 +0200
@@ -10,6 +10,7 @@
 # default value.
  
 #Port 22
+Port 443
 Protocol 2
 #ListenAddress 0.0.0.0
 #ListenAddress ::
@@ -85,6 +86,7 @@
 #ClientAliveCountMax 3
 #UseDNS yes
 #PidFile /var/run/sshd.pid
+PidFile /var/run/sshd443.pid
 #MaxStartups 10
  
 # no default banner path

The ssh_config and ssh_known_hosts files may be used by both instances, so we just use hard links. The keys will be generated when the server starts up for the first time.

# cd ssh443
# ln ../ssh_config ssh_config
# ln ../ssh_known_hosts ssh_known_hosts

2. In parallel to /etc/rc.d/sshd a start script for the new sshd443 is required. We copy the sshd script and change it to refer to /etc/ssh/ssh443/. Although you should better change the current version of your script, an example for NetBSD 3.1 can be found here.

--- /etc/rc.d/sshd      2004-08-13 20:08:03.000000000 +0200
+++ /etc/rc.d/sshd443   2008-07-04 14:41:57.000000000 +0200
@@ -1,58 +1,54 @@
-# PROVIDE: sshd
+# PROVIDE: sshd443
 # REQUIRE: LOGIN
  
 $_rc_subr_loaded . /etc/rc.subr
  
-name="sshd"
-rcvar=$name
-command="/usr/sbin/${name}"
-pidfile="/var/run/${name}.pid"
-required_files="/etc/ssh/sshd_config"
+name="sshd443"
+rcvar="sshd443"
+command="/usr/sbin/sshd"
+pidfile="/var/run/sshd443.pid"
+required_files="/etc/ssh/ssh443/sshd_config"
 extra_commands="keygen reload"
  
 sshd_keygen()
 {
        (
        umask 022
-       if [ -f /etc/ssh/ssh_host_key ]; then
+       if [ -f /etc/ssh/ssh443/ssh_host_key ]; then
                echo "You already have an RSA host key" \
-                   "in /etc/ssh/ssh_host_key"
+                   "in /etc/ssh/ssh443/ssh_host_key"
                echo "Skipping protocol version 1 RSA Key Generation"
        else
                /usr/bin/ssh-keygen -t rsa1 ${ssh_keygen_flags} \
-                   -f /etc/ssh/ssh_host_key -N ''
+                   -f /etc/ssh/ssh443/ssh_host_key -N ''
        fi
  
-       if [ -f /etc/ssh/ssh_host_dsa_key ]; then
+       if [ -f /etc/ssh/ssh443/ssh_host_dsa_key ]; then
                echo "You already have a DSA host key" \
-                   "in /etc/ssh/ssh_host_dsa_key"
+                   "in /etc/ssh/ssh443/ssh_host_dsa_key"
                echo "Skipping protocol version 2 DSA Key Generation"
        else
                /usr/bin/ssh-keygen -t dsa ${ssh_keygen_flags} \
-                   -f /etc/ssh/ssh_host_dsa_key -N ''
+                   -f /etc/ssh/ssh443/ssh_host_dsa_key -N ''
        fi
  
-       if [ -f /etc/ssh/ssh_host_rsa_key ]; then
+       if [ -f /etc/ssh/ssh443/ssh_host_rsa_key ]; then
                echo "You already have a RSA host key" \
-                   "in /etc/ssh/ssh_host_rsa_key"
+                   "in /etc/ssh/ssh443/ssh_host_rsa_key"
                echo "Skipping protocol version 2 RSA Key Generation"
        else
                /usr/bin/ssh-keygen -t rsa ${ssh_keygen_flags} \
-                   -f /etc/ssh/ssh_host_rsa_key -N ''
+                   -f /etc/ssh/ssh443/ssh_host_rsa_key -N ''
        fi
        )
 }
  
 sshd_precmd()
 {
-       if [ ! -f /etc/ssh/ssh_host_key -o \
-           ! -f /etc/ssh/ssh_host_dsa_key -o \
-           ! -f /etc/ssh/ssh_host_rsa_key ]; then
+       if [ ! -f /etc/ssh/ssh443/ssh_host_key -o \
+           ! -f /etc/ssh/ssh443/ssh_host_dsa_key -o \
+           ! -f /etc/ssh/ssh443/ssh_host_rsa_key ]; then
                run_rc_command keygen
        fi
 }

3. Make sure the port 443 instance of sshd starts up when your server is booted. Enable the start script in /etc/rc.conf:

[...]
sshd=YES
sshd443=YES
sshd443_flags="-f /etc/ssh/ssh443/sshd_config"
[...]

Then start the new SSH server by calling /etc/rc.d/sshd443 start or reboot the system. That's it. Now back to the client side.


On your client system (no matter if NetBSD, FreeBSD or Linux) you need a program called proxytunnel, which is called by SSH internally to tunnel over your https proxyserver. Download the source from http://proxytunnel.sourceforge.net, compile and install the program.

Let's assume the SSH server we want to connect to is called home.dyndns.org. To be able to connect to it, you have to extend your ~/.ssh/config with the following block:

Host home
        Hostname home.dyndns.org
        Port 443
        Compression yes
        KeepAlive yes
        ForwardAgent yes
        ProxyCommand /usr/local/bin/proxytunnel -q -p 192.168.0.99:3128 -d %h:%p -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n"
        IdentityFile ~/.ssh/id_frank

Explanation: When typing ssh home your SSH client will connect to home.dyndns.org at port 443 running the proxytunnel program. Among the proxytunnel options -p specifies the host and port of your local proxyserver and -d defines the destination host and port to build the tunnel to. %h and %p are placeholders which are replaced by SSH with home.dyndns.org and 443. Another interesting option is -H which makes us spoof as a common Mozilla/Win32 browser, in case somebody checks the proxyserver's logs. This example is for a proxyserver without authentication. If your proxyserver requires to authenticate yourself you have to add the option -P user:pass to specify your user name and password. For security reasons you may consider to use the -F option instead and save your credentials in a file nobody else can read.

The IdentityFile is optional and saves you from having to enter your password everytime. At this point you should be able to run a SSH shell on your home server, which finally gives you full internet access.


Possible Extensions

Now since we have a SSH connection we have the possibility to tunnel any host-port combination we need on the local machine. Yes, that would be another tunnel through the proxy-tunnel! Those SSH tunnels have the advantage that they are secure. Nobody can read what is transmited through such a tunnel, because SSH connections are always encrypted. For example you want to fetch private email by POP3 protocol from mail.gmx.net. Just log in into your home server while building a tunnel to the POP3 server at port 110:

$ ssh -L8110:mail.gmx.net:110 home

This enables you to connect to mail.gmx.net:110 via localhost:8110 (of course you can use 110 in both ends, as long as it is unused on your local machine). Enter localhost:8110 as a mail server in your favourite mail client and fetch mail!


Surfing the web was also possible before, but perhaps you feel uncomfortable with the thought that the proxyserver's admins have the possibility to log and check all the sites you are visiting. Unfortunately a single tunnel will not help you here, because you would need a new tunnel for every single web server host. The solution is to build a proxyserver yourself, running on your home server.

We will use the Apache web server and configure it to work as a proxy. Install the server and locate the configuration file, called httpd.conf. At least two modifications should be done. At first replace Port 80 in the beginning of the file with Port 8181 (or anything else different from 80 and not allocated by another server), because we want our proxyserver to respond to port 80. To enable the proxy module, add the following section:

<IfModule mod_proxy.c>
    ProxyRequests On
    Listen 80
    <Directory proxy:*>
        Order deny,allow
        Deny from all
        Allow from 127.0.0.1
    </Directory>
    ProxyVia On
    CacheRoot "/var/proxy"
    CacheSize 1024
    CacheGcInterval 1
    CacheMaxExpire 12
</IfModule>

Create the directory /var/proxy, owned by the httpd (Apache). It will be used as a cache. The proxy listens on port 80 and allows connections from localhost only. This is ok, because when building a tunnel with our SSH login, it will be localhost. And it denies any connections from the outside world, which must not use our private proxy. Make sure that Apache is running (make a start script for it, etc.).

When back at the office open a SSH tunnel to your home proxyserver through a proxytunnel on the local proxyserver. For example:

$ ssh -L8080:localhost:80 home

Then set localhost:8080 in your browser's config as the current proxyserver to use, instead of the usual one. Now you are surfing via your private proxy. All data is encrypted and your company's admins will never know what you're doing. A tunnel in a tunnel which is encrpyted, and your home server has to receive and transmit all the data, sounds slow. And indeed it is slower than using the official proxy, but even with a 512kBit upstream at your home server it is enough for using Google Maps, for example, which is sufficient for me. The higher security and privacy is worth it.


Frank Wille, March 2009