Home | Linux |     Share This Page
Secure Shell Fun and Games

— All content Copyright © 2007, P. LutusMessage Page

Introduction | Teaser | Quick Setup
Applying SSH | Secure E-mail | SSH tunnel on demand using xinetd
Firewall Tunneling | Conclusion

(double-click any word to see its definition)

Introduction

This article explains how to securely communicate between computers on a network, including the big network — the Internet. The described methods assume something that is increasingly true among rational people — it requires Linux as written. Some parts can be made to work on Windows, but only by way of the bizarre (and increasingly common) step of grafting parts of Linux onto Windows, to try to reduce the innate dreadfulness of the latter.

A brief digression. The reason Windows doesn't already contain all the required parts of this very secure communication protocol is because Microsoft doesn't own it. In the Linux world, merit derives from effectiveness, while at Microsoft, merit derives from ownership. It really is as simple as that — if something useful comes along and Microsoft notices, they will react in one of these ways:

  • They will try to buy it.
  • They will claim it violates their software patents.
  • They will disparage it, describe it as useless and beneath them.
  • All the above at once.

But, digressions aside, Windows is so difficult to work with that, for reasons of efficiency and brevity, I will focus on how to make these methods work with Linux servers and clients, and let Windows users try to apply it to Windows on their own.

Teaser

At the time of writing (Fall 2007) the Secure Shell (hereafter SSH) protocol has evolved to the point where it stands as an obvious replacement for a whole raft of prior communications technologies, including but not limited to:

  • FTP
  • NFS
  • RSH
  • Rlogin
  • Telnet
  • SMTP
  • POP3
  • Virtual private networks

Once it has been set up with a reasonable amount of care, SSH is very secure indeed — its packets are encrypted end-to-end, using advanced encryption methods, in a way that is transparent to the user. Also, for particular communication pathways chosen by the user, one can eliminate the requirement to enter passwords without compromising security.

Okay, that should whet your appetite. Let's get started.

Quick Setup

For this part of the tutorial, let's assume we have two machines running Linux, a local machine and a remote machine. The remote can be another machine on a local network, or an Internet server, basically any machine at any location. Even better, the remote machine can be secured behind a firewall that has only one open port, the SSH service port (22), and communications can be established with any machine behind the firewall as though it is on the local network. All this can be done with a very high degree of security.

