blog

Woman holding camera, Photo by Tiago Muraro on Unsplash

SSH: Tips Your Grandmother Wished She Knew

by

Alright, maybe your grandmother doesn’t need tips for using ssh. In fact, she probably doesn’t even know ssh is a secure shell for accessing a remote host.  Go figure.

But I know when I reach the age of grandparenthood and I begin contemplating shuffling off this mortal coil, I will still be using ssh and exploiting its power. (Just you wait: Senior Citizen Geeks, hacking for the pleasure of it…we’re coming sooner than you think.)

As software engineers, particularly those of us who work remotely, we are very dependent on ssh. We use it for remote login. We use it to run remote commands. We use it as a transport for secure software repository access (e.g., git over ssh). The savvy among us install public keys to do this without passwords. But did you know:

  •  You can proxy your ssh connection from one host (say, a gateway to a remote site) to another (i.e., an internal host at the remote site)?
  • Forward arbitrary ports from your computer to the remote host? For example, you are developing a database application and want to test your local development branch against the schema running on the remote server.
  • You can use ssh as a SOCKS server, using your remote connection as your browser’s proxy server?
  • You can use it to subvert firewalls that block that port you need, including the ssh port, 22?
  • You can collect options into a configuration file to run all the above simultaneously without hassle?

In my experience, ssh is one of the most underutilized tools by remote workers. It is my sincere hope that this brief introduction will whet your appetite.

Early sections will review ssh basics; later sections will demonstrate advanced topics like proxy commands and port forwarding.

But first, from the ssh manpage:

SSH (Secure SHell) is a program for logging into a remote machine and for executing commands on a remote machine. It is intended to replace rlogin and rsh, and provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP ports can also be forwarded over the secure channel.


Table of Contents


Remote Login

The basic syntax for logging into a remote host is:

    ssh hostname

For example, if user guesta is logged in to host A and wants to login to host B:

    guesta@A:~$ ssh b
    guesta@bs password: xxxxxx
    ...
    guesta@B:~$

The remote username is taken from the client username. If you want to login with a different username you can use one of the following syntaxes:

ssh -l username hostname

or

ssh username@hostname

For example, if user guesta wants to login to host X as user guestq:

    guesta@A:~$ ssh guestq@X
    guestq@xs password: xxxxxx
    ...

Server Keys

The first time you login to a remote server you are asked to verify the server’s key:

guesta@A:~$ ssh somehost
The authenticity of host "somehost (192.168.0.1)" cant be established.
RSA key fingerprint is b1:0c:3b:45:bd:e1:2a:5c:8c:91:a1:a7:4f:63:e9:c6.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added "somehost,192.168.0.1" (RSA) to the list of known hosts.
Password: xxxxxx
Last login: Wed Oct 22 17:02:05 2008 from 192.168.0.14

This is done to prevent an attacker from impersonating a server, which could allow them to capture passwords or other sensitive data. Once you have accepted the server’s key (it will be stored in ~/.ssh/known_hosts) it will be checked automatically with each future login to the server; should the server’s key change for some reason you will see the following message:

guesta@A:~$ ssh tlab-x
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ 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 a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
b1:0c:3b:45:bd:e1:2a:5c:8c:91:a1:a7:4f:63:e9:c6.
Please contact your system administrator.
Add correct host key in /home/guesta/.ssh/known_hosts to get rid of this message.
Offending key in /home/guesta/.ssh/known_hosts:578
remove with: ssh-keygen -f "/home/guesta/.ssh/known_hosts" -R tlab-x
RSA host key for tlab-x has changed and you have requested strict checking.
Host key verification failed.

Note the output at line 14. If you are sure the key change is valid you can remove the line from ~/.ssh/known_hosts (in the above example, line 578), either manually or as suggested in the output, with ssh-keygen:

ssh-keygen -f "/home/guesta/.ssh/known_hosts" -R tlab-x

After removing the offending key, login again, accepting the new key.

If you know the key has not been changed, then you should contact the system administrator.


Remote Command Execution

You can specify a remote command with ssh:

    ssh hostname command

E.g., to get a file listing on host X:

guesta@A:~$ ssh x ls -C /bin
guesta@xs password: xxxxxx
bash      dnsdomainname   more         sleep
bunzip2   echo            mount        stty
bzcat     ed              mountpoint   su
bzcmp     egrep           mt           sync
bzdiff    false           mt-gnu       tailf
bzegrep   fgconsole       mv           tar
...
guesta@A:~$

In the above example, ssh will execute the specified command (ls -C /bin) on the remote host with the remote user’s shell, redirecting stdout back to your terminal. When the remote command exits, so will ssh on the client.

In the following example, the output is piped to grep:

