SSH commands - the Secure SHell commands

ssh-keygen

Usage :

Generate / manage / convert / ... / SSH keys

Flags :

Flag Usage
-a numberRounds When saving a private key, this option specifies the number of KDF rounds used.
Higher numbers result in slower passphrase verification and increased resistance to brute-force password cracking (should the keys be stolen).
-b numberBitsKey Specifies the number of bits in the key to create, which depends on the key type.
-C comment Will append comment at the end of the public key (myKey.pub) :
ssh-rsa AAAAB3NzaCDvrOAYDtfSbKsIzDt4fOKQ1yc2EAUadaPsKA2ofpo95n/Altv9t4mu1Ob/YHaZcdX0aCzS7WVQ== comment
ssh-ed25519 AAAACxOtLlwWTzud97ZBIpcQ3NzaC1lZDI1NTE5IHyXmUPVMe+qyxB5II3CeXx9k comment
comment defaults to username@hostname
-f keyFile Specifies the filename of the key file.
-N newPassphrase Specifies the new passphrase
-o save private keys using the new OpenSSH format rather than the more compatible PEM format. The new format has increased resistance to brute-force password cracking but is not supported by versions of OpenSSH prior to 6.5 (check it : dpkg -l | grep openssh).
ed25519 keys always use the new private key format.
-p Requests changing the passphrase of a private key file instead of creating a new private key. The program will prompt for the file containing the private key, for the old passphrase, and twice for the new passphrase.
-R host Removes all keys belonging to host from a known_hosts file (example)
-t type Specifies the type of key to create

Example :

The default OpenSSH key encryption is worse than plaintext :

Not sure how it could actually be "worse" than plaintext, but it sounds rather poor anyway (see gory technical details below). To workaround this :
  • replace old keys with keys using the new private key format :
    ssh-keygen -t ed25519
  • update private keys to the new storage format :
    1. privateKey=~/.ssh/id_rsa; oldPrivateKey="$privateKey.old"; cp "$privateKey" "$oldPrivateKey"; ssh-keygen -p -o -f "$privateKey"
    2. make sure the key pair still works (just in case...)
    3. rm -i "$oldPrivateKey"
  • This applies only to private keys having a passphrase. Commands will work on passwordless keys but are merely useless.
  • Compatibility with servers is not a concern, because the private key never leaves your machine.
Technical details :
Make a private key without passphrase (i.e. unencrypted private key, source) :
myKey='/run/shm/myKey'; ssh-keygen -N '' -f "$myKey"; head -5 "$myKey"; rm "$myKey"*
-----BEGIN RSA PRIVATE KEY-----
MIIEoAIBAAKCAQEA0Fcqiy4b2hoYLdy2jxgJ4n4Ek+S5rKCwfkbMTarq5WsSJ4Rf	MII... : the base64 DER clue that an RSA key follows
KBeDO5T1q8pjPefViRfJTuPIhWfdbivIkXIDdyGTOf+m6VufArnAW6kpj7cTWoeo
39L0qot4zV73LWv6aM/esLYqIuuY4bdZusEqEqdgMCB62uzo71CY3ti+F6o3VDUY
trMSFTQhyN/bqfhnrtBKpkmDnol4N6CThVpck1JFat0LDRu/hhtWdRlm2bmIgozv

The private key is an ASN.1 data structure, serialized to a byte string using DER, and then Base64-encoded.

View the ASN.1 structure (source):
myKey='/run/shm/myKey'; ssh-keygen -N '' -f "$myKey"; openssl asn1parse -in "$myKey"; rm "$myKey"*
Outputs the ASN.1 structure, made of 9 integers.
Make a private key with a passphrase (source) :
myKey='/run/shm/myKey'; ssh-keygen -N 'secret' -f "$myKey"; head -5 "$myKey"; rm "$myKey"*
----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,D634E4857AF0E3B1EF0CA8518425C70E	"D634E4857AF0E3B1EF0CA8518425C70E" is the Initialization Vector (IV)

yJ66gBHIeKhDSBB0Bh6oJezrprNcv8BQ/QszaOD3Qgc5XaJW04yaVbX5btO2ucpD
Try to view its ASN.1 structure :
myKey='/run/shm/myKey'; ssh-keygen -N 'secret' -f "$myKey"; openssl asn1parse -in "$myKey"; rm "$myKey"*
fails because not in ASN.1 format anymore : it's encrypted
Generate an encrypted key, then decode it (source):
password='123456'; myKey='/run/shm/myKey'; ssh-keygen -N "$password" -f "$myKey"; openssl rsa -text -in "$myKey" -passin "pass:$password"; head -5 "$myKey"; rm "$myKey"*
==> displays the decoded key.
Trying to do it manually :

