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
- Server Keys
- Remote Command Execution
- A Note About ssh-copy-id
- Public Key Authentication
- Proxy Commands
- Port Forwarding
- SOCKS server: A Web Browser Proxy
- Subverting Firewalls
- Other Recipes
The basic syntax for logging into a remote host is:
For example, if user
guesta is logged in to host
A and wants to login to host
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
For example, if user
guesta wants to login to host
X as user
guesta@A:~$ ssh guestq@X guestq@xs password: xxxxxx ...
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 -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
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
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
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 ...
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,
$ 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
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,
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:
to make sure we haven’t added extra keys that you weren’t expecting.
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
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:~$
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:
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$ 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!
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
Configure private/public keys.
Review the steps in Public Key Authentication and create keys on your home computer, copying the public key to
Before continuing: make sure you can login to
rem-gw and copy files to
rem-gw without a password.
Configure an ssh
On your home computer create a file,
~/.ssh/config, with the following content:
User remd_user ProxyCommand ssh -l gw_user rem-gw nc rem-d 22
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
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:
ProxyCommand ssh rem-gw nc rem-a 22
ProxyCommand ssh rem-gw nc rem-b 22
ProxyCommand ssh rem-gw nc rem-c 22
ProxyCommand ssh rem-gw nc rem-d 22
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
Here’s more detail on the
-L option; paraphrased from the ssh manpage:
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,
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
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
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.
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 ...
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.