guesta@A:~$ ssh x ls /bin | grep ^b
guesta@xs password: xxxxxx
bash
bunzip2
bzcat
bzcmp
...
guesta@A:~$

In the above example, ls is run on host x and the output is sent back to the client on host a where grep is run. If you wanted grep to be run on host x then you would need to put the whole pipeline within double quotes:

guesta@A:~$ ssh x "ls /bin | grep ^b"
guesta@xs password: xxxxxx
bash
bunzip2
bzcat
bzcmp
...
guesta@A:~$

We’ve seen how you can redirect (or pipe) stdout. You can also redirect stdout from a client process to stdin of the remote process:

guesta@A:~$ echo "this is a test from $(hostname)" | ssh x cat
guesta@xs password: xxxxxx
this is a test from A
guesta@A:~$

In this next example, a remote backup is taken and stored locally:

guesta@A:~$ ssh x "cd /bin;tar -zcf - b*" > backup.tgz
guesta@xs password: xxxxxx
guesta@A:~$ ls -l backup.tgz
-rw-r--r-- 1 guesta guesta 361375 2008-10-24 11:56 backup.tgz
guesta@A:~$ tar ztf backup.tgz
bash
bunzip2
bzcat
bzcmp
bzdiff
bzegrep
bzexe
bzfgrep
bzgrep
bzip2
bzip2recover
bzless
bzmore
...

Psuedo TTYs

By default, when executing remote commands no pseudo tty is assigned to your session, so running editors or other utilities requiring interactive input may fail, as in:

guesta@A:~$ ssh x emacs
guesta@xs password: xxxxxx
emacs: standard input is not a tty
guesta@A:~$

In this case, specify the -t option which forces ssh to assign a pseudo tty:

guesta@A:~$ ssh -t x emacs
guesta@x's password: xxxxxx

A Note About ssh-copy-id

In serveral places throughout the rest of this document referenceswill be made to ssh-copy-id, a tool to copy public keys (see PublicKey Authentication) to remote servers. ssh-copy-id is a shell script available on many linux distributions. It may not be included in your ssh installation (it is known to be missing from some versions of OS X). Here is some shell magic you can use if it is not available on your computer…

Assuming the public key you want to copy is ~/.ssh/id_rsa.pub and you want to install the key on remote computer, X:

$ cat ~/.ssh/id_rsa.pub |
ssh X "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys"

File this away if you want to follow the instructions in this document, but discover you do not have ssh-copy-id.


Public Key Authentication

SSH includes the ability to authenticate users with public/private key pairs instead of passwords. With key authentication, the server will challenge the client with a token generated by the user’s public key and the client will validate itself with a return token based on the private key.

To use passwordless authentication you must generate your own public/private key pairs with the ssh-keygen utility and then install the public key on remote servers. Your private key
should be password protected and never, ever revealed to anyone.

guesta@A:~$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/guesta/.ssh/id_rsa):
Enter passphrase (empty for no passphrase): xxxxxxxx
Enter same passphrase again: xxxxxxxx
Your identification has been saved in /home/guesta/.ssh/id_rsa.
Your public key has been saved in /home/guesta/.ssh/id_rsa.pub.
The key fingerprint is:
f8:e4:89:3e:c9:60:82:21:52:42:15:ab:87:6f:41:f5 guesta@A
guesta@A:~$ ls -l ~/.ssh/id*
-rw------- 1 guesta guesta 1743 2008-10-24 12:23 /home/guesta/.ssh/id_rsa
-rw-r--r-- 1 guesta guesta 390 2008-10-24 12:23 /home/guesta/.ssh/id_rsa.pub
guesta@A:~$

The above generates two files:

~/.ssh/id_rsa (Private Key) This should never be revealed or copied to another computer.

~/.ssh/id_rsa.pub (Public Key) This should be copied to all remote computers.

You install the public key by appending its contents to the file ~/.ssh/authorized_keys on the remote computer. There is an utility that can do this for you, ssh-copy-id:

guesta@A:~$ ssh-copy-id -i ~/.ssh/id_rsa.pub somehost
Password: xxxxxx

Now try logging into the machine, with ssh "somehost", and check in:

.ssh/authorized_keys

to make sure we haven’t added extra keys that you weren’t expecting.

guesta@A:~$

Now when we login to somehost we will be prompted for our private key’s passphrase:

guesta@A:~$ ssh somehost
Enter passphrase for key '/home/guesta/.ssh/id_rsa': xxxxxxxx
Last login: Fri Oct 24 13:49:26 2008 from A
somehost>

You may be wondering what advantage we’ve gained if instead of typing in the remote password we have to always type in our private key’s passphrase. A utility helps keep track of private key passphrases: ssh-agent. Ssh-agent is started automatically login…from the ssh-agent manpage:

