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 |
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 |
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 |
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.
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.
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
|
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.
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.
Get products and technologies
- Download the latest AIX
OpenSSH at http://sourceforge.net/projects/openssh-aix/
- Download TCP Wrappers
Discuss
- Follow developerWorks on Twitter.
- Get involved in the My developerWorks community.
-
Participate in the AIX and UNIX® forums:
- AIX Forum
- AIX Forum for developers
- Cluster Systems Management
- Performance Tools Forum
- Virtualization Forum
- More AIX and UNIX Forums




