Penguin

See the SSHErrors page for resolving SSH related errors, and SSHKeys for information about setting up SSH key logins.

Config file aliases

To ease your life, you can put all settings for a particular host in your .ssh/config. Say you would normally use the following command to connect to somewhere:

ssh me@some.long.winded.fqdn.name.here.com

That's a lot to type. If you spell this out in configuration directives (see ssh_config(5)) it looks like this:

Host foo
  User me
  HostName some.long.winded.fqdn.name.here.com

From then on it's enough to simply say this:

ssh foo

Much better! You can also use wildcards for the hostname. A Host * stanza defines directives that should be in effect for all SSH connections.

Control character

~ is the control character for SSH. Once you get into control mode there are a few things you can do – exit the current ssh session (use ~.) or background it (~^Z). ~? will give you a list of control sequences. This is discussed in ssh(1), of course.

~ is only special if it is pressed after a newline, otherwise a literal "~" character is sent (as it is if the following character is not one of the meaningful ssh commands). ~~ after a newline will also force a "~" char to be sent. If you are SSHed from another SSH connection, you can do ~^Z after a newline to make the remote ssh client get the background command, instead of your local client.

Virtual Terminal

Ever tried to do reconnect a Screen session over SSH?

ssh joe@home.sweet.home screen -rx
Must be connected to a terminal.

This is Screen complaining, not SSH. Well, you can provide a terminal easily by using the -t switch: ssh -t user@host screen -rx

Combined with SSHKeys it's excellent for reconnecting those IRC sessions with a single key/buttonpress.

Unfortunately this option cannot be set from your ssh_config(5).

Connecting to two hosts behind one IP address

You may connect directly to a number of machines NATted behind a single IP address using different ports.

After the first host has been connected to, subsequent hosts will give a warning
Warning: the RSA host key for 'example.com' differs from the key for the IP address '1.1.1.1'
Offending key for IP in /home/joerandom/.ssh/known_hosts:2
Matching host key in /home/joerandom/.ssh/known_hosts:7
Are you sure you want to continue connecting (yes/no)?

To avoid this, use the HostKeyAlias keyword to specify an alternative name rather than the hostname to use for finding the host key in your known_hosts file. Set up aliases for each host/port combination in your ssh_config as explained above in Config file aliases, then assign them each unique key aliases:

Host foo
  HostKeyAlias example_com_9622
  HostName example.com
  Port 9622
  CheckHostIP no
Host bar
  HostKeyAlias example_com_9623
  HostName example.com
  Port 9623
  CheckHostIP no

Note that your system's configuration (in ssh_config) might default to CheckHostIP being off, which means you can safely omit those lines from your configuration. YMMV.

Now instead of typing ssh -p 9622 example.com, you say ssh foo and ssh(1) will figure things out correctly.

A clumsier workaround is to add UserKnownHostsFile ~/.ssh/known_hosts_hostname to each ssh_config(5) stanza, so a different list of known hosts is used for each host.

If you just need a quick and dirty workaround, add StrictHostKeyChecking no to each stanza in your ssh_config(5). This will still produce a warning, but will connect without any input needed, while decreasing your security.

Port Forwarding

SSH can forward ports across its encrypted tunnel. F.ex., if you issue ssh -L 5000:very.remote:80 user@host.remote SSH will be listening on port 5000 on localhost. By connecting to this port you get a connection tunneled to host.remote, which will then establish a connection to port 80 on very.remote. This is very helpful if very.remote lives behind a FireWall that blocks direct traffic, but will let you SSH to host.remote.

By the same token, using ssh -L 5000:localhost:80 user@host.remote you can tunnel a connection from port 5000 on your own host to port 80 on host.remote, which for the purposes of the webserver on the other end will appear to come from that localhost.

If you don't need the shell, and just want the tunnel, you can add options -f (go to background before command execution) and -N (don't execute remote command): ssh -f -N -L 5000:localhost:80 user@host.remote SSH will then effectively run as a daemon.