ssh-agent is a program to hold private keys used for public key authentication (RSA, DSA). The idea is that ssh-agent is started in the beginning of an X-session or a login session, and all other windows or programs are started as clients to the ssh-agent program. Through use of environment variables the agent can be located and automatically used for authentication when logging in to other machines using ssh(1).

You use the ssh-add utility to communicate with the ssh-agent daemon:

guesta@A:~$ ssh-add
Enter passphrase for /home/guesta/.ssh/id_rsa: xxxxx
Identity added: /home/guesta/.ssh/id_rsa (/home/guesta/.ssh/id_rsa)

Once you have authenticated your private key passphrase to the ssh-agent daemon you can login remotely without passwords!

guesta@A:~$ ssh somehost
Last login: Fri Oct 24 13:51:21 2008 from A
somehost>

And you can run remote commands without passwords:

guesta@A:~$ ssh somehost date
Fri Oct 24 13:54:00 EDT 2008
guesta@A:~$ ssh somehost uptime
13:54:05 up 71 days, 4:05, 10 users, load average: 0.00, 0.00, 0.00
guesta@A:~$

Proxy Commands

For this section, imagine you are at computer HOME, attempting to login to REM-D, via a gateway host, REM-GW, as in this diagram:

Access to remote computer, REM-D, via ssh gateway host, REM-GW


Access to remote computer, REM-D, via ssh gateway host, REM-GW

Does this look familiar to you:

home$ ssh rem-gw
Password: xxxxxx
rem-gw$ ssh rem-gw
Password: xxxxxx
rem-d$

Or this, for copying a file from HOME to REM-D:

home$ ls -l foo.dat
-rw-r--r-- 1 user1 user1 3866 2009-09-09 10:57 foo.dat
home$ scp foo.dat rem-gw:
Password: xxxxxx
foo.dat 100% 3866 3.8KB/s 00:00
home$ ssh rem-gw
Password: xxxxxx
rem-gw$ ls -l foo.dat
-rw-r--r-- 1 user1 user1 3866 2009-09-09 10:57 foo.dat
rem-gw$ scp foo.dat rem-d:
Password: xxxxxx
foo.dat 100% 3866 3.8KB/s 00:00
rem-gw$ ssh rem-d
Password: xxxxxx
rem-d$ ls -l foo.dat
-rw-r--r-- 1 user1 user1 3866 2009-09-09 10:57 foo.dat
rem-d$

In both of the above examples you are staging access to your rem-d by first explicitly using rem-gw: login to rem-gw → login to rem-d -or- copy file(s) to rem-gw → login to rem-gw → copy files to rem-d. With proper ssh configuration you can avoid having to do this staging: you can copy directly from your home computer to your rem-d by transparently using rem-gw AND you can do it without passwords!

Goals

Login directly to your rem-d:

home$ ssh rem-d
rem-d$

Copy files directly to your rem-d:

home$ scp foo.dat rem-d:
foo.dat 100% 3866 3.8KB/s 00:00
home$ ssh rem-d ls -l foo.dat
-rw-r--r-- 1 user1 user1 3866 2009-09-09 10:57 foo.dat

HOWTO

Configure private/public keys.

Review the steps in Public Key Authentication and create keys on your home computer, copying the public key to rem-gw.

Before continuing: make sure you can login to rem-gw and copy files to rem-gw without a password.

Configure an ssh ProxyCommand

On your home computer create a file, ~/.ssh/config, with the following content:

Host rem-d

    User remd_user
    ProxyCommand ssh -l gw_user rem-gw nc rem-d 22

Where remd_user is your username on rem-d (if your username on rem-d is the same as your username on your home computer, you can omit this directive), gw_user is your username on rem-gw (if your username on rem-gw is the same as your username on your home computer, you can omit the -l option to ssh). The Host directive specifies an alias to use for this connection, i.e., you will specifiy rem-d on the ssh commandline.

Copy your public key to rem-d:

You already have your public key on rem-gw, but you also need your public key on the destination computer. Usessh-copy-id to copy the public key:

home$ ssh-copy-id -i ~/.ssh/id_rsa.pub rem-d

You will be prompted to accept the hostkey for rem-d as well as your rem-d password.

Confirm password-less connectivity.

You should now be able to connect directly to your desktopwithout passwords. Try:

home$ ssh rem-d

And…

home$ scp somefile rem-d:

You can now use rem-d as the hostname for any of the ssh suite of tools (ssh, scp, sftp) as well as any software that uses these tools, such as rsync. For example, to archive a directory hierarchy from rem-d to your home computer:

home$ rsync -av rem-d:some/directory/ local/directory

