The OpenSSH secure shell, ssh, provides the necessary client/server security plumbing, to allow shell execution on a remote machine. ssh can be used interactively, as per a normal shell, or to run one off commands, for example:

$ ssh ben@wookie.local uname -a
ben@wookie.local's password:
Linux wookie.local 3.14-1-686-pae #1 SMP Debian 3.14.7-1 (2014-06-16) i686 GNU/Linux

Hot tip: the w command is gem for showing users currently logged in

$ w -f
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
ben      tty2     :0               17:05   10:36m 16:53  50.97s /usr/bin/evince /home/ben/podcasts/redhat/Docs/RH124-RHEL7.pdf

User ben logged into virtual console 2 (tty2) via a graphical login (:0) at about 5PM. OK, lets access a couple of the virtual terminals:

Ctrl + Alt + F2 Ctrl + Alt + F3

Return to the desktop environment (GNOME) with this combination: Ctrl + Alt + F1

# w -f
 20:38:31 up 10:40,  3 users,  load average: 0.26, 0.20, 0.25
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
ben      tty2     :0               17:05   10:40m 17:00  54.07s -bash
ben      tty3                      20:37   30.00s  0.34s  0.30s vim /home/ben/git/scripts/linux/bash/.bashrc
ben      tty4                      20:38   15.00s  0.02s  0.02s -bash

Virtual terminals 3 (tty3) and 4 (tty4) are now active, by account ben, editing something with Vim in one session, and a Bash shell in the other session.

Lets fire up some psedudo terminals, by making a local SSH connection. First, ensure your local openssh server is running, using your process manager (e.g. systemd):

# systemctl start sshd.service
# systemctl status sshd.service
● sshd.service - OpenSSH server daemon
   Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2016-07-17 20:41:12 AEST; 39s ago
     Docs: man:sshd(8)
           man:sshd_config(5)
 Main PID: 9880 (sshd)
   CGroup: /system.slice/sshd.service
           └─9880 /usr/sbin/sshd

Jul 17 20:41:12 think.local systemd: Starting OpenSSH server daemon...
Jul 17 20:41:12 think.local systemd: sshd.service: PID file /var/run/sshd.pid not readable (yet?) after start: No such f...rectory
Jul 17 20:41:12 think.local sshd: Server listening on 0.0.0.0 port 22.
Jul 17 20:41:12 think.local sshd: Server listening on :: port 22.
Jul 17 20:41:12 think.local systemd: Started OpenSSH server daemon.

The ssh server is running, hook up a client to it:

# ssh george@localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256:BAhnNz1IRWcdfNjp6WwqmDnQP9Z4oCwT01Sb0BPsuv8.
ECDSA key fingerprint is MD5:39:f7:ec:09:90:87:70:86:02:9b:71:7d:74:34:8e:aa.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
george@localhost's password:
Last login: Sun Jul 17 20:38:13 2016

And, voila, psueduo terminal 5 is occupied by the george’s new ssh session, he’s been idle for 22 seconds:

$ w -f
 20:42:29 up 10:44,  4 users,  load average: 0.25, 0.24, 0.25
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
ben      tty2     :0               17:05   10:44m 17:27   1.11s /opt/google/chrome/chrome --type=renderer --enable-features=Incident
ben      tty3                      20:37    4:28   0.34s  0.30s vim /home/ben/git/scripts/linux/bash/.bashrc
ben      tty4                      20:38    4:13   0.02s  0.02s -bash
george   pts/5    127.0.0.1        20:42   22.00s  0.02s  0.00s -bash

SSH key based authentication

Don’t like passwords? Enter public key authentication, which ssh supports. The private key is treated as the authentication credential, and like a password should be kept secure. The public key is copied to the target host, intended to be logged into, and is used to verify the private key. The SSH server now has everything it needs in order to craft a challenge, only (feasibly) answerable with the private key.

