This is a brief cheat sheet for basic OpenSSH use and configuration, because I can never remember how to do something or why to do it that way.
The advice is based on several sources listed either inline or at the end.
Examples use the user hello
at some remote machine with the IP address 10.11.22.33
,
listening for SSH connections on port 3742
.
Establish a connection with
ssh -i ~/.ssh/id_rsa_world [email protected]:3742
The SSH client can be configured to reduce the command to
ssh world
To avoid having to repeat the passphrase
when authenticating several times in succession
an SSH agent implementation can be used.
Some operating systems,
including Mac OS X and Ubuntu distributions,
manage these by default.
Otherwise, OpenSSH includes ssh-agent
for this purpose.
ssh-agent
sets up environment variables it needs to function
so its output needs to be evaluated:
eval $(ssh-agent)
Afterwards, a key can be unlocked with
ssh-add ~/.ssh/id_rsa_world
For public key authentication you need to have a key pair. A new one can be generated with
ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa_world
Specify a passphrase when prompted, then transfer the public key.
It is recommended not to use the default key names (id_rsa
, etc.).
In addition to simply being difficult to keep track of,
by default OpenSSH will look for these and transmit any it finds
when establishing a connection.
The client can be configured to not do this,
but getting in the habit of avoiding the default names is probably better.
After generating a key pair, the public key needs to be copied to the remote machine.
If the remote machine is, say, a VCS host with SSH support, it may provide a Web interface for inputting the public key (and it may provide no other means of transferring it). They public key can be copied from the command line with
xsel --clipboard < .ssh/id_rsa_world.pub
or an equivalent selection utility.
The file can also be opened in any plain text editor and the contents copied from there.
Otherwise, if the remote machine provides shell access, you can use
ssh-copy-id -i ~/.ssh/id_rsa_world -p 3642 [email protected]
which, with SSH client configuration, can be reduced to
ssh-copy-id -i world
Lastly, the key can be copied manually with
scp ~/.ssh/id_rsa_world.pub -P 3742 [email protected]:
ssh [email protected]:3742
mkdir ~/.ssh
chmod 700 ~/.ssh
cat ~/id_rsa_world.pub >> ~/.ssh/authorized_keys
rm ~/id_rsa_world.pub
chmod 600 ~/.ssh/authorized_keys
In the last case it is not possible to reuse the identity file,
because the identity file requires public key authentication,
which in turn depends on the above steps having been carried out.
Afterwards, however, scp -i world
can replace the destination specification
(insofar as the necessary client configuration has been made).
A passphrase for an existing key can be changed with
ssh-keygen -f ~/.ssh/id_rsa_world -p
For documentation, see man ssh_config
.
Client configuration precedence is
- command line options
- user configuration (
~/.ssh/config
) - system-wide configuration (
/etc/ssh/ssh_config
)
In the configuration file,
settings are divided into Host
sections
applied in the order of most to least specific match.
From man ssh_config
:
Since the first obtained value for each parameter is used, more host-specific declarations should be given near the beginning of the file, and general defaults at the end.
This means Host *
should always be the last section.
Below is a basic ~/.ssh/config
template
aiming for generally sensible security settings.
It prefers to re-specify default system-wide settings
for situations where these may not be trusted;
for instance, where you do not own the system
or where the distribution's default configuration
diverges from vanilla OpenSSH.
All indentation is optional.
# Per-host configuration.
#
# If HostName is omitted, the value for Host is used instead.
# If the value for Host is anything other than the actual host name,
# the configuration may be ignored by other tools:
# for instance, Git will accept "github.com"
# but not "GitHub" or "github".
#
# $ ssh world
Host world
# connects to this case-sensitive hostname...
HostName 10.11.22.33
# listening on port...
Port 3742
# as user...
User hello
# with identity file...
IdentityFile ~/.ssh/id_rsa_world
# A set of trusted servers with a common prefix.
Host my-*
# Forward X11 fully;
# default: no
ForwardX11Trusted yes
# Fall-back options.
# Set global defaults here.
Host *
# Do not connect to any remote machine using SSH-1;
# default: 2
Protocol 2
# Only use the 'IdentityFile's specified in this file;
# default: no
IdentitiesOnly yes
# Do not forward authentication agent to remote machine;
# default: no
ForwardAgent no
# Do not forward X11;
# default: no; no
ForwardX11 no
ForwardX11Trusted no
# Check every 60 seconds that the connection is still alive,
# and terminate connection after 5 failed tries;
# default: 0 (never); 3
ServerAliveInterval 60
ServerAliveCountMax 5
# Reuse existing connections,
# useful especially for slow hosts;
# default: no
ControlMaster auto
ControlPath /tmp/%r@%h:%p
ControlPersist no
These specific configurations are useful for some popular Git hosts:
Host github.com
User git
IdentityFile ~/.ssh/id-github
Host bitbucket.org
User git
IdentityFile ~/.ssh/id-bitbucket
Host gitlab.com
User git
IdentityFile ~/.ssh/id-gitlab
Nearly always RSA, with a length of at least 2048.
For several years now 1024 bits has been considered inadequate, with 2048 being the recommended minimum.
RSA and DSA keys of equal length offer similar levels of security, so the faster algorithm tends to be preferable.
DSA is faster at generating signatures but slower at validating them.
In typical usage, which includes SSH authentication,
validation will happen much more than generation,
so RSA is superior.
This sample output from openssl speed
(very slow command)
shows a difference of a factor 10:
sign verify sign/s verify/s
rsa 512 bits 0.000048s 0.000004s 20733.3 250973.3
rsa 1024 bits 0.000156s 0.000011s 6422.8 93317.1
rsa 2048 bits 0.001173s 0.000036s 852.2 27853.8
rsa 4096 bits 0.008385s 0.000134s 119.3 7485.3
sign verify sign/s verify/s
dsa 512 bits 0.000048s 0.000046s 20686.6 21779.0
dsa 1024 bits 0.000111s 0.000122s 8995.7 8222.6
dsa 2048 bits 0.000355s 0.000422s 2814.1 2372.1
Another argument against DSA is that FIPS 186-2
mandates a length of exactly 1024 bits for DSA keys,
with no limit for RSA keys.
FIPS 186-2 has been superseded by FIPS 186-3,
which explicitly allows longer DSA keys,
but older implementations of ssh-keygen
are not (yet) FIPS 186-3 compliant
and so will prevent generating longer keys.
Recent advancements in cryptography suggest that elliptic curve cryptography (ECC) is better still. A caveat is that of the two ECC algorithms supported by OpenSSH, ECDSA looks like it may have been sabotaged by NSA and Ed25519 requires version 6.5 (Jan 2014) and up.
If the availability of Ed25519 can be relied on, it is considered both faster and more secure than RSA.
Usually, yes.
The recommendation here is to provide a passphrase,
since otherwise the private key will not be encrypted,
for instance allowing root
free access to it.
In certain situations, such as for headless script automation,
a passphrase is not feasible, however.
In those situations you should feel free to omit the passphrase
but instead take care to configure the remote machine
so that such connections are as safe as possible.
One tool for this is the command=
directive in the authorized_keys
file;
further configuration is outside the scope of this article.
You need the SSH client to issue a challenge.
The simplest way to do this is with ssh-keygen
:
ssh-keygen -f ~/.ssh/id_rsa_world -y
If the passphrase is correct the public key will be printed to stdout
.
You can also achieve this by attempting to load the key into an agent,
for instance with ssh-agent
and ssh-add
.
A recurring issue is incorrect file permissions, for which there are quite strict requirements.
For the connecting client, ensure
chmod go-w ~/
chmod 700 ~/.ssh/
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/id_rsa_world
chmod 600 ~/.ssh/*.pub
Note that public keys (*.pub
) may optionally be readable by anyone (644
).
For the remote machine, ensure
chmod go-w /home/hello/
chmod 700 /home/hello/.ssh/
chmod 600 /home/hello/.ssh/config
chmod 600 /home/hello/.ssh/authorized_keys
If that does not solve the problem, look elsewhere.
With ProxyCommand
.
The example below uses the previous configuration for world
.
It also defines an additional finnish-world
,
but this machine is on a network
that can only be reached from world
.
Host world
HostName 10.11.22.33
Port 3742
User hello
IdentityFile ~/.ssh/id_rsa_world
Host finnish-world
HostName 192.168.1.1
User hei
ProxyCommand ssh -q -W %h:%p world
To connect to finnish-world
we can first connect to world
as normally with
ssh world
and from there establish a direct connection to finnish-world
.
The ProxyCommand
directive used above does this automatically,
so that
ssh finnish-world
on the original machine works as desired.
Do not use SSH agent forwarding (ssh -A
).
https://wiki.archlinux.org/index.php/SSH_keys
https://heipei.github.io/2015/02/26/SSH-Agent-Forwarding-considered-harmful/
https://www.reddit.com/r/programming/comments/3fut6m/his_ssh_server_knows_who_you_are/ctt71cd
https://www.reddit.com/r/programming/comments/3fut6m/his_ssh_server_knows_who_you_are/ctsfdb3
https://superuser.com/questions/261361/do-i-need-to-have-a-passphrase-for-my-ssh-rsa-key
https://security.stackexchange.com/questions/5096/rsa-vs-dsa-for-ssh-authentication-keys
https://heipei.github.io/2015/04/29/OpenSSH-Secure-Networking-Swiss-Army-Knife/