The above will create a copy of the directory hierarchy on your desktop to a local directory without placing any files on `rem-gw.

Adding more hosts

You can add as many Host directives as you want to your ~/.ssh/config file. For example:

Host rem-a
ProxyCommand ssh rem-gw nc rem-a 22
Host rem-b
ProxyCommand ssh rem-gw nc rem-b 22
Host rem-c
ProxyCommand ssh rem-gw nc rem-c 22
Host rem-d
ProxyCommand ssh rem-gw nc rem-d 22


Port Forwarding

Ssh allows you to forward any arbitrary port on your local computer to any port on any host through your connection.

For example, if a private web server is running within a remote network listening on standard HTTP port 80, you can use ssh remotely to tunnel a different port through rem-gw to port 80 on the web server. For example, let’s say a apache is running on rem-a, from our above network diagram:

ssh -f -N -L 8000:rem-a:80 rem-gw

What the above does:

-f puts ssh in the background and turns off stdin.
-N tells ssh not to run a remote command. We use this (and -f) in aid of what we’re really after…port-forwarding (-L):
-L 8000:rem-a:80 tells ssh to forward any connections to port 8000 on your local computer to port 80 on rem-a via the connection torem-gw’.

After authenticating on rem-gw (by typing in your password or by public/private keys) the ssh command will run in the background and you can view the site in your browser by going to http://localhost:8000/.

Here’s more detail on the -L option; paraphrased from the ssh manpage:

-L port:host:hostport
Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side. Whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to the specified host and port from the remote machine. Only the superuser can forward privileged ports (< 1024). Host can be an IP address or a hostname; if a hostname the name is resolved on the remote machine not the local client.

Port forwarding can be used for any tcp/ip service. A few more usefule examples:

You are running a PostgreSQL database on an internal server, dbserver:

ssh -fNL 5432:dbserver:5432 rem-gw

After authenticating you can access your database like this:

psql -h localhost -p 5432

You have a staging server, devstaging up and running and part of the software suite is a redis server that, for security purposes, only listens for connections on localhost. You have just updated your local branch and want to test against the staging server’s redis:

ssh -fNL 16379:localhost:6379 devstaging

If you configure your redis connection to use localhost:16379, you will connect to the server running on devstaging.

Your company uses proprietary software, some-program, protected by FlexNet Publisher and a license server is running on (using our network diagram from above) rem-b, port 1700:

$ export LM_LICENSE_FILE=1700@localhost
$ ssh -fNL 1700:rem-b:1700 rem-gw
$ some-program

SOCKS server: A Web Browser Proxy

Let’s say your company has a content license with a publishing house. When at work you can browse an extensive library of on-line, web browser based, technical manuals, which are not available away from the office. These services work by checking the incoming IP address(in addition to, perhaps, a login) and if the address is from a known network, e.g., your company’s network address, then you are granted access to the service, otherwise you are denied access.

SSH servers have a built-in SOCKS server which allows proxying IP connections. When away from the office, you can start the SOCKS server by running this command:

home$ ssh -D 8888 rem-gw

Then you can configure your browser’s access to the Internet to use a SOCKS v4 or v5 proxy server; specify the server is host localhost, port 8888. You then browse the web normally, but all your connections will go through rem-gw and each server you connect to will see your connection as originating from rem-gw, not your home’s ISP.


Subverting Firewalls

Sometimes you will find yourself on a network that is rather aggressive in its firewall policies and does not allow external connections except to a few standard ports (http, https, ssh, etc). If so, you can use port forwarding as described above.

But what if port 22 is blocked by your ISP? If you have administrative access to a remote ssh server (or know someone who does) you can configure ssh to listen on any port, including port 80, which no ISP blocks. You could then connect to the ssh server specifying port 80:

home$ ssh -p 80 rem-gw

Mounting remote filesystems.

SSHFS is a tool to mount a remote filesystem using ssh as the underlying transport. Using once again our diagram from above, if I wanted to browse the files on rem-d as if they were on my home computer:

home$ mkdir remdfiles # sshfs needs an empty directory as a mount point
home$ sshfs rem-d: remdfiles
home$ ls remdfiles
...

Other Recipes

As you may be realizing at this point, ssh is a Swiss army knife for all manner of network connections. This document has only scratched the surface of the possibilities. More advanced configurations can create a personal VPN. While using ssh, if you come up with a solution to a problem not documented here, or a recipe using ssh that increases productivity, share your idea with the rest of us by leaving a comment.

And don’t forget to tell grandma.

+ more

Accurate Timing

Accurate Timing

In many tasks we need to do something at given intervals of time. The most obvious ways may not give you the best results. Time? Meh. The most basic tasks that don't have what you might call CPU-scale time requirements can be handled with the usual language and...

read more