If you add -g, your local end of the SSH tunnel will accept connections from anyone, not just localhost. Say there are two machines called host1.lan and host2.lan on a LAN. By doing

host1$ ssh -f -g -N -L 5000:localhost:80 user@host.remote
host2$ lynx host1:5000

you have opened a connection from host2.lan to the webserver on host.remote without, in fact, connecting from host2.lan to host.remote.

Imagine the fun you can have with multiple SSH forwards!

If you've set up your .ssh/config as in the tip above, you can spare yourself typing the same parameters to set up tunnels in the same manner. -L 5000:localhost:110 translates to LocalForward 5000 localhost:110. If you'd like to have -g taken care of as well, add GatewayPorts. -f and -N don't have corresponding options, but those wouldn't be very useful anyway.

X Connection Forwarding

If you use the -X option to ssh(1), you will enable X-connection forwarding. This is essentially a reverse port forward with a few added effects: for instance it will set your DISPLAY EnvironmentVariable on the remote end to something like localhost:15. Most of the time you won't need to mess with xhost(1) or xauth(1) either. If you've set up your .ssh/config as discussed above, you can spare yourself typing -X every time using the ForwardX11 directive.

The SSH daemon on the remote machine must be configured to allow X11 forwarding. If it is disabled, you have to edit /etc/ssh/sshd_config and then restart sshd(8).

Be aware that you will need the X libraries and utilites installed on the server even if you're forwarding clients from a headless server. On Debian, you need the xbase-clients Package; on FreeBSD, it’s xorg-clients. In general, you need whatever packages will give you the xhost(1) binary.

If you are running OpenSSH 3.8 or newer, some X applications may give you one of these errors:

  • BadWindow (invalid Window parameter)
  • BadAccess (attempt to access private resource denied)
  • X Error of failed request: BadAtom (invalid Atom parameter)
  • Major opcode of failed request: 20 (X_GetProperty)

In that case you will also find by invoking xdpyinfo(1)? from the remote machine that they can only use a fraction of the extensions your XServer offers. This is because the new default is to use untrusted X11 cookies for forwarding connections. You need to invoke ssh(1) with the -Y option for X11 forwarding, or add ForwardX11Trusted yes to your configuration. Since one of the affected extensions is XRENDER, which greatly reduces the bandwidth required to draw AntiAliasedFonts and accelerates their rendering, it is unclear why anyone would ever use untrusted cookies.

If you are forwarding an X11 connection over a link with relatively high latency (such as ADSL rather than over a LAN), then you will also get a performance improvement by enabling compression (either Compression=yes in your config file, or the -C command-line option). See the section below for more on compression/transfer rates.

STDIN Forwarding

SSH forwards its standard input to be the standard input of a command executed on the remote machine. You can use this to do some pretty cool things like stream tar(1) archives across a network to eliminate any overhead with copying many small files:

tar c sourcedir | ssh user@host tar xf -

There are many other neat tricks that can be perfomed using this technique as well. The feature was inherited from rsh(1). If you are having problems with corruption when piping data via SSH, you may find a solution on the SSHErrors page.

