A mini-HOWTO on cloaking an SSH connection in an HTTPS-like one: useful for connecting
via an HTTP proxy and for passing under network administrators' radars.
What you need: root access on a server (a *NIX home box on cable/DSL is fine),
Stunnel 3.x, its
ssl_method_sp patch, and optionally
Connect for passing through an HTTP proxy.
The participants:
- The client is the machine on which you run the ssh client, supposedly behind some kind of firewall or monitoring system. Here it is assumed to be OpenSSH on some kind of *NIX; root access is not needed. Instructions for a Windows client will follow sometime in the future, if I ever need to come up with them or if somebody asks.
- The TLS server is the server on which you have root access, as you need to start a server on port 443 (https); it should have a valid hostname: set up something like DynDNS if it doesn't.
- The target SSH server, which usually is the same host as the TLS server, but need not be.
The basic idea is to tunnel the SSH connection in a TLS one, much like HTTP is tunneled in TLS when accessing secure websites. TLS is a different protocol than SSH, so you need to:
- Configure your SSH client to use an additional piece of software (Stunnel) to tunnel the SSH connection in a TLS one, and possibly another one (Connect) to speak the language of HTTP proxies, if you need to pass through the local proxy.
- Configure a piece of software (again Stunnel) on your TLS server to answer on port 443, like any other 'secure website' would, then unwrap the TLS connection, un cover the SSH one within and forward it to the real SSH server, which is usually localhost.
Setting up the TLS server
Compile and install Stunnel 3.x on your TLS server. You can use Stunnel 4.x if you want, but being it easier to use Stunnel 3.x on the client part, I've sticked to it for the server part too. The main difference is that 3.x can be configured by commandline parameters, while 4.x needs a separate configuration file, which IMHO makes it awkward to put it into a ProxyCommand line on the client part.
After Stunnel is installed, you need to generate a server key. Answer "." (type a dot and then Enter) to every question except Common Name, for which you should enter your TLS server's full hostname:
openssl req -new -x509 -days 999999 -nodes -out sshd.pem -keyout sshd.pem
openssl gendh >> sshd.pem
Copy
sshd.pem to an appropriate location. Then put a line like the following in your startup scripts, so that it is executed by root:
stunnel -d 443 -r SSH-SERVER-HOSTNAME:22 -p /PATH/TO/sshd.pem \
-P none -N sshd -s nobody -g nogroup -O l:TCP_NODELAY=1 -O r:TCP_NODELAY=1
Only the options in the first line are strictly required. You can leave out
SSH-SERVER-HOSTNAME: if the target ssh server is the same as your TLS server.
-P none disables creation of a pidfile: adjust to your liking. The
-N option tells Stunnel to look into
/etc/hosts.{allow,deny} with the service name
sshd: this might be appropriate or even needed on some systems.
-s and
-g make Stunnel drop privileges to the specified user and group after grabbing the listening socket on port 443, which can only done as root. The
-O options set the TCP_NODELAY option on the TLS connection, which sometimes reduces latency, like OpenSSH does on its SSH connections.
If you want to debug the setup, start Stunnel with the
-f option, which leaves it on foreground and prints debug messages to standard error. With an Stunnel server listening on port 443, try to launch a client instance on another terminal:
stunnel -c -f -r 443
If everything works, you should see a couple of debug lines, followed by the target SSH server's banner line, something like:
SSH-2.0-OpenSSH_4.2p1 Debian-5.
Setting up the SSH client
An ordinary connection to a secure website uses TLS, while an Stunnel connecting to another Stunnel will agree on SSL2 or SSL3. This might not be stealth enough. One way to force the connection to TLS is to patch Stunnel 3.x with the ssl_method_sp patch linked above. This will augment it with the
-m option, accepting as arguments
ssl2,
ssl3, or
tls1. Patch Stunnel, compile it and install it somewhere in your path. Check that you have installed it correctly by typing
stunnel -h and looking for
-m.
Now you need to put the following in your
~/.ssh/config:
Host cloaked
Hostname TLS-SERVER-HOSTNAME
Port 443
ProxyCommand stunnel -c -f -m tls1 -r %h:%p
Compression yes
This configuration snippet tells ssh to use Stunnel for making the network connection, and then just assume a connection is made and send/receive to/from Stunnel's standard input/output. The "cloaked" label is just an alias: typing
ssh cloaked will trigger this whole machinery. The Hostname and Port lines aren't really needed: you could as well put hostname and port in place of %h and %p; but they make the configuration a bit more generic and readable. The Compression line is also not strictly needed, but it speeds up remote connections.
Stunnel doesn't know how to use an HTTP proxy, so if you need to do that, you will have to put Connect into the chain. Download it from the link above, compile it, install it in your path and modify your
~/.ssh/config as follows:
Host cloaked-proxy
Hostname TLS-SERVER-HOSTNAME
Port 443
ProxyCommand stunnel -c -f -m tls1 -l connect -- connect -h %h %p
Compression yes
# expects env HTTP_PROXY, possibly HTTP_PROXY_USER, HTTP_PROXY_PASSWORD
This configuration differs from the previous one for the ProxyCommand line. It makes Stunnel use Connect instead of making the network connection by itself; much like the whole ProxyCommand line makes SSH use Stunnel instead of connecting directly. The
-h option tells Connect to get the proxy details from environment variables, so that your
~/.ssh/config is as generic as possible. If instead you don't plan to use more than one proxy, you can put the details in the ProxyCommand line: see Connect's documentation (look inside
connect.c.) The invocation, with this configuration, will look like one of these:
HTTP_PROXY=proxy.company.com:8080 ssh cloaked-proxy
HTTP_PROXY=12.34.56.78:9999 ssh cloaked-proxy
HTTP_PROXY=proxy:80 HTTP_PROXY_USER=me HTTP_PROXY_PASSWORD=duh ssh cloaked-proxy
That's it. Now you can make stealth connections to your SSH server of choice, passing through the strictest proxies and firewalls, except those blocking secure websites altogether. You can redirect ports, surf the web through your own remote HTTP proxy (Tinyproxy is a good choice) and do lots of other interesting stuff. But they belong to other tutorials.
This article is ©2009 by the respective authors. Reproduction is prohibited without express permission from all contributors.