SSH restriction

Restricting SSH connections

Restrict access to SSH users connecting to your host by utilising the users, group, and deny/allow stanzas. TCP Wrappers can also be used on a host-by-host basis.

Share:

David Tansley (david.tansley@btinternet.com), System Administrator, Ace Europe

David TansleyDavid Tansley is a freelance writer. He has 15 years of experience as a UNIX administrator, using AIX the last eight years. He enjoys playing badminton, then relaxing watching Formula 1, but nothing beats riding and touring on his GSA motorbike with his wife.


developerWorks Contributing author
        level

05 October 2010

Also available in Chinese

Introduction

SSH (OpenSSH) provides a secure encrypted connection to remote hosts. If a user has a valid AIX® account, they then can connect via SSH. However, as with any system regarding security awareness, there maybe a requirement to restrict certain users or hosts from connecting to a designated system via SSH. SSH provides two mechanisms to restrict user access: Deny and Allow attributes. These keywords are based on user and group lists. TCP Wrappers can also be used to block SSH connections from known or unknown hosts. I suggest using TCP Wrappers to block hosts either by host name/domain or IP and use the sshd_config for user/group based access control.

The configuration files and SSH daemon can be found in two places depending on what variant of SSH you have installed. For AIX OpenSSH, the configurations files are located in the /etc/ssh file and the SSH (sshd) daemon is in the /usr/sbin/sshd file.

For other OpenSSH types, typically the configuration files are located in /usr/local/etc and the SSH (sshd) daemon is located in /usr/local/sbin/sshd.


Restricting SSH with TCP Wrappers

TCP Wrappers is used to block or allow TCP-based applications that are started from inetd, like the telnet or FTP services. As a rule, SSH is not started from inetd. If you wish to control SSH access via TCP Wrappers, you will need to copy the libwrap.a into a directory where your LIBPATH points to (for third party applications, this could be: /usr/local/lib). The TCP Wrappers daemon is tcpd.

Looking through the /var/adm/messages file, there could be occasions where you find SSH attempts to your system. These maybe totally innocent attempts at connecting from a local machine within your network or a brute force attempt from an unknown host. These entries should be further investigated. If one concludes that these are unlawful attempts at access, then these should be blocked. Although one can block hosts using the sshd_config file, I suggest using TCP Wrappers to block hosts, since this is your primary wall of defense for incoming based TCP connections.

TCP Wrappers (tcpd) reads two files to determine if the incoming tcp based connection should be allowed or disallowed. The two files are:

/etc/hosts.allow
/etc/hosts.deny

Changes to these files are dynamic. Rules to pattern matching in both these files will control what gets denied or allowed. These rules can be complicated or simple. The hosts.allow is searched first. If a pattern match is found, the connection is let in, or more precisely, tcpd dies and lets the real serving daemon take over. The hosts.deny is then searched for a pattern match. If a match is found, then tcpd will deny access for that connection. Simply put, this means hosts.allow rules are applied first, followed by hosts.deny rules. The overall rule is to first allow hosts you trust then deny everything else.

Now, let's go over some examples of how TCP Wrappers can control access via SSH to a host. Imagine we belong to the domain drwho.com, and we only want to allow users belonging to this domain to SSH in. We could use the following to achieve this:

# cat hosts.allow
sshd :  .drwho.com

# cat hosts.deny
ALL : ALL

To further refine the previous example, we could still allow all the drwho.com domain connections in and also allow the following IP address ranges: 172.24.11, 172.24.12. (the dot at the end of the IP addresses means to match anything after the dot). Notice in the following example each pattern is separated by a comma.

# cat hosts.allow
sshd: .drwho.com , 172.24.11. , 172.24.12.

Generally, you would want to allow all of the domain in, except certain hosts. This would be the case if it was a production system, and you did not want connection coming from a development system. The following will allow all in the previous example just presented apart from the following hosts: dev01 and dev02.

# cat hosts.allow
sshd: .drwho.com , 172.24.11. , 172.24.12. , EXCEPT dev01, dev02

One can also use the username@hostname format in the hosts.allow or hosts.deny file. For example:

dxtans@dev01

You may have to supply the FQDN (fully qualified domain name). To test this, look at how TCP Wrappers resolves the hostname in the messages file, then amend the hosts.allow file accordingly.

For the username@host format, I prefer to use the sshd_config file to control access. This is because it allows me to use TCP Wrappers to authenticate hosts only for TCP-based services like telnet, FTP and the ssh_config file to authenticate users/groups for SSH only. It makes for easier management of host and user control access from a system administrators point of view.