In a rudimentary test involving about 200MB of variously sized files, the tar(1) stream finished in about 4:00 minutes, where scp -R took about 5:40 minutes. (That is even though it doesn't seem to establish a new connection for each file as ftp(1) does, nor have much overhead at all. So tar(1) could just be better at doing this than scp(1).)

Executing the same command on multiple machines

Check out the distributed shell, dsh(1). It lets you SSH to a set of drones and execute the same series of commands on all of them, watching the output as you go.

SSHing to not directly reachable hosts

Suppose you have a shell account on safe, a machine on a NATted LAN without a public IP address assigned to it. Let's also suppose you can SSH to the publicly visible gateway router of that LAN, door, and that on this machine a copy of nc(1) (aka netcat) is installed. With the aid of a config file alias for the NATted host and a config directive called ProxyCommand, you can easily set yourself up such that SSHing to safe is no different from SSHing to any publicly visible box:

# this stanza isn't necessary of course
Host door
  Protocol 2
  User me
  HostName homelan.at.dsl.my.isp.com
# but this one is
Host safe
  Protocol 2
  User me
  HostName 192.168.0.42
  ProxyCommand ssh door nc -q 0 safe 22

If you have the sshd(8) running on a different port than 22 (the standard SSH port) on safe, you need to change the argument to nc(1) accordingly, of course. Note the "-q 0" argument to netcat – without this, you will likely end up with stale netcat processes on the intermediary machine. Now, you can just ssh safe and SSH will transparently invoke another copy of itself that connects to door and uses the netcat installed there to establish a connection from door to safe. netcat's STDIN/STDOUT is tunnelled back to your via your slave SSH connection to door, across which the primary SSH process can then communicate with the sshd(8) on safe.

Note that since the master SSH process is only communicating with the proxy process, it has no way of knowing the IP address of the remote host it is talking to. Since its address is needed to verify its host key's authenticity, it is advisable to use a HostName directive for the proxied host to specify its address. Since the address is not otherwise used, you can leave it out or even enter something fake – you'll just get a complaint from SSH.

Finally, remember that ssh foo nc bar 1234 is just one example of what the proxy command could be. The only requirement is that it forward the traffic, whether that be a TCP connection or even anything else, to and from SSH over STDIN/STDOUT. You might even use something like ProxyCommand pppd nodetach call foo and have the remote getty(8) launch sshd -i on the incoming line, thus logging into a machine that's not even connected to the InterNet.

The possibilities are endless. In the standard case of using a slave SSH connection to some gateway, nothing stops you from using a ProxyCommand in the alias configured for the gateway – so you can build an entire cascade of SSH tunneled connections from one gateway to the next. Of course eventually the onion of tunnels wrapped around the connection will push the latency up to and the throughput down to unworkable levels.

Improving the detection of lost connections / coping with flaky net links

Configure your SSH client to keep making sure it can still talk to the remote host if it hasn't received any data in a while. This is done by setting ServerAliveInterval in your .ssh/config to how many seconds of silence the client should wait. The ServerAliveCountMax directive defines how many attempts to get a reaction from the remote host may go unanswered before the SSH clients decides the connection has been lost. If you specify 5 seconds and 3 tries, a dead connection will be detected in 15 seconds.

The default values are 0 seconds, which means never, and 3 tries.

Pick these according to bandwidth, ping time to the server, and packet loss on your link.

Speeding up transfer rates

SSH can use gzip compression on any connection, and will use a modest level by default. Compression is a great idea if you are forwarding X sessions on a dial-up or slow network. It can be turned on using the -C switch, or using Compression yes in your .ssh/config.

Changing your encryption cipher from the common default of DES or 3DES can speed things up as well. BlowFish and AES are faster, and new versions of OpenSSH default to BlowFish. The CommandLine switch to change the cipher is -c blowfish. For .ssh/config it depends on whether you are connecting with SSH1 or SSH2.

Cipher blowfish # SSH1
Ciphers blowfish-cbc,aes128-cbc,3des-cbc,cast128-cbc,arcfour,aes192-cbc,aes256-cbc # SSH2

If you are transferring large files across a GigE LAN, you will probably be limited by small buffers inside ssh preventing you from reaching anywhere close to line speed. There are patches available for improving performance on such LANs in several different ways (eg increasing these buffer sizes, disabling encryption if you trust the LAN, multi-threaded encryption) - see http://www.psc.edu/networking/projects/hpn-ssh/

SSH for apt-get(8)

If you try and run apt-get(8) without a terminal or the right paths, it won't be able to find dpkg(8) or display debconf information. This is the way I've found (useful for remote upgrading of machines – note, only do this off security or your own repository…)

ssh root@hostname -t "su - -c 'apt-get update && apt-get upgrade'"

Part of CategorySecurity and CategoryNetworking