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 ss hd_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 Show more Show more icon

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 Show more Show more icon

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. Show more Show more icon

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 Show more Show more icon

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

dxtans@dev01 Show more Show more icon

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 Show more Show more icon

Or

*.auth /var/adm/messages Show more Show more icon

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

# refresh -s syslogd Show more Show more icon

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 Show more Show more icon

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 Show more Show more icon

Restricting with sshd_config

Located in the sshd_config , there are four entries:

AllowUsers AllowGroups DenyUsers DenyGroups Show more Show more icon

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 Show more Show more icon

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 Show more Show more icon

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: Show more Show more icon

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 Show more Show more icon

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

DenyUsers bravo charlie mother oscar delta echoa hotel juliet papa ukflag Show more Show more icon

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 Show more Show more icon

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: Show more Show more icon

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 Show more Show more icon

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 Show more Show more icon

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 Show more Show more icon

The members of the group water are:

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

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

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

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 Show more Show more icon

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 Show more Show more icon

In the sshd_config file, we had the following entry:

DenyGroups nossh Show more Show more icon

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 Show more Show more icon

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 Show more Show more icon

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.. Show more Show more icon

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 Show more Show more icon

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 ' ' '

' | grep -v '^'$user'$' | tr '

' ' ') 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 " " "

" | while read name do echo $name >>/tmp/curr.txt2 done cat /tmp/pre.txt| tr " " "

" | 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 Show more Show more icon

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> Show more Show more icon

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 Show more Show more icon

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 Show more Show more icon

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 $? Show more Show more icon

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

Conclusion