
How to Set Up and Use SSH
Step 1 — Administrative Work
Configure the Server and Client
Configure the service by modifying
/etc/ssh/ssh_config
and
/etc/ssh/sshd_config
on all hosts as follows:
/etc/ssh/ssh_config
is used by local users
connecting from this host
to some remote server.
Outbound connections.
Put the following in that configuration file:
# Only support the more secure SSH2 protocol. # Recent versions of OpenSSH default to SSHv2 only, # but there's no harm in explicitly configuring this. Protocol 2 # Allow the user to tunnel X applications through SSH. # Read "man ssh_config" first. Yes, there is a security # issue. However, very carefully read its description. # It is an additional problem if the attacker is able # to subvert Unix file system security on one of the # hosts. But if the attacker is able to do that, # I have far worse problems that must be fixed NOW. ForwardX11 yes
/etc/ssh/sshd_config
controls how this server handles connection requests
from remote users.
Inbound connections.
Put the following in that configuration file:
# Only support the more secure SSH2 protocol. # Recent versions of OpenSSH default to SSHv2 only, # but there's no harm in explicitly configuring this. Protocol 2 # If you list users allowed to connect via SSH, then # they will be the only users allowed to authenticate # to SSH. THIS IS OPTIONAL, you do NOT need to include # an AllowUsers line! If you do, these will be the # only users allowed to authenticate. If you do not # include this line, then ALL users other than root will # be allowed to authenticate. Also see "AllowGroups", # "DenyUsers", and "DenyGroups" in "man sshd_config". AllowUsers joe jane .... # Only accept authentication via cryptographic # challenge-response. The remote client must have # cryptographic keys. PasswordAuthentication no # Do not allow root login -- first login as an ordinary # user, then su to root PermitRootLogin no
Collect host public keys
You could collect host public keys with the following command on a small network. The parameters need to list every SSH server as both simple hostname and fully-qualified domain name, as well as their IP addresses, so this could quickly lead to a lot of typing:
# ssh-keyscan host1 host1.example.com 10.0.0.1 fc00::2e27:d7ff:fec5:d37b \ host2 host2.example.com 10.0.0.2 fc00::213:3bff:fe12:6fa9 \ [.... and so on ....] \ hostN hostN.example.com 10.0.0.N fc00::ba27:ebff:feac:bf18 > /etc/ssh/ssh_known_hosts
For a solution that scales well to a typical organization,
first create a file, let's say /tmp/hostlist,
containing all hostnames, all fully-qualified domain names,
and all IP addresses, one item per line,
for all of your SSH servers.
You might be able to generate such a list with the following
command pipeline, if your name server allows
zone transfers from your workstation:
$ dig yourdomain.com @nameserver.yourdomain.com AXFR | \ awk '/\<A\>|\<AAAA\>/ {print $1; print $NF}' | \ sed 's/\.$//' > /tmp/hostlist
That list will include every host,
not just the SSH servers.
However, ssh-keyscan
will quickly give up on
hosts that don't respond.
Then run the following command:
# ssh-keyscan -f /tmp/hostlist > /etc/ssh/ssh_known_hosts
Once you have generated
/etc/ssh/ssh_known_hosts,
securely copy it to every host on which SSH will
be used, even the workstations that will only be SSH clients.
You will generate new (and different!) SSH keys if you reinstall the operating system. That means that clients will refuse to connect to that freshly built SSH server, because it will look like it is a different host trying to masquerade. You will need to at least update that host's key. A simple yet safe procedure would be:
-
On some host, edit
/etc/ssh/ssh_known_hosts
and remove the entry for the freshly rebuilt server. -
Re-run the above
ssh-keyscan
command to create an updatedssh_known_hosts
file with the public key for the newly rebuilt server. -
Securely copy the new
/etc/ssh/ssh_known_hosts
file into place. Note — since we have disabledroot
login over SSH, we have also disabledroot
use ofscp.
You must copy the files to all servers as a user, and thensu
toroot
on each server to install them.
-
At this point you may wish that you had
thought to save a backup copy of those key
files before starting...
If you did, be careful about file permissions!
The public keys are in files named
*key.pub
and must be mode 644 or 444. The private keys are in files named*key
and must be mode 600 or 400.
Protect the user accounts
Now would be a good time to disable all unneeded accounts and make sure that all accounts still in use have strong passwords. Attackers are trying constantly to guess passwords.
The patterns of logins and passwords used in these guessing attacks can be interesting. It certainly would be a very bad idea for even an unprivileged user account to have a commonly used password.
Disable unused accounts
Lock the password, meaning replace the password hash
in /etc/shadow
with an invalid string so that
there is no such thing as a valid password for that
user.
On Linux:
# passwd -l username
On other Unixes (BSD, Solaris) you may need to
carefully edit
/etc/shadow
(Solaris) or
/etc/master.passwd
(BSD)
and replace the password hash for the user with
some other string.
Simply inserting LOCKED
as the initial part
of the string will suffice.
From:
cromwell:$2a$06$5Fkvg4EFsRCk....
to:
cromwell:LOCKED$2a$06$5Fkvg4EFsRCk....
On BSD you then need to run pwd_mkdb
—
don't omit this crucial step!
For defense in depth, also replace that user's login shell
with something that exits immediately and is not a shell.
On Linux:
# chsh -s /bin/false username
On Solaris:
# chsh -u username
and then specify /bin/false
On BSD:
# chpass -s /usr/bin/false username
Set file permissions, especially in NFS situations
At some point, SSH started refusing to work if the user's SSH key storage area was not securely configured. That's a good idea, although it tends not to explain why it's failing and can be frustrating to debug.
The directory ~/.ssh
should be mode 700, and the files
in there containing private keys
(id_dsa
,
(id_ecdsa
,
(id_ed25519
,
id_rsa
)
should be mode 600.
Also, all the files must not be
group-writeable.
Do something like this:
# chmod -R g-w /home/*/.ssh
The SSH daemon must be able to read those files,
and since it is owned by root
that normally
is no problem.
However, root
is "squashed" to
nobody
by default on NFS mounts.
So, make sure that NFS exports of user home directories
are shared with the no_root_squash
option.
Start an SSH agent at login time
Start an SSH agent at login time
if your display manager does not already do this.
Some, like Gnome's gdm
display manager,
tend to do this.
Others, like KDE's kdm
display manager,
tend not to.
To test if this is needed, login to the graphical desktop as a user and run this command:
$ ssh-add -l
If an SSH agent is running, you will see the following and you don't need to do anything additional to start an agent for the user:
The agent has no identities.
However, if you see the following, you need to make a change to get an agent started for the users:
Could not open a connection to your authentication agent.
If you need to make a change to get an SSH agent running at login time, modify your display manager configuration. Depending on the display manager, this may take some investigation and experimentation.
For example, on Linux with KDE's display manager,
the configuration is stored in
/usr/share/config/kdm/.
On OpenBSD, it is
/usr/local/share/config/kdm/.
The file Xsession
originally contains a block
reading as follows:
case $session in "") exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session." ;; failsafe) exec xterm -geometry 80x24-0-0 ;; custom) exec $HOME/.xsession ;; default) exec /usr/bin/startkde ;; *) eval exec "$session" ;; esac
Change that code to the following, with the change highlighted:
case $session in "") exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session." ;; failsafe) exec xterm -geometry 80x24-0-0 ;; custom) exec $HOME/.xsession ;; default) exec ssh-agent /usr/bin/startkde ;; *) eval exec "$session" ;; esac
If that doesn't work on OpenBSD, do this:
# cd /usr/local/bin # mv startkde4 startkde4-real # cat > startkde4 << EOF #!/bin/sh /usr/bin/ssh-agent /usr/local/bin/startkde4-real EOF # chmod 755 startkde4
KDE may be changing its logic,
at least in the
Mageia
distribution.
I had to instead modify /etc/X11/wmsession.d/01KDE
to refer to a simple shell script wrapper.
For some reason I was unable to put the
exec ssh-agent startkde
line directly in that file, so instead I have:
NAME=KDE ICON=kde-wmsession.xpm DESC=The K Desktop Environment # EXEC = /usr/bin/startkde EXEC = /usr/local/bin/start-kde SCRIPT: # exec /usr/bin/startkde exec /usr/local/bin/start-kde
The script /usr/local/bin/start-kde
looks like this:
#!/bin/sh /usr/bin/ssh-agent /usr/bin/startkde
If you are stuck with CDE and its dtlogin
interface (decent technology for about 1990), modernize.
Seriously.
If you must live in the past,
you need to make a change within the large and complicated file
/usr/dt/bin/Xsession.
Look for a block like the below,
and insert the highlighted code.
# # Session startup clients and args # if [ "$SESSIONTYPE" = "altDt" ]; then dtstart_session[0]="/usr/bin/ssh-agent $SDT_ALT_SESSION" dtstart_hello[0]="$SDT_ALT_HELLO" else dtstart_session[0]="/usr/bin/ssh-agent $DT_BINPATH/dtsession" dtstart_hello[0]="$DT_BINPATH/dthello &" fi dtstart_session[1]="$HOME/.xsession" dtstart_session[2]="$HOME/.x11start"
Log out, log back in, and run this command:
$ ssh-add -l
You should see a message that the SSH agent has no identities. That's fine, that's what we were looking for. If you instead see a message that the SSH agent cannot be contacted, then you got something wrong. Either you didn't really log all the way out and back in again, or (more likely, I would hope) you didn't get things set up correctly.
Install Keychain
Even with the above changes, the KDM display manager may still ignore what you want. It was able to get it right, once. Here is what we see on OpenBSD with KDE 3.5.10:
% pstree | egrep -C 4 'startkde|ssh-agent' |-+= 16874 root /usr/local/bin/kdm | |-+= 07660 _x11 /usr/X11R6/bin/X vt05 :0 -auth /etc/X11/xdm/authdir/A:0-r7LN2x (Xorg) | | \--- 23894 root X: [priv] (Xorg) | \-+- 26766 root kdm: :0 (kdm) | \-+= 17300 cromwell /bin/sh /usr/local/bin/startkde | |--= 09760 cromwell /usr/bin/ssh-agent /usr/local/bin/startkde | \--- 02140 cromwell kwrapper ksmserver |-+= 31281 cromwell kdeinit: kdeinit Running... (kdeinit) | |--- 09133 cromwell kdeinit: kdeinit: klauncher --new-startup (kdeinit) | |--- 05026 cromwell kdeinit: kdeinit: kwin -session 101dd1d820716f00013....
And on CentOS with KDE 3.5.4 and gdm 2.16.0:
% pstree -u | egrep -C 4 'startkde|ssh-agent' |-eggcups(cromwell) |-events/0 |-gconfd-2(cromwell) |-gdm-binary---gdm-binary-+-Xorg | `-tcsh(cromwell)-+-ssh-agent | `-startkde---kwrapper |-gdm-rh-security |-gpg-agent(cromwell) |-hald(haldaemon)---hald-runner(root)-+-hald-addon-acpi(haldaemon) | |-hald-addon-keyb(haldaemon)
But on Mageia with kdm 4.10.2:
% pstree -u | egrep -C 4 'startkde|ssh-agent' | |-xterm---luit---tcsh-+-egrep | | `-pstree | `-xterm---luit---tcsh---su(root)---bash |-kdm-+-X | `-kdm---startkde(cromwell)---kwrapper4 |-kglobalaccel(cromwell) |-kmix(cromwell)---{kmix} |-knotify4(cromwell)---{knotify4} |-konsole(cromwell)-+-tcsh---vim---{vim}
The solution is to install the keychain package.
It works by creating files named
your-hostname-csh
and
your-hostname-sh
which can set environment variables specifying the PID
and socket used by the long-running SSH agent.
New shells or the KDE desktop are started plugged into
those agents.
It also handles the GnuPG agent, and,
in case it's installed, the fish
shell:
% ls -la .keychain total 76 4 drwx------ 2 cromwell cromwell 4096 Jun 14 15:02 ./ 56 drwxr-xr-x 205 cromwell cromwell 57344 Jun 16 15:26 ../ 4 -rw------- 1 cromwell cromwell 82 Jun 14 15:02 your-hostname-csh 0 -rw------- 1 cromwell cromwell 0 Jun 14 15:02 your-hostname-fish 4 -rw------- 1 cromwell cromwell 112 Jun 14 15:02 your-hostname-sh % more .keychain/* :::::::::::::: .keychain/your-hostname-csh :::::::::::::: setenv SSH_AUTH_SOCK /tmp/ssh-WeBckDhIvaF7/agent.11515; setenv SSH_AGENT_PID 2269; :::::::::::::: .keychain/your-hostname-fish :::::::::::::: set -e SSH_AUTH_SOCK; and set -x -U SSH_AUTH_SOCK /tmp/ssh-WeBckDhIvaF7/agent.11515 set -e SSH_AGENT_PID; and set -x -U SSH_AGENT_PID 2269 :::::::::::::: .keychain/your-hostname-sh :::::::::::::: SSH_AUTH_SOCK=/tmp/ssh-WeBckDhIvaF7/agent.11515; export SSH_AUTH_SOCK; SSH_AGENT_PID=2269; export SSH_AGENT_PID;
At least on Mageia, the package adds these two files:
/etc/profile.d/99keychain.csh
/etc/profile.d/99keychain.sh
If these detect the directory
~/.keychain
,
they check to see if an SSH agent is running.
If not, it starts one and prompts you for your passphrase.
Either on the command-line or with a graphical pop-up
depending on your interface
If you don't get those two system-wide files,
put the appropriate code in your shell startup file.
I use tcsh
so I have this in my
~/.cshrc
file (the shell reads that
if there is no .tcshrc
):
# Things needed only in interactive shells if ( "$?prompt2" == "1" ) then eval `keychain --eval id_rsa id_dsa id_ecdsa id_ed25519` if (-f $HOME/.keychain/$host-csh) then source $HOME/.keychain/$host-csh endif endif
Re-start the SSH service
Re-start the SSH service however your version of *nix does this. You may find that the following signals your SSH daemon to re-read its configuration while continuing to run:
# pkill -HUP sshd
If not, consult the below table or your system documentation. If you reconfigured your display manager, also restart that.
Linux |
# systemctl restart sshd
|
old Linux |
# /etc/init.d/sshd restart
|
Solaris and similar |
# /etc/init.d/sshd stop # /etc/init.d/sshd start |
BSD |
# /etc/rc.d/sshd restart
|
Be careful — some of the above commands may terminate existing SSH connections. If you are trying to do this work remotely, it may leave you locked out of the system!
Now your system is considerably more secure.
- It only uses the more secure SSH2 protocol.
- If users had been asked, "Is the following the correct fingerprint for the server's public key?", they would have had no idea and simply answered "Yes". You have enabled the hosts to make the correct and cautious decision, so users are not asked a question they cannot really answer.
-
Even someone who knows your
root
password cannot authenticate asroot.
You must first authenticate as some user and then transition toroot
with thesu
command. -
And even if someone knew both your UNIX login password
and your
root
password, they still could not becomeroot
remotely! Password authentication is disabled for SSH. So, we need to set up public-key cryptographic authentication for every user. That's done in a later step, first we need to consider access control.