To log connections via syslog to /var/adm/messages in /etc/syslogd.conf, make sure you have one of the following entries:

*.info                 /var/adm/messages

Or

*.auth		/var/adm/messages

Be sure to restart syslogd for the changes to take effect:

# refresh -s syslogd

A typical entry in the messages file for a successful connection (in this case, user dxtans from the host tardis which belongs to the domain drwho.com) could be:

May 25 19:04:46 rs6000 auth|security:info sshd[245928]: Accepted password for dx
tans from tardis port 49371 ssh2

A blocked attempt by a user from the host dev01, via the rules applied in the hosts.allow file, could be:

May 25 19:11:12 rs6000 auth|security:info sshd[270484]: refused connect from dev01

Restricting with sshd_config

Located in the sshd_config, there are four entries:

AllowUsers
AllowGroups
DenyUsers
DenyGroups

If these keywords are not present, simply create them as required. Be sure you only create the keyword you require to avoid unreliable results from occurring. Each of the Allow or DenyUsers lists are space separated by valid usernames, because the Allow or DenyGroups valid groups can only be present. The pattern match is in the following order: DenyUsers,AllowUsers,DenyGroups,AllowGroups.

Any changes made to the file sshd_config will not take effect until sshd is restarted, like so:

# stopsrc -s sshd
# startsrc -s sshd

If you populate the AllowUsers list with user ids, then only those users can SSH in. The same principal applies to the DenyUsers list, only those users contained in the deny list are denied access. This same rules applies to the group lists. This can become very confusing when you mix some of these rules up, so don't. Just have one list, either an allow list or deny list depending on what your security requirements are.

Users that have an account on that system, but do not have their user ID present in the AllowUsers list, will be denied entry. The same rule applies for the DenyUsers list. Let's now look at that with an example, suppose our AllowUsers list contained the following:

AllowUsers bravo charlie mother oscar delta echoa hotel juliet papa ukflag

If user alpha tries to SSH in then that user will be denied access, as the user name is not present in the AllowUsers list.

login as: alpha
alpha@rs6000's password:
Access denied
alpha@rs6000's password:
Access denied
alpha@rs6000's password:

Looking at the /var/adm/messages file confirms this, informing the administrators that user alpha is not listed in the AllowUsers list.

Jun 22 18:43:41 rs6000 auth|security:info sshd[270552]: User alpha from tardis 
not allowed because not listed in AllowUsers
Jun 22 18:43:41 rs6000 auth|security:info sshd[270552]: Failed none for invalid
user alpha from tardis port 49617 ssh2

Now looking at the DenyUsers list, suppose our list contained the following:

DenyUsers bravo charlie mother oscar delta echoa hotel juliet papa ukflag

Now all users whom have an account on the system who are not listed in the deny user list can SSH in.

If user alpha tries to SSH in, then that user will be allowed access as his user name is not contained in the DenyUsers list. This can be verified by viewing the messages file:

Jun 22 18:52:48 rs6000 auth|security:info sshd[250040]: Accepted password for 
alpha from tardis port 49634 ssh2

However, if user bravo who is on the DenyUsers list tries to SSH in, he will be denied access:

login as: bravo
bravo@rs6000's password:
Access denied
bravo@rs6000's password:

Again, the reason for denial can be verified by viewing the messages file:

Jun 22 18:54:03 rs6000 auth|security:info sshd[245962]: User bravo from tardis 
not allowed because listed in DenyUsers
Jun 22 18:54:03 rs6000 auth|security:info sshd[245962]: Failed none for invalid
user bravo from tardis port 49635 ssh2

Keep it simple. Only allow users you want to let in using the AllowUsers list. All other users will be denied; there is no need to add a DenyUsers list.

To stop pattern matching producing unpredictable results, I strongly suggest you do not have a group and user list entries, but rather, have either a user list entry (allow or deny) or group list entry (allow or deny), but not both.

Looking at how it could be confusing, suppose we have the following:

AllowUsers alpha
DenyUsers bravo charlie mother oscar delta echoa hotel juliet papa ukflag

Because of pattern matching order, we can now state that no users contained in the DenyUsers list will be granted access, only the user alpha will be granted access. In that case, I suggest just having the AllowUsers list only. If the user ID is not listed in the allow list, then you are not connecting, no matter what is contained in the deny list.

The same principle applies to the groups. Suppose we had the following entry:

AllowGroups water
DenyGroups nossh

The members of the group water are:

# lsgroup -a users water
water users=delta,echoa,golf,plutt

Now, if the user dxtans (a member of groups staff and admin)

