Secure Shell
Secure Shell (or SSH) is a set of standards and an associated network protocol that allows establishing a secure channel between a local and a remote computer. It uses public-key cryptography to authenticate the remote computer and (optionally) to allow the remote computer to authenticate the user.
Note: This article will only consider OpenSSH.
Contents
SSH without passwords
- Step 1: Generate keys (public and private) and leave passphrase blank if you want password-less logins:
ssh-keygen # ~OR~ ssh-keygen -t dsa # ~OR~ ssh-keygen -t rsa -b 4096 -f /home/bob/my-key
- Step 2: Copy public key to remote server (Important: Only the public key!):
scp ~/.ssh/id_dsa.pub username@remote-host:.ssh/authorized_keys # ~OR~ ssh-copy-id -i ~/.ssh/id_rsa.pub username@remote-host
- Step 3: Set directory/file permissions (if not already set):
chmod 0700 ~/.ssh chmod 0600 ~/.ssh/authorized_keys
- Step 4: Now, SSH into your remote server (password will be required the first time):
ssh username@remote-host
That's it! You are now free to log into your remote server without entering a password. This is useful for automating file transfers. However, it must be used with care. If not executed properly, it is a potential security risk.
Using SSH private keys
For illustration purposes, I will generate a pseudo-key by generating 512 random characters to give you an idea of what a RSA private key should look like (note: You should never really create a private key less than 2048 bits):
$ echo "-----BEGIN RSA PRIVATE KEY-----" && openssl rand -base64 512 && echo -e "-----END RSA PRIVATE KEY-----\n" -----BEGIN RSA PRIVATE KEY----- AgaLRL9vUvHb736UVEavYIgpDJywdAvy+Y8/PGnS2aXbr1JzRXsvmoufcYpdJev+ 9E2XigSgoEuP3eDH4lRCtYRVuSqN7jUVJT26KBQbC34qw72mrfcVoW5H442l2oGF oOcWTcRz0F4R0LKbCecx7tGgzAW/XOVocmcC4CsEIrA+hmUkk9sXO/VD7eV6dP5D d3k3bqoDI4VEkhpavKSTRnoDBrl33tiz43vyiQUegPjZVkg+jOI7fyZL2hElQea2 o+KjEFfr4a1ZJs/58XitoCcHb7vaFX4PGNDuveBchFKmeWROuMxHalBVbV/sZVr4 bJYfNHTHHr4rNjQdf5cO9wnzIhC1hsutxZWPEj9JF3X+BVtAgKVS9Zbkh9BxSJJG cLWrmyqM7gRhE96ibHF6hGJ7jj0cf/pK8e8NVIVzD1jwvXAT7FeJHkKltoAKQ7LQ bC4d0b27jOccLpR6C4SU6zhSyWBnsoawiMfYR7HsEmLlOZW6fycrukFzi5wm/zpK r4YVIrzWHJzJbP+CIVvLUp8hv13OO3ozQo3tCNofpESV2/vYOGStDQtF9GVq53rS DWn2NAzT6X1IFtJlxQxG0CNsnNBAAZoOA3lgEPQqPzdoqKA/deS64oBH8j8CUSUp DQgaIxzVF1/2bKO3JoHKLaeui4vFIH7KT8ITS/FKoD8= -----END RSA PRIVATE KEY-----
- Save your private key to a file (let's call it "
my_private_key.txt
") and:
$ chmod 600 my_private_key.txt
- Now use that private key to log into your remote server (assuming, of course, that server has the matching key):
$ ssh -i /path/to/my_private_key.txt -l root <SERVER_IP> $ #~OR~ $ ssh root@<SERVER_IP> -i /path/to/my_private_key.txt
- Get the private key's "fingerprint":
$ ssh-keygen -lf /path/to/my_private_key.txt 2048 f6:a0:8c:99:ba:c2:31:36:1c:f2:5d:c5:da:37:27:b7 bob@hostname (RSA)
- Create a "signature":
$ echo -n 'this is my signature' |openssl sha1 -binary |\ openssl pkeyutl -sign -inkey my_private_key.txt -pkeyopt digest:sha1 > signature
Converting and verifying OpenSSH public keys
- First, generate a public/private key:
$ ssh-keygen -t rsa -b 2048 -f /home/bob/my-key
- Extract public key from private key:
$ openssl rsa -in my-key -pubout
- Note the difference between the above and the default public key
`ssh-keygen`
provides (i.e., the "my-key.pub
" file):
$ cat /home/bob/my-key.pub
- Or, get your public key in PEM format (only works with OpenSSH v5.6+):
$ ssh-keygen -f my-key.pub -e -m pem
- Check the integrity of your public key:
$ sed -e 's/ssh-rsa //' ~/.ssh/id_rsa.pub|awk '{print substr($1,1,76)}'|openssl base64 -d|hexdump 00000000 00 00 00 07 73 73 68 2d 72 73 61 00 00 00 03 01 |....ssh-rsa.....| 00000010 00 01 00 00 01 01 00 a3 f3 03 a0 8b 08 df 93 ac |................| 00000020 34 19 6c 19 1b 1a b5 b7 bf 43 0e 41 2f be 33 9a |4.l......C.A/.3.| 00000030 3f 15 c0 91 8c 27 09 ba c5 |?....'...| 00000039
The above reads as such:
00 00 00 07 The length in bytes of the next field 73 73 68 2d 72 73 61 The key type (ASCII encoding of "ssh-rsa") 00 00 00 03 The length in bytes of the public exponent 01 00 01 The public exponent (usually 65537, as here) 00 00 01 01 The length in bytes of the modulus (here, 257) 00 a3 f3... The modulus
So the key has type RSA, and its modulus has length 257 bytes, except that the first byte has value "00", so the real length is 256 bytes (that first byte was added so that the value is considered positive, because the internal encoding rules call for signed integers, the first bit defining the sign). 256 bytes is 2048 bits.
SSH config file
Note: See the ssh_config (5) man page for details.
- Edit your SSH config file (
~/.ssh/config
) and add the following (example) lines:
# contents of $HOME/.ssh/config Host dev HostName dev.example.com Port 22321 User bob Host github IdentityFile ~/.ssh/github.key
Now you can simply type:
ssh dev
to SSH into that dev.example.com
remote host.
See: for more examples.
Making SSH even more secure
Note: All of the following settings will be implemented in your /etc/ssh/sshd_config
file.
- Disable SSH protocol 1. Make sure no lines reads
Protocol 1
. If so, change it to:
Protocol 2
- Enable key-based logins (see above for how to do this):
PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys
- Disable password-based logins (Only do this if you first enable key-based logins!):
PasswordAuthentication no
- Run on ports other than 22
Port 1717 # any free port above 1024
You will then need to point to this port when SSHing into your remote machine
ssh -p 1717 remote.machine
- Disable root logins (Very important!):
PermitRootLogin no
Disable / deny brute force attacks
The following iptables rules should deny almost all brute force attacks on your firewall's port 22 (SSH port):
iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 8 --rttl --name SSH -j DROP
Miscellaneous
User rights must follow the principle of least privilege. The restriction can be applied to a number of parameters: accessible commands or originating IP addresses. For example, in the authorized_keys file, you can authorize only connections from a specific network (for a given key):
from="192.168.1.*" ssh-ed25519 AAAA...
So, for this key, only connections from the 192.168.1.0/24
network will be accepted.
Supported escape sequences
Note: The following escapes are only recognized immediately after newline.
~. - terminate connection (and any multiplexed sessions) ~B - send a BREAK to the remote system ~C - open a command line ~R - Request rekey (SSH protocol 2 only) ~^Z - suspend ssh ~# - list forwarded connections ~& - background ssh (when waiting for connections to terminate) ~? - this message ~~ - send the escape character by typing it twice
Miscellaneous
- Create a custom SSH prompt:
$ echo 'Defaults passprompt="LAUNCH CODE: "' | sudo tee -a /etc/sudoers.d/launch_code
- Proxy Jump
- SSH into a backend host (using its private IP) via a bastion host:
$ ssh -i ~/.ssh/backend.pem ubuntu@<backend-private-ip> \ -o ProxyCommand="ssh -i ~/.ssh/bastion.pem -o ForwardAgent=yes -W %h:%p ubuntu@<bastion-public-ip>" #~OR~ $ ssh -i ~/.ssh/backend.pem ubuntu@<backend-private-ip> \ -o ProxyCommand="ssh -i ~/.ssh/bastion.pem -o ForwardAgent=yes ubuntu@<bastion-public-ip> 'nc %h %p'"
Another way to do the above is to use the proxy jump (-J
) option:
$ ssh-add ~/.ssh/backend.pem $ ssh-add ~/.ssh/bastion.pem $ ssh-add -L $ ssh -v -J ubuntu@<bastion-public-ip> ubuntu@<backend-private-ip>
Here we tell SSH to connect to the target host by first making a ssh connection to the jump host (aka "bastion") described by destination and then establishing a TCP forwarding to the ultimate destination from there. Multiple jump hops may be specified, separated by comma characters. This is a shortcut to specify a ProxyJump
configuration directive.
Finally, you can add something like the following to your ~/.ssh/config
file:
Host bastion HostName <bastion-public-ip> User ubuntu IdentityFile ~/.ssh/bastion.pem IdentitiesOnly yes ProxyCommand none TCPKeepAlive yes ServerAliveInterval 5 Host backend User ubuntu HostName <backend-private-ip> IdentityFile ~/.ssh/backend.pem ProxyCommand ssh bastion nc %h %p
With the above, you can now SSH "directly" to the backend (really proxying via the bastion host) with:
$ ssh backend
- Run a local Bash script on a list of remote hosts:
$ for i in $(seq 102 104); do ssh root@128.14.163.${i} "bash -s" < ./install_docker.sh; done
Todo
- Access your local subversion repository from the road
ssh -NfL 3690:127.0.0.1:3690 USER@64.3.10.24 -p6111
Then you can access the repository via
svn://127.0.0.1/YOUR-SVN-PATH
- Secure web traffic when traveling
ssh -D 9999 -p6111 USER@64.3.10.24
then go to Firefox's Preferences->Advanced->Network->Settings->Manual proxy settings with:
SOCKS Host: 127.0.0.1 Port: 9999 No proxy for: localhost, 127.0.0.1
See also
- SSH Filesystem (sshfs)
- Fish protocol
- rsync