ssh-keygen will create you a both a private key ~/.ssh/id_rsa and a public key ~/.ssh/id_rsa.pub

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ben/.ssh/id_rsa):
/home/ben/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ben/.ssh/id_rsa.
Your public key has been saved in /home/ben/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:AneBy9it1334Kx2VO0t3C1334i525pjFit/OR9Jf5V0 ben@think.local
The key's randomart image is:
+-------+
|                 |
|               . |
|             .o  |
|     o     . .ooE|
|    o +.S . o.*.=|
|   . *.X.o.o.= =o|
|    ooB =.+.= .  |
|   o.=o+ o =     |
|  . *++o. .      |
+---------+

By default keys are stored in ~/.ssh, with permissions 600 on the private key, and 644 on the public, e.g:

$ ls -l
total 12
-rw-------. 1 ben ben 1679 Jul 17 21:05 id_rsa
-rw-r--r--. 1 ben ben  397 Jul 17 21:05 id_rsa.pub
-rw-r--r--. 1 ben ben  711 Jul 17 20:53 known_hosts

ssh-copy-id conveniently takes care of tranferring the public key to the remote location. The -i switch defines the file to be used to locate the identity.

$ ssh-copy-id -i ~/.ssh/id_rsa.pub ben@wookie.local
/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
sign_and_send_pubkey: signing failed: agent refused operation
ben@wookie.local's password:

Without the -i switch, ssh-copy-id will scan for identities using ssh-add

$ ssh-copy-id wookie.local
/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
sign_and_send_pubkey: signing failed: agent refused operation
ben@wookie.local's password:

Number of key(s) added: 1

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

Which, unless you’ve registered the identity with ssh-add will likely fail, like so:

$ ssh ben@wookie.local date
sign_and_send_pubkey: signing failed: agent refused operation
ben@wookie.local's password:

If you want to go down the ssh-add path, simply refresh its cache by running it:

$ ssh-add -l
2048 SHA256:ZqbEy9it9SXRKx2VO0t3CasIOi525msFjt/OR9Jf5V0 ben@think.local (RSA)
$ ssh-add
Identity added: /home/ben/.ssh/id_rsa (/home/ben/.ssh/id_rsa)
$ ssh-copy-id -f wookie.local
Number of key(s) added: 2
Now try logging into the machine, with:   "ssh 'wookie.local'"
and check to make sure that only the key(s) you wanted were added.

And you should be cooking with gas:

$ ssh ben@wookie.local date +%A
Sunday

SSH service configuration

Configuration for sshd lives in /etc/ssh/sshd_config. After modifying sshd_config ensure you bounce the sshd daemon using your friendly service manager (e.g systemd):

# systemctl restart sshd

Some useful options for hardening a default installation include:

Preventing root login

The root account is powerful, making it a good choice for a remote adversary.

PermitRootLogin no

If this is not feasible consider enforcing key-based authentication for the root user:

PermitRootLogin without-password

Disabling password (i.e. not PKI) authentication

Not only is key based authentication a great convenience, RSA keys are much longer and complex than an average password.

PasswordAuthentication no

Whitelist users and/or groups

AllowUsers ben tom alice bob
AllowGroups sysadmin dbadmin webadmin

Blacklist users and/or groups

Conversely, users and/or groups can be explicitly denied.

DenyUsers apache postgres jenkins
DenyGroups developers testers managers

You may be thinking in what order are the white and black lists applied: DenyUsers => AllowUsers => DenyGroups => AllowGroups

Changing the default port (22)

Change the default port binding to something other than 22:

Port 1337

Reducing the login grace period of 2 minutes

The time window a new session has to successfully authenticate, before being disconnected. Default is 2 minutes (2m), here we set it to 30 seconds.

LoginGraceTime 30

Reviewing the authentication log for failed authentication attempts

By default logs will route through the syslog facility of the distribution (for me Redhat based), and into /var/log/secure.

# tail -n 3 /var/log/secure
Jul 30 07:34:36 centosbox sshd: Invalid user monkey from ::1
Jul 30 07:34:36 centosbox sshd
Jul 30 07:34:36 centosbox sshd

An interesting project called (http://denyhosts.sourceforge.net/) parses this log periodicly, and based on failed attempts and frequency patterns, will automatically populate the /etc/hosts.deny.