# id dxtans
uid=203(dxtans) gid=1(staff) groups=205(admin)

tries to connect, the user dxtans will fail with the following entry in /var/adm/messages

Jun 30 19:50:26 rs6000 auth|security:info sshd[278732]: User dxtans from tardis
not allowed because none of user's groups are listed in AllowGroups

because dxtans is not in the allow list nor in the deny list.

It maybe advantageous to create a group that contains users that you do not want to allow to SSH in, as this will require least maintenance, and then create a group of users that are allowed to SSH in. For example, suppose we had a group called nossh, containing users not allowed to SSH in:

# lsgroup -a users nossh
nossh users=golf,hotel,india,julie

In the sshd_config file, we had the following entry:

DenyGroups nossh

Now any user belonging to the group nossh would be denied access, as can be viewed in the messages file when user golf, whom belongs to the group nossh, tries to SSH in:

Jun 22 19:40:36 rs6000 auth|security:info sshd[278778]: User golf from tardis 
not allowed because a group is listed in DenyGroups

Using the group list, rather than the user list, can seem more favorable when you have a large number of users on the system.


Control root access

When dealing with direct root SSH access from remote hosts, I suggest it is best to limit which hosts can SSH in as user root. This approach allows administrators to keep control of where user root can SSH to, thus keeping control on root remote connections.

To control access by user and host via the sshd_conf file, you can use the following formats: root@<hostname> or root@<hostname.domain>.

You will need to see what format SSH reports in the /var/adm/messages file, whether the host is resolved as a short or long hostname. Once this has been identified, simply insert the corresponding entry into the AllowUsers list. If you are not sure whether it is a short or long name, as this can be dependent on different operating systems from the originating SSH connection, it is best to include both the short and long name.

For example, to allow only root access from the host tardis (which belongs to the drwho domain) we could have the following:

AllowUsers root@tardis root@tardis.drwho.com

Of course, in the real world, the above list would also be populated with other users.

The above method would be ideally used where you have a deployment server, and only root from that deployment server (in the above example) can SSH and SCP from one determined host to all clients.


Automatically generating the user list

Being able to generate automatically a user list, be it for allow or deny lists, does have its benefits. Users get added and deleted over a period of time, so it can be easy to become complacent and forgot to update the list. What list should you generate? I would suggest populating only the allow list. It can be easier to determine if that user is not in the AllowUsers list. If not, then access will be denied. What rules do you apply to allow a user into the AllowUsers list? Consider that if the user's attributes state that the user can remote rlogin/telnet (rlogin=true), then why not be allowed to SSH in, as well? Using this rule immediately cuts out all the system and application owner accounts and should leave just normal users.

On a typical system, all system accounts and applications owners should, as a rule, be non-login able. Thus, the only access to those accounts is via su or sudo.

One could then perhaps manually edit the list only for adhoc events, like to further allow (or deny) temporary access during application roll-outs. Listing 1 below demonstrates one way this can be achieved. For example, first the location of the sshd_config file is determined, so we know which SSH version to update. Next, a list of all users on the host is gathered where the rlogin attribute is true, excluding root. The file /tmp/ssh_ignore is then read and any matches found in the generated list of users is then deleted from the list. The use of the file ssh_ignore allows administrators to make adhoc changes on users whom are not to appear in the allowed user list in the sshd_config file.

The format of the file is:

user1
user2
user..

The AllowUsers list in the sshd_config file is then extracted and compared with the newly generated user list. If there are changes, the sshd_config file is updated. Notice the short and long name of the root@tardis is included. As discussed previously, this means only root from the host tardis can SSH in; this provides control as only root from the hosts tardis can SSH in. If there are any changes in the sshd_config, then the SSH service is restarted, and an email is sent out to administrators informing them of the change to sshd_config AllowUsers list.

The script assumes that there is already an entry for the AllowUsers list, and that the list contains at least an entry for the deployment server, like so:

AllowUsers root@tardis root@tardis.drwho.com

