
How to Set Up and Use SSH
Step 2 — Access Control
SSH Access Control
If you are reachable from the Internet, attackers using hosts all over the world will be making SSH connections and trying to guess passwords.
You might want to consider some access control,
in which hosts or blocks of IP addresses in specific lists
are blocked out or are the only ones allowed to connect.
Be aware that this can only provide limited protection,
the recommendations on
the previous page
for using cryptographic
authentication and disabling root
access are
crucial first lines of defense.
If you keep your server patched and configured carefully, and you have strong passwords and SSH key pass phrases on all accounts, you can feel reasonably comfortable. You might want to use access control just to more quickly frustrate the attackers. They will move on to what appear to be weaker targets and they won't clutter up your logs. However, if your primary interest is in reducing the number of attempted captured in your logs, you will find that most forms of access control will not accomplish that. Packet filtering is the only method that won't show up in your logs, unless, of course, you specifically configure it to do so.
SSH access control applies in the sequence shown in the following diagram. Notice the multiple opportunities for Syslog messages: from the kernel, from the SSH daemon (at multiple times during the connection or attempted connection), from the PAM modules, and from the shell or command executed through the SSH connection.

-
Packet filtering within IP module of kernel
(e.g.,
iptables
, IP Filter,pf
, etc)- This is obviously the first step — packet filtering happens in the kernel before the datagram can be passed to the user-space service.
-
TCP Wrapper control with
/etc/hosts.allow
and /etc/hosts.deny-
Denied users will not be prompted for a
password,
there will be a timeout followed by a
message:
ssh_exchange_identification: Connection closed by remote host
-
Denied users will not be prompted for a
password,
there will be a timeout followed by a
message:
-
PAM and Allow/Deny rules in
/etc/ssh/sshd_config
- The point of PAM is to control what users have to do to authenticate. Type UNIX passwords? Offer Kerberos tickets? In what order and combination?
-
The point of the Allow/Deny rules for both
users and groups in
sshd_config
is to control which users and/or group members are allowed to authenticate, and possibly from which client hosts. - It doesn't really matter whether the PAM rules are applied before the Allow/Deny rules or vice-versa, because unless you specified something illogical, the result is the same — multiple ways to deny access.
-
The denied user will be prompted
multiple times for a password,
but even if they know the password they
will be refused with a vague message
similar to:
Permission denied (publickey,password,keyboard-interactive).
The syslog messages on the server should indicate why the connection was refused. Note that a single connection may generate multiple syslog messages, one from the SSH daemon and one from each PAM module it uses. Access control rules lead to more log messages, not less.
Packet filtering
This is a big topic. For Linux, see the Netfilter/iptables documents at netfilter.org.
You must be very careful with packet filtering rules,
as you can easily make your own server inaccessible.
But as an example of something you might find useful,
as long as you don't want to do certain legitimate things,
you could add these two iptables rules to Linux,
placing them near the top of the INPUT chain.
The rules use two modules, state
and recent,
the second of which is used for connection rate limiting:
# The "recent" module dynamically creates a list of IP addresses. # This rule creates a list named "SSH" containing those clients # that have sent a TCP/SYN packet to the SSH port. iptables -A INPUT -p tcp --dport ssh \ -m state --state NEW -m recent --name SSH --set # This rule specifies that if the client has attempted to establish # at least 5 SSH connection attempts within the past 120 seconds, the # packet is rejected with a TCP/RST packet, which is what happens # when you try to connect to a host not running that service at all. iptables -A INPUT -p tcp --dport ssh \ -m state --state NEW -m recent --name SSH \ --rcheck --seconds 120 --hitcount 5 \ --jump REJECT --reject-with tcp-reset
See the ipt_recent module web page for more details.
A big problem with automatically adding packet filtering rules is that you may keep yourself from doing something that you want to do. Packet filtering works down at the IP layer, and while it is aware of TCP/UDP things like ports and flags and even connection state, it has no clue about application-layer issues like SSH authentication failures. The above two rules would prevent anyone from making more than 5 connections every 120 seconds. Some times I do something like the following when I plan to edit a number of files, let's say anywhere under my home directory and within the collection of web pages. I expect this to take some time, and some interruptions will occur, so I can't reliably keep track of the list of files that need to be copied to the server when I'm done. No problem, I just do this:
% touch /tmp/TIMESTAMP .... edit lots of files under /home/cromwell and /var/www .... % foreach F ( `find /home/cromwell /var/www -type f -newer /tmp/TIMESTAMP` ) > scp $F server:$F > end .... or, if you use Bash or ksh for your interactive shell .... $ for F in `find /home/cromwell /var/www -type f -newer /tmp/TIMESTAMP` > do > scp $F server:$F > done
I would not be allowed to do that if the connection
rate throttling were in place!
Yes, I know, I probably should make a tar
file,
then copy it over and extract it, but I prefer to do it
the above way.
Some times many files have been changed,
so many SSH connections would be made for all those
scp
commands.
Other rules could be inserted before those rules to allow arbitrarily many fast connections from specified IP address blocks. This would be fine to allow connections within an organization, but it could not be used for users working from home or on the road (hotels, Internet cafes) as you could not predict their IP addresses.
# Allow many fast connections from inside, where we use the RFC 1918 # IP address blocks. These rules MUST appear BEFORE filtering rules. iptables -A INPUT -p tcp --dport ssh -s 10.0.0.0/8 --jump ACCEPT iptables -A INPUT -p tcp --dport ssh -s 172.16.0.0/12 --jump ACCEPT iptables -A INPUT -p tcp --dport ssh -s 192.168.0.0/16 --jump ACCEPT # Now throttle everyone else with those two rules shown earlier: iptables -A INPUT -p tcp --dport ssh \ -m state --state NEW -m recent --name SSH --set iptables -A INPUT -p tcp --dport ssh \ -m state --state NEW -m recent --name SSH \ --rcheck --seconds 120 --hitcount 5 \ --jump REJECT --reject-with tcp-reset
See the ipt_recent module web page for more details.
TCP Wrapper by example
To allow SSH access for only one block of IP addresses while allowing all connection requests on all other network services:
# cat /etc/hosts.allow sshd : 192.168. # cat /etc/hosts.deny sshd : ALL
To allow SSH access for only one block of IP addresses, but allow no other connection requests for network services aware of TCP Wrapper access control:
# cat /etc/hosts.allow sshd : 192.168. # cat /etc/hosts.deny ALL : ALL
To allow only specific user@host
or
user@IP-block
SSH access
and deny all other network services:
# cat /etc/hosts.allow sshd : joe@192.168.12.0/24 , jane@host1.example.com , jane@host2.example.com # cat /etc/hosts.deny ALL : ALL
Denyhosts
Denyhosts is a script to watch the sshd logs and
automatically add attacking hosts to a black-list maintained
through entries added to the TCP Wrapper control file
/etc/hosts.deny.
A big advantage of Denyhosts over the packet filtering techniques shown above is that it uses authentication failures, as shown by the logs, instead of simple TCP connection rates. This means that while it is more complex, you are less likely to unintentionally lock yourself out.
You can run Denyhosts as a daemon, constantly monitoring the Syslog contents to add a host to the black-list as soon as it reaches the threshold of SSH authentication failures per unit time.
Alternatively, you can run Denyhosts every so often via
cron.
Get Denyhosts and far more information about it from http://denyhosts.sourceforge.net/
Automatic Black-List with PAM
The pam_abl
PAM module can automatically add
a password-guessing host to a black-list.
You will have to experiment to verify whether your
SSH daemon uses PAM by default.
Hint: see if the syslog messages from sshd
are
associated with any from PAM rules.
If the SSH daemon does not already use PAM by default,
you should be able to add the following line to your
/etc/ssh/sshd_config
configuration file.
However, check the manual pages for sshd
and
sshd_config
and also experiment to verify what's
really going on with SSH and PAM:
UsePAM
Next, add a line like the following in the file
/etc/pam.d/sshd
before the existing auth
lines:
auth required pam_abl.so config=/etc/security/pam_abl.conf
Then add something like the following to that configuration
file /etc/security/pam_abl.conf.
Change the numbers as you see fit:
# Black-list any remote host with 10 consecutive authentication failures # in one hour, or 30 in one day. Keep them in the black-list for two days # and then purge them. host_db=/var/lib/abl/hosts.db host_purge=2d host_rule=*:10/1h,30/1d # Black-list any local user other than root for which there are 10 # consecutive authentication failures in one hour, or 30 in one day. # Keep them in the black-list for two days and then purge them. # Note that this means that non-root users may be subjected to denial of # service attacks caused by remote password guessing. user_db=/var/lib/abl/users.db user_purge=2d user_rule=!root:10/1h,30/1d
Allow/Deny rules in /etc/ssh/sshd_config
See the
sshd_config manual page
for the details on this.
The short version is that you add lines like the
following to sshd_config
and then send a HUP
signal to the daemon.
Once you add one AllowUsers
rule,
then the only users allowed to login via SSH
are the listed ones:
AllowUsers joe AllowUsers jane
You can also do this by client hosts:
AllowUsers *@host1.example.com AllowUsers *@host2.example.com
Or even by domains:
AllowUsers *@*.example.com