Here is a step-by-step procedure to establish basic SSH functionality between two machines:

  • Goal 1. Basic Installation

    • Make sure both machines have SSH installed (this may already be true). On each participating machine, and depending on your Linux distribution and your personal preferences, as root run:

      • "yum -y install openssh openssh-server" or
      • "apt-get -y install openssh openssh-server"

      One may also use the RPM package manager using downloaded packages or packages present on an installation DVD.

    • Set up encryption keys (if encryption keys are already present in the ~/.ssh directory, don't perform this step). On each participating machine, as the user of interest, execute this command locally:

      • No password entry: ssh-keygen -q -t dsa -N "" -f ~/.ssh/id_dsa
      • Password entry: ssh-keygen -q -t dsa -f ~/.ssh/id_dsa

      Use the password entry form above if the machine might be compromised and the SSH directory made readable. You will be prompted for a password — enter a password and make a note of it (it will be required to read your private key).

      This step will create two files in the user's SSH directory (normally located at ~/.ssh), named "id_dsa" and "id_dsa.pub". These two files are the cornerstone of a very secure encryption method named "public-key cryptography".

    • Test communications. As the same user that generated the above keys, from the local machine execute this command:

      ssh (remote machine name or IP)

      This will launch the SSH client, which will establish contact with the remote machine. You will be asked for a password. Once the relevant password (the user's login password) has been entered, a shell session with the remote machine will begin.

      The SSH shell session is highly secure, it has every advantage over Telnet as just one example, but for more advanced kinds of automation we need to eliminate the password requirement.

  • Goal 2. Passwordless Setup

    The remote machine maintains a list of machines that may access it without entering a password. Depending on the version of SSH in use, the list is named ~/.ssh/authorized_keys or ~/.ssh/authorized_keys2. Not to be too informal about this, but you can either discover which file is required by the remote machine or you can create two files. I will leave this up to the reader. In this example let's assume that the remote file is named "authorized_keys".

    Perform the following steps on the local machine, either at the keyboard or as part of a shell script:

    • key=$(cat ~/.ssh/id_dsa.pub)
    • ssh (remote machine name or IP) "echo $key >> ~/.ssh/authorized_keys"

    During the second step above, you will be prompted for a password, but if the procedure is successful, subsequent SSH operations will not require a password. NOTE: If you are still asked for a password, repeat the above steps using the remote file name "authorized_keys2".

    For a peer-to-peer arrangement, in which each machine might be either server or client, perform the above steps on each machine, so that both machines have a file named "authorized_keys" that contains the public keys for the other machines in the network. In essence this creates a virtual private peer-to-peer network.

    NOTE: While creating encryption keys, in particular if you inadvertently overwrite prior keys or re-install Linux on one of the machines, subsequent connections will fail and you will see a warning like this:

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    @    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
    Someone could be eavesdropping on you right now (man-in-the-middle attack)!
    It is also possible that the DSA host key has just been changed.
    The fingerprint for the DSA key sent by the remote host is
    48:45:0b:e7:0b:bb:70:ac:8e:94:93:7f:33:44:10:c6.
    Please contact your system administrator.
    Add correct host key in /home/user/.ssh/known_hosts to get rid of this message.
    Offending key in /home/user/.ssh/known_hosts:17
    DSA host key has changed and you have requested strict checking.
                    

    The remedy is given in the warning, but just to be clear about it, one fixes this problem by editing the local ~/.ssh/known_hosts file and removing the line that identifies the remote machine. On the first connection after this step, you will be asked to confirm that you want to connect to the remote machine and add its identifier to the local list of known hosts.

  • Goal 3. Test

    Before moving on, let's test the connection and make sure it is seamless, with no password prompts or other anomalies:

    • ssh (remote machine name or IP) (did the connection complete without any requests or warnings?)
    • scp local-file-name (remote machine name or IP):remote-file-name (was the copy successful?)

If all three goals in this section have been met, we can move on to some useful SSH applications.

Applying SSH

SSH is a very useful, very secure way to communicate between machines. Over a period of years I have abandoned one protocol after another in favor of SSH, in particular while communicating with remote servers. Here are some easy examples:

  • Assuming both machines are running X Windows and KDE, this command will open a local GUI incarnation of Konqueror (as a file manager) running remotely:
    ssh -X (remote machine name or IP) konqueror --profile=filemanagement
  • Again assuming both machines are running X Windows and KDE, this command will open a local GUI incarnation of Firefox running remotely:
    ssh -X (remote machine name or IP) firefox (url)

A somewhat more complex application:

  • Use SSH and "rsync" to efficiently back up changed local files to a similar directory tree on a remote machine:
    rsync -a (local path) remote-machine-name:(remote path)
  • Use SSH and "rsync" to efficiently back up changed local files to a similar directory tree on a remote machine, plus delete from the remote machine any files no longer present on the local machine (dangerous unless performed carefully):
    rsync -a --delete (local path) remote-machine-name:(remote path)

An advanced example of something called "port tunneling":

  • Seamlessly forward a local port to a remote machine (disconnects after one port connection):
    ssh remote-machine-name -L local-port:localhost:remote-port -N
  • Seamlessly forward a local port to a remote machine (persists over many port connections):
    ssh remote-machine-name -L local-port:localhost:remote-port "sleep 86400"

An example of the latter port tunneling technique would be:

ssh remote-server -L 7777:localhost:smtp -N

Having set up the above tunnel, one can test it this way:

telnet localhost 7777

This will connect to the remote machine's SMTP daemon, which will have no way to know the connection is not local.

The reader might wonder what the big deal is — why not just connect to the remote machine directly, like we did in the old days, like this:

telnet remote-machine-name smtp

The answer is that e-mail has changed dramatically in the past few years. It has come under attack from many sides, and the days are numbered for the old-style, informal, unencrypted access methods presently in use. The next section discusses this issue in depth.

Secure E-mail

I was tempted to call this section "Somewhat more secure e-mail," but I decided not to overdo the qualifiers. First, for perspective, I offer this narrative.

I've been writing about spam issues for quite a few years (see "The Anti-Spam Home Page"), during which time the problem has gotten much worse. It is increasingly difficult to write an article so dark and pessimistic that it manages to live up to subsequent events.

I travel a lot, and I have a server (arachnoid.com) through which I route all my e-mail. Because the SMTP server is set up in a reasonably secure way, it won't accept outgoing e-mail from any source — it requires either a prior validated POP3 contact, or an entry in /etc/mail/access that allows relaying from a particular IP.

Both these validation approaches have drawbacks — the first requires me to perform e-mail transactions in a particular order, the second requires me to edit a server configuration file any time I move from one place to another (and change my IP by so doing).

This last summer, I got the brilliant idea that I would run "sendmail" locally — I would configure local sendmail as my SMTP server, and sendmail would sort out how to route the e-mails. I tested the idea by successfully posting a message to myself via my online server, thus confirming my brainless illusion that this setup would actually work.

After the successful test, and now armed with a breathtaking level of confidence arising from near-perfect stupidity, I traveled all over Alaska, posting e-mails from every wireless hotspot I could find. Unbeknownst to me, about 80% of the emails were being rejected by their destinations because my arachnoid.com reply address didn't match the source server (localhost). Sendmail would silently queue the rejected e-mails, silently try again later, and eventually (silently) give up.

About halfway through the Alaska expedition I realized something was amiss. I opened and read a neglected log file and discovered my stupid mistake, which required me to recreate and repost dozens of orphaned e-mails. I decided that, as cumbersome and insecure as it was, I would have to go back to the prior method and add all the wireless hotspot IPs to the server's relay list. This was a profoundly discouraging moment.

Since then I have been researching ways to avoid this problem, but without coming up with anything practical. But last week, a new crisis arose — all my e-mails began to be rejected en masse, by way of a blacklist maintained by Spamhaus and accessed by many SMTP servers across the country.

The story is that my access provider (cablespeed.com) now hosts so many compromised Windows machines (thus becoming cells in a "botnet" — read more about this here) that Spamhaus decided to simply blacklist all of them. This isn't as draconian as it may sound — all end users need to do is set up SMTP transactions to be authorized by way of a username and password (technically, SMTP_AUTH), a beneficial change over past practice. All the e-mail client programs know how to do this, so it's a simple configuration change and everything is back to normal. And, the present topic aside, everyone should do this anyway if possible, on general principles. It has the effect of increasing the security of the entire e-mail system.

Unfortunately for me, even though I could set up Thunderbird to honor SMTP_AUTH, the SMTP server on my Web hosting service wasn't configured to honor the protocol, and I couldn't change this. So I was in a pickle — I could arrange to get my specific cablespeed.com IP unblocked by way of Spamhaus, but this is a stopgap measure because my IP is dynamically assigned. Next week, I might have to do it all again.

So I began the project that led to this article. Although I have been using SSH for many things for years, I had never before tried to apply it to e-mail routing problems.

The first thing I discovered was that I could manually create an SSH port tunnel on the command line, between an arbitrary local port and an arbitrary remote port. One advantage to this approach is that the remote machine cannot tell that the access isn't local, which removes the cablespeed.com address from e-mail source paths:

Setup:

  • Issue this command: ssh servername -L 7777:localhost:smtp -N
  • Configure email client to use "localhost:7777" as the SMTP server and port.

Results:

E-mail source path, before:
Received: from [192.168.0.1] (cxxx-xxx-xxx-xxx.sea2.cablespeed.com [xxx.xxx.xxx.xxx])
	by vps.arachnoid.com (8.11.6/8.11.0) with ESMTP id lA8KnjX06029
	for <address@arachnoid.com>; Thu, 8 Nov 2007 12:49:46 -0800 (PST)
	(envelope-from address@arachnoid.com)
              
E-mail source path, after:
Received: from [127.0.0.1] (vps.arachnoid.com [64.226.42.136])
	by vps.arachnoid.com (8.11.6/8.11.0) with ESMTP id lA8KmOX05724
	for <address@arachnoid.com>; Thu, 8 Nov 2007 12:48:24 -0800 (PST)
	(envelope-from address@arachnoid.com)
              

Notice the absence of all non-local addresses in the second path. As far as sendmail is concerned, there is no need to authorize a relay because the access is from localhost (127.0.0.1), and there is no need to even consult a blacklist database because all the addresses are local.

Apart from solving the original problems of being blacklisted and having to provide a relay entry for every visited wireless hotspot, another advantage to this setup is that e-mail transactions are much, much faster. It's not obvious why this would be true, until one realizes that the SMTP server needs to access a very large Spamhaus blacklist database any time it sees a non-local address. Over a period of years I had noticed a gradual increase in the time required for e-mails to go through, but I hadn't discovered the reason until this project.

A problem with the above approach is that the SSH tunnel is not on-demand, it must be explicitly set up. It can be made persistent, like this:
ssh arachnoid.com -L 7777:localhost:smtp "sleep 86400"

Which avoids the problem of having to reestablish the connection (not the only way), but if I am on the road, changing locations, I don't want a persistent connection anyway — it will just cause problems. So I decided to find a way to create the tunnel on demand.

SSH tunnel on demand using xinetd

There are a number of descriptions of this method on the Web, but at the time of writing, all of them are perfectly dreadful, some of them requiring a separate, unique DSA key file for the SMTP tunnel, with some ad-hoc server instructions typed into it. My method uses the default DSA key and no special instructions on the server side.

This method creates the SSH SMTP tunnel on demand, when the e-mail client needs it, and destroys the tunnel at the end of each transaction, with no user actions required. For someone traveling with a laptop, this is ideal.

This method requires two additional applications beyond those used in the prior examples:

On the client machine, the xinetd daemon needs to be installed and running.

On the server machine, a copy of netcat needs to be present. On some systems, modified versions or similar utilities go by the command name(s) nc, ncat, pnetcat, socat, sock, socket, spd. The reader may want to check whether one of these variants is present on the server. I will be showing the syntax for netcat, because I tested it and I know its syntax — the other applications may require different syntax.

Both these applications are readily available through package managers and other means.

Here is how to set up SSH SMTP on-demand:

  • Create a file named "tunnel_smtp" with the following content:

    service tunnel_smtp
    {
    	type = UNLISTED
    	port = 7777
    	socket_type = stream
    	protocol = tcp
    	wait = no
    	user = (username)
    	disable = no
    	server = /usr/bin/ssh
    	server_args = (servername) netcat localhost smtp
    	groups = yes
    	bind = localhost
    }
                    

    NOTE 1: Replace the name (username) above with the server-side user name you used to set up SSH earlier. Replace the name (servername) above with the server name or IP.

    NOTE 2: There is nothing special about port 7777. If you are not running an SMTP server locally, you can even use the SMTP port (25) on both ends of the circuit, although this might get confusing.

  • Depending on your Linux distribution, either put a copy of the file "tunnel_smtp" into the directory /etc/xinetd.d, or use an editor to insert the file's contents into /etc/xinetd.conf.
  • Restart xinetd as root: /sbin/service xinetd restart
  • If xinetd isn't running after you reboot your computer, restart it as shown above and also issue this command as root: /sbin/chkconfig xinetd on
  • Set up your e-mail client to use localhost:7777 as its SMTP server specification.
  • Test this method. Try sending an e-mail to yourself and examine the received message header to see the route the message took. Only local addresses should be present in the header.
Firewall Tunneling

Here is a way to gain access to a machine on the other side of a firewall. The only precondition is that the firewall allow port 22, the SSH port, through. In this setup, there are two addresses or machine names, one for the firewall/router and one for the target machine.

Here is an example that creates a tunnel to an HTTP server on a machine with the address 192.168.1.5 behind a router/firewall with the address 111.222.333.444:
ssh 111.222.333.444 -L 8888:192.168.1.5:http "sleep 86400"
Having created the tunnel, one may access the remote machine this way:
firefox localhost:8888

Again, as before, there is nothing special about port 8888, it is simply a port chosen at random above the privileged port range 0-1023, so an ordinary user can create the tunnel.

Because a typical HTTP transaction consists of dozens to hundreds of individual transactions, the persistent form of the tunnel creator (using "sleep 86400") is advised. Otherwise the first item will be downloaded, but then the connection will terminate.

As in the earlier example, one can use xinetd to automate the creation and destruction of the SSH tunnel:

service tunnel_http
{
	type = UNLISTED
	port = 8888
	socket_type = stream
	protocol = tcp
	wait = no
	user = (username)
	disable = no
	server = /usr/bin/ssh
	server_args = 111.222.333.444 netcat 192.168.1.5 http
	groups = yes
	bind = localhost
}
            

Unfortunately for this specific example, the HTTP protocol is stateless, which means every request for a Web page or graphic represents a separate transaction, and therefore requires the creation of a separate SSH tunnel (and a separate xinetd server process). Depending on the specifics of your connection, this factor can dramatically slow down the loading of a Web page compared to a local connection.

In the command-line HTTP tunnel example above, we were able to use the persistent form to keep a single tunnel open, but when using xinetd, it is important to remember that a new tunnel is created for each client-side port access. This is an example where the use of xinetd to automate the creation of the tunnels may not be very practical, depending on the speed of the connection.

Conclusion

Protocols like SSH represent a needed change in the internet, a move toward greater security, and a gradual replacement of older, insecure protocols with more robust ones.

In these examples, SSH worked alongside some of the older protocols, for example by making half an e-mail transaction secure (between the sender's client program and the server) while leaving the other half as it was (between server and the recipient's client program). Eventually the entire message pathway will be handled by secure protocols, and other steps will be taken to secure the transmission of messages across the Internet. The end result will be an Internet totally different than the present one — the only way you will read about organ enlargement is if you ask to read about it.

 

Home | Linux |     Share This Page