password='123456'; myKey='/run/shm/myKey'; ssh-keygen -N "$password" -f "$myKey"; head -5 "$myKey"
	-----BEGIN RSA PRIVATE KEY-----
	Proc-Type: 4,ENCRYPTED
	DEK-Info: AES-128-CBC,DD75D306F2CEADFCD9F5B0AA88E72368

	jVdXts1mwNg2ZLB0GhmODPB1YBfn3R5i9cUlsViE9hBCZMTfp0st5eyMTUy6Otlr

echo -n 123456DD75D306 | md5sum
	500d7daf9dac3883117c63b073996144

tail -n +5 "$myKey" | grep -v 'END ' | base64 -d | openssl aes-128-cbc -d -iv DD75D306F2CEADFCD9F5B0AA88E72368 -K 500d7daf9dac3883117c63b073996144 | openssl asn1parse -inform DER
	==> fails :-(((


openssl rsa -text -in "$myKey" -passin "pass:$password";
rm "$myKey"*
...
...
...

...doesn't work 

Long story short, the AES encryption key (used to encrypt the private key) is built with :

md5(passphrase + 8 first bytes of the IV)
  • MD5 is used in the process of encrypting the private key with the passphrase.
  • It is used only once
  • both AES and MD5 functions are cheap in terms of CPU and memory
this key is prone to brute-forcing, especially if the passphrase is short/poor quality

ssh-copy-id

Usage :

ssh-copy-id is a script used to copy your SSH public key to the specified host. If necessary, it can also create the remote :

Flags :

Flag Usage
-i identityFile
  • use only the key corresponding to identityFile (i.e. do not try other existing keys)
  • .pub will be appended to identityFile if missing : identityFile is actually the public part of the key pair

Example :

ssh-copy-id -i ~/.ssh/myProject/id_ed25519.pub mySshServer
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/stuart/.ssh/myProject/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Password: t-o-t-a-l-e-m-e-n-t i-n-t-e-r-d-i-t

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'mySshServer'"
and check to make sure that only the key(s) you wanted were added.

ssh

Usage :

There is a lot to say about SSH, since there are :

Flags :

Flag Usage
-A Enable forwarding of the authentication agent connection (see ssh-agent, example)
You can enable this behavior in ~/.ssh/config with ForwardAgent yes.
-b bindAddress Use bindAddress on the local machine as the source address of the connection
-f Requests SSH to go to background just before command execution. Implies -n.
-F configFile Specifies an alternative per-user configuration file.
-i privateKey identity file (aka private key)
defaults to ~/.ssh/id_rsa for RSA
-J jumpHost connect to targetHost via jumpHost :
ssh -J jumpHost targetHost
This is equivalent to using the ProxyJump client configuration directive.
-M Enable SSH Master mode allowing connection sharing (for details, see ControlMaster) (See -S)
-n Redirects stdin from /dev/null (actually, prevents reading from stdin).
-N Do Not execute a remote command, just forward ports (protocol v2 only).
-p portNumber port number on which sshd will listen (default is 22).
-q quiet mode. Causes most warning and diagnostic messages to be suppressed (may be overzealous)
-S /path/to/controlSocket Specifies the path to the control socket for connection sharing (to be used with -M).
-t Force pseudo-tty allocation. This can be used to :
  • transmit signals between the SSH process and the remote one
  • allow sudo'ed commands to prompt for a password (example)
-T Disable pseudo-tty allocation. This proves useful only in scripts.
-v verbose mode. Repeat to increase verbosity : -vvv
-X enable X11 forwarding
-Y enable trusted X11 forwarding. These are not subjected to the X11 SECURITY extension controls.

Example :

Send command to SSH host :

  • ssh kevin@myServer 'command'
  • ssh -t haproxyServer 'sudo /usr/sbin/haproxy -c -V -f /etc/haproxy/conf.d/myConfig.cfg'
  • for server in server1 server2; do ssh "$server" 'df -h /home'; done

If the SSH session is interrupted (with CTRL-c or anything else) the remote process receives no signal and continues executing. To prevent this, you may allocate a pseudo-tty to SSH with -t. (source)