That is, the name of the host, that user root can only connect from. In this example, it contains both the short and long host name (the host's name is tardis). Please amend the script to suit your needs, replace the host tardis with your own host. The script could be run from your favorite scheduler once a day to keep the user list up to date.

Listing 1. pop_user_allow_ssh
#!/bin/sh
#set -x 
# pop_user_allow_ssh
# populate UserAllow entries in sshd_conf
# if rlogin is true 

host=$(hostname)
log=/opt/dump/ssh_pop.log
>$log

if [ -f /usr/local/etc/sshd_config ]
  then
   sshd_conf="/usr/local/etc/sshd_config"
fi
 
if [ -f /etc/ssh/sshd_config ]
  then
   sshd_conf="/etc/ssh/sshd_config"
fi


echo " on ${host}: ssh config to use: [$sshd_conf]" | tee -a $log

# check if we need to run..any user adds/deletes
pre=`grep -w ^AllowUsers $sshd_conf`
pre=$(echo $pre | sed s/root@tardis.drwho.com//g |sed 
s/root@tardis//g|sed s/AllowUsers//g)
curr=$(lsuser -a rlogin ALL |grep true| grep -v root| awk '{print $1}')

# check for ignores - do NOT add to allow list
if [ -f /tmp/ssh_ignore ]
 then
cat /tmp/ssh_ignore|while read user
 do
   new_curr=$(echo $curr | tr ' ' '\n' | grep -v '^'$user'$' | tr '\n' ' ')
    echo "[$user]"
   curr=$new_curr

 done
fi
   echo "curr [$curr]"
  echo "pre [$pre]"

echo $curr >/tmp/curr.txt
echo $pre >/tmp/pre.txt
diff /tmp/curr.txt /tmp/pre.txt 2>&1
if [ $? = 0 ]
  then
   echo "no user adds/deletes detected on system"| tee -a $log
   exit 0
   else
    echo "changes detected..re-populating" |tee -a $log
fi

cp $sshd_conf $sshd_conf.bak

sed '/AllowUsers/d' $sshd_conf >$sshd_conf.$$
echo "AllowUsers root@tardis root@tardis.drwho.com" $curr >>$sshd_conf.$$
mv $sshd_conf.$$ $sshd_conf
stopsrc -s sshd
sleep 2
startsrc -s sshd

# next determine change and  email out change
>/tmp/curr.txt2
>/tmp/pre.txt2
cat /tmp/curr.txt|tr " " "\n" |
 while read name
 do
 echo $name >>/tmp/curr.txt2
 done 

 cat /tmp/pre.txt| tr " " "\n" |
 while read name
  do
  echo $name >>/tmp/pre.txt2
  done
 
diff /tmp/pre.txt2 /tmp/curr.txt2 >/tmp/allow_diff.txt
changelist=$(cat /tmp/allow_diff.txt | egrep "<|>" | sed 's/[<>]//g')

list="admins@drwho.com"


mail -s "`hostname` allowed users sshd_config change" $list <<mayday
Host: `hostname`
SSH: $sshd_conf
The following users have been either added or deleted in AllowUsers:

$changelist

mayday

Debugging

To enable debugging, where the messages file just does not display enough information. From the client side, use the verbose (v) where more v's means more verbose option when connecting via SSH:

ssh -vv -l <login> <hostname>

From the server side, stop the sshd service and start it up with the debug option. The debug (d) means more verbose. When a connection is made, sshd will then terminate. You will then need to either restart it in debug mode again (if you require further testing) or normally via the startsrc command. To start in debug mode use:

# /usr/sbin/sshd -dd

If you do find issues when trying to connect via SSH, and the issue is not immediately obvious, then I recommend using both the client and server debug methods together to diagnose issues.

Typical output of starting sshd in debug mode could be:

# /usr/sbin/sshd -dd
debug2: load_server_config: filename /etc/ssh/sshd_config
debug2: load_server_config: done config len = 277
debug2: parse_server_config: config /etc/ssh/sshd_config len 277
debug1: sshd version OpenSSH_5.0p1
debug1: read PEM private key done: type RSA
debug1: private host key: #0 type 1 RSA
debug1: read PEM private key done: type DSA
debug1: private host key: #1 type 2 DSA
debug1: rexec_argv[0]='/usr/sbin/sshd'
…..
debug2: parse_server_config: config reprocess config len 277
User bravo from rs6000 not allowed because listed in DenyUsers
input_userauth_request: invalid user bravo

If sshd is started in debug mode, any errors found in sshd_conf by sshd will be reported to standard ouput. If sshd is started by the startsrc command any errors found in the sshd_conf file will be displayed in the /var/adm/messages file. You can also use the following method by checking the last exist status (you can determine if you have issues with the configuration):

# /usr/sbin/ssh -t
# echo $?

Using the debug method, however, should be first when diagnosing SSH related issues.


Conclusion

Using SSH allows for secure encrypted connections to remote hosts. However, as with any incoming connection, these need to be policed. The use of TCP Wrappers and the Allow/Deny lists in sshd_config can put you on a proactive stance regarding security.

Resources

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into AIX and Unix on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=AIX and UNIX
ArticleID=549109
ArticleTitle=SSH restriction
publish-date=10052010