More Secure SSH Connections
If you need remote access to a machine, you'll probably use SSH, and for a good reason. The secure shell protocol uses modern cryptography methods to provide privacy and confidentiality, even over an unsecured, unsafe network, such as the Internet. However, its very availability also makes it an appealing target for attackers, so you should consider hardening its standard setup to provide more resilient, difficult-to-break-into connections. In this article, I cover several methods to provide such extra protections, starting with simple configuration changes, then limiting access with PAM and finishing with restricted, public key certificates for passwordless restricted logins.
Knock for SSH
Trying to attack your machine will be harder if the would-be invader cannot
even find a possible SSH door. The methods shown in this article are compatible with
the port-knocking technique I wrote about in a previous article ("Implement
Port-Knocking Security with knockd", January 2010), so I won't
knockd configuration here. By using all techniques together,
attackers will have an even harder time getting to your machine (where all the other
measures shown in this article will be waiting), because they won't even be able to
start trying to attack your box.
Where Is SSH?
As defined in the standard, SSH uses port 22 by default. This implies
that with the standard SSH configuration, your machine already has a nice
target to attack. The first method to consider is quite simple—just
change the port to an unused, nonstandard port, such as 22022. (Numbers
above 1024 are usually free and safe, but check the Resources at the end
of this article just to avoid possible clashes.) This change won't affect
your remote users much. They will just need to add an extra parameter to
their connection, as in
ssh -p 22022
yes, this kind of change lies fully in what's called "security through
obscurity"—doing things obscurely, hoping that no one will get wise
to your methods—which usually is just asking for problems. However,
it will help at least against script kiddies, whose scripts just try
to get in via port 22 instead of being thorough enough to try to scan
your machine for all open ports.
In order to implement this change, you need to change the
/etc/ssh/sshd_config file. Working as root, open it with an editor,
look for a line that reads "Port 22", and change the 22 to whatever
number you chose. If the line starts with a hash sign (#), then
remove it, because otherwise the line will be considered a comment. Save
the file, and then restart SSH with
With some distributions, that could be
instead. Finally, also remember to close port 22 in your firewall and to
open the chosen port so remote users will be able to access your server.
While you are at this, for an extra bit of security, you also could add or
edit some other lines in the SSH configuration file (Listing 1). The
Protocol line avoids a weaker, older version of the SSH protocol. The
LoginGraceTime gives the user 30 seconds to accomplish a login. The
MaxAuthTries limits users to three wrong attempts at entering
the password before they are rejected. And finally,
forbids a user from logging in remotely as root (any attacker who managed
to get into your machine still would have to be able to break into the
root account; an extra hurdle), so would-be attackers will have a harder
time at getting privileges on your machine.
Listing 1. These little SSH configuration changes can add a bit of security
Port 22022 Protocol 2 LoginGraceTime 30 MaxAuthTries 3 PermitRootLogin no
Be sure to restart the SSH service dæmon after these changes
/etc/init.d/sshd restart does it) and, for now, you already
to add a bit of extra safety (but not much really), so let's get down to
adding more restrictions.
Who Can Use SSH?
Your machine may have several servers, but you might want to limit
remote access to only a few. You can tweak the sshd_config file a
bit more, and use the
DenyGroups parameters. The first one,
AllowUsers, can be
followed by a list of user names (or even patterns, using the common *
and ? wild cards) or user@host pairs, further restricting access to
the user only from the given host. Similarly,
a list of group name patterns, and login is allowed only for members of
those groups. Finally,
DenyGroups work likewise,
but prohibit access to specific users and groups. Note: the priority order
for rules is
DenyUsers first, then
AllowGroups, so if you explicitly
disallow users from
DenyUsers, no other rules will allow
them to connect.
For example, a common rule is that from the internal network, everybody
should be able to access the machine. (This sounds reasonable; attacks
usually come from outside the network.) Then, you could say
that only two users, fkereki and eguerrero, should be able
to connect from the outside, and nobody else should be able to connect. You
can enable these restrictions by adding a single line
*:192.168.1.*,fkereki,eguerrero to the SSH configuration file and
restarting the service. If you wanted to forbid jandrews from remote
connections, an extra
DenyUsers jandrews would be needed. More
specific rules could be added (say, maybe eguerrero should
be able to log in only from home), but if things start getting out of hand
with too many rules, the idea of editing the ssh configuration files and
restarting the server begins to look less attractive, and there's a better
solution through PAM, which uses separate files for security rules.
The PAM Way
If you google for meanings of PAM, you can find several definitions, ranging from a cooking oil spray to several acronyms (such as Power Amplitude Modulation or Positive Active Mass), but in this case, you are interested in Pluggable Authentication Modules, a way to provide extra authentication rules and harden access to your server. Let's use PAM as an alternative solution to specify which users can access your server.
From a software engineering viewpoint, it would just be awful if each and every program had to invent and define and implement its own authentication logic. How could you be certain that all applications did implement the very same checks, in the same way, without any differences? PAM provides a way out; if a program needs to, say, authenticate a user, it can call the PAM routines, which will run all the checks you might have specified in its configuration files. With PAM, you even can change authentication rules on the fly by merely updating its configuration. And, even if that's not your main interest here, if you were to include new biometrics security hardware (such as fingerprint readers, iris scanners or face recognition) with an appropriate PAM, your device instantly would be available to all applications.
PAMs can be used for four security concerns: account limitations (what the users are allowed to do), authorization (how the users identify themselves), passwords and sessions. PAM checks can be marked optional (may succeed or fail), required (must succeed), requisite (must succeed, and if it doesn't, stop immediately without trying any more checks) and sufficient (if it succeeds, don't run any more checks), so you can vary your policies. I don't cover all these details here, but rather move on to the specific need of specifying who can (or cannot) log in to your server. See the PAM, PAM Everywhere sidebar for a list of some available modules.
PAM, PAM Everywhere
Although there is no "official" list of PAMs, most distributions are likely to include the following:
pam_access: allows or denies access according to the file /etc/security/access.conf.
pam_cracklib: checks passwords against dictionaries.
pam_debug: used for testing only.
pam_deny: always denies access.
pam_echo: displays the contents of a file.
pam_env: sets or unsets environment variables.
pam_exec: lets you run an external command.
pam_group: grants group memberships to the user.
pam_lastlog: shows the date and time of the user's last log in.
pam_ldap: allows authentication against an LDAP server.
pam_limits: lets you set system resource limits, through the file /etc/security/limits.conf.
pam_listfile: an alternative to pam_access, with some extra options.
pam_mail: checks if the user has pending mail.
makein a given directory.
pam_motd: displays the "message of the day" file, usually /etc/motd.
pam_nologin: blocks all logins should file /etc/nologin exist.
pam_permit: always allows access.
pam_pwcheck: checks passwords for strength.
pam_pwhistory: checks new passwords against recently used ones to avoid repetition.
pam_rootok: usually is included in /etc/pam.d/su as a "sufficient" test so root can act as any other user without providing a password.
pam_selinux: sets the default security context for SELinux.
pam_sepermit: allows or denies login depending on SELinux state.
pam_shells: allows access only if the user's shell is listed in the file /etc/shells.
pam_succeed_if: checks for account characteristics, such as belonging to a given group.
pam_tally: just keeps count of attempted accesses and can deny access if too many attempts fail.
pam_time: restricts access based on rules in the file /etc/security/time.conf.
pam_umask: lets you set the file mode creation mask (think
umask) for newly created files.
pam_unix (or pam_unix2): provides classical UNIX-style authentication per the /etc/passwd and /etc/shadow files.
pam_userdb: authenticates the user against a Berkeley database.
pam_warn: records logs in the system logs.
pam_wheel: provides root access only to members of group wheel.
File locations vary, but you can check /usr/lib/security or
/lib/security (or read lib64 for lib, for 64-bit Linux) to
see what modules you actually have. For more information on each module,
man name.of.the.module, but don't try to execute them from the
command line, for they can't be run that way.
PAM configurations are stored in /etc/pam.d, with a file for each
command to which they apply. As root, edit /etc/pam.d/sshd, and add an
account required pam_access.so line after all the
so it ends up looking like Listing 2. (Your specific version of the
file may have some different options; just add the single line to it,
and that's it.) You'll also have to modify the sshd configuration
file (the same one that you modified earlier) so it uses PAM; add a
line to it, and restart the sshd dæmon.
Listing 2. Adding
pam_access.so to the
account PAM checks lets you specify which users have SSH access to your
account required pam_unix2.so account required pam_access.so auth required pam_env.so auth required pam_unix2.so auth required pam_nologin.so password requisite pam_pwcheck.so nullok cracklib password required pam_unix2.so use_authtok nullok session required pam_limits.so session required pam_unix2.so session optional pam_umask.so
account part is what is important here. After using the standard UNIX
methods for checking your password (usually against the files /etc/passwd
and /etc/shadow), it uses the module
pam_access.so to check if
the user is in a list, such as shown in Listing 3. Both
required, meaning that the user must pass both checks
in order to proceed. For extra restrictions, you might want to
pam_listfile, which is similar to
provides even more options, and
pam_time, which lets you fix time
restrictions. You also would need to add extra
account lines to the
You need to edit /etc/security/access.conf to specify which users
can access the machine (Listing 3). Each line in the list starts
with either a plus sign (login allowed) or a minus sign (login disabled),
followed by a colon, a user name (or ALL), another colon and a host
(or ALL). The
pam_access.so module goes down the list in order, and
depending on the first match for the user, it either allows or forbids
the connection. The order of the rules is important. First, jandrews
is forbidden access, then everybody in the internal network is allowed
to log in to the server. Then, users fkereki and eguerrero
are allowed access from any machine. The final
-:ALL:ALL line is
a catchall that denies access to anybody not specifically allowed to
log in in the previous lines, and it always should be present.
Listing 3. The file /etc/security/access.conf specifies which users have access and from which hosts.
-:jandrews:ALL +:ALL:192.168.1. +:fkereki:ALL +:eguerrero:ALL -:ALL:ALL
Note that you could use this configuration for other programs and services (FTP, maybe?), and the same rules could be applied. That's an advantage of PAM. A second advantage is that you can change rules on the fly, without having to restart the SSH service. Not messing with running services is always a good idea! Using PAM adds a bit of hardening to SSH to restrict who can log in. Now, let's look at an even safer way of saying who can access your machine by using certificates.