Centralizing sudoers configuration with IBM Directory Server
I like questions. Especially the questions which challenge me. Christian Sonnemans asked on IBM Community forum about experiences with sudo and LDAP. Answering his questions I understood, that I never configured sudo with IBM Directory Server. Only with FreeIPA or Active Directory. So, what’s the problem? Let’s do it!
But before starting let me tell you two words about sudo and AIX. Of course there are case when you want to have sudo on AIX. But we have much better security tools on AIX. You don’t need sudo if the only thing you need is to give your users the privilege to execute some commands under root account. Learn what RBAC is and use it!
Let’s assume we have so much need for sudo, that we must install it!
Installing sudo
First of all there are two sudo packages in AIX Toolbox for Opensource applications. Yes, sudo is not a standard part of AIX. Why? See above! It means you must configure DNF on your AIX box or download and install the RPM packages manually.
Which package should you use?
Both packages have LDAP support. The biggest difference is which LDAP implementation they use. The package sudo uses OpenLDAP. The package sudo_ids uses IBM LDAP libraries. If you install sudo with DNF, the OpenLDAP libraries will be automatically installed as dependency. If you install sudo_ids, you must install IBM LDAP libraries before installing the package.
There are also some other differencies between two packages - like the place for LDAP configuration, and documentation files.
Because my test AIX is already connected to IBM Directory Server, I already have the LDAP libraries and install sudo_ids. If you have AIX LDAP client already running on your box, you already have LDAP libraries. It doesn’t matter if you use the LDAP client to connect to IBM Directory Server, to Active Directory or to Red Hat Identity Management.
All what I write after this point is based on sudo_ids package. If you have sudo package (without _ids), you are on your own…
The installation of sudo is supposed to be easy:
/opt/freeware/bin/dnf -y install sudo_ids
If you previously installed the sudo package, you must remove it before installing sudo_ids.
If you get strange error message like “Can’t find what provides libibmldap.a”, you need to update your virtual RPM packages definitions:
updtvpkg
and then install the sudo_ids package.
LDAP schema
Sudo uses its own LDAP schema. By default it is neither in IBM Directory Server nor in Active Directory. Only Red Hat Identity Management (FreeIPA) is delivered with the sudo schema by default.
Because I use IBM Directory Server, I must install first the schema into the server before using sudo with LDAP.
You can find several different schemas in /opt/freeware/share/docs/sudo_ids-*/
:
# ls schema*
schema.ActiveDirectory schema.OpenLDAP schema.iPlanet schema.olcSudo
There are two different schemas for different versions of OpenLDAP - schema.OpenLDAP and schema.olcSudo. There is a separate schema for Active Directory - schema.ActiveDirectory.
The last file - schema.iPlanet - is supposed to be for all iPlanet-compatible LDAP servers. If you don’t know what iPlanet is, don’t worry! It is something like grandfather of all modern LDAP servers, and no, you can’t find it anymore by any customer. At least I hope, that you don’t use software which development was stopped more than 20 years ago.
IBM Directory Server is iPlanet compatible. So what can be the problem? Just execute idsldapadd -D uid=admin -w \? -i schema.iPlanet
and you have your sudoers! I did it and got back - DSA unwilling to perform
.
It means there are some syntax in the file which IBM Directory Server doesn’t understand.
I started digging deeper and changed the schema file several times. I’ve got new errors, but it didn’t work for me so well.
Fortunately enough several years ago during another customer project I’ve got experience developing my own schemas for IBM Directory Server. I decided to take the iPlanet schema and rewrite it completely for IBM Directory Server.
Several minutes later I’ve got the file:
dn: cn=schema
changetype: modify
add: attributetypes
attributetypes: ( 1.3.6.1.4.1.15953.9.1.1 NAME 'sudoUser' DESC 'User(s) who may run sudo' EQUALITY caseExactMatch SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.1 DBNAME( 'sudoUser' 'sudoUser' ) ACCESS-CLASS normal LENGTH 512 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.2 NAME 'sudoHost' DESC 'Host(s) who may run sudo' EQUALITY caseExactIA5Match SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.2 DBNAME( 'sudoHost' 'sudoHost' ) ACCESS-CLASS normal LENGTH 512 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.3 NAME 'sudoCommand' DESC 'Command(s) to be executed by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.3 DBNAME( 'sudoCommand' 'sudoCommand' ) ACCESS-CLASS normal LENGTH 2048 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.4 NAME 'sudoRunAs' DESC 'User(s) impersonated by sudo (deprecated)' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.4 DBNAME( 'sudoRunAs' 'sudoRunAs' ) ACCESS-CLASS normal LENGTH 512 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.5 NAME 'sudoOption' DESC 'Options(s) followed by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.5 DBNAME( 'sudoOption' 'sudoOption' ) ACCESS-CLASS normal LENGTH 512 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.6 NAME 'sudoRunAsUser' DESC 'User(s) impersonated by sudo' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.6 DBNAME( 'sudoRunAsUser' 'sudoRunAsUser' ) ACCESS-CLASS normal LENGTH 512 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.7 NAME 'sudoRunAsGroup' DESC 'Group(s) impersonated by sudo' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.7 DBNAME( 'sudoRunAsGroup' 'sudoRunAsGroup' ) ACCESS-CLASS normal LENGTH 512 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.8 NAME 'sudoNotBefore' DESC 'Start of time interval for which the entry is valid' EQUALITY generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.8 DBNAME( 'sudoNotBefore' 'sudoNotBefore' ) ACCESS-CLASS normal LENGTH 512 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.9 NAME 'sudoNotAfter' DESC 'End of time interval for which the entry is valid' EQUALITY generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.9 DBNAME( 'sudoNotAfter' 'sudoNotAfter' ) ACCESS-CLASS normal LENGTH 512 )
dn: cn=schema
changetype: modify
add: attributetypes
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.10 NAME 'sudoOrder' DESC 'an integer to order the sudoRole entries' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
-
add: ibmattributetypes
ibmattributetypes: ( 1.3.6.1.4.1.15953.9.1.10 DBNAME( 'sudoOrder' 'sudoOrder' ) ACCESS-CLASS normal )
dn: cn=schema
changetype: modify
add: objectClasses
objectClasses: ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL DESC 'Sudoer Entries' MUST ( cn ) MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoRunAsUser $ sudoRunAsGroup $ sudoOption $ sudoOrder $ sudoNotBefore $ sudoNotAfter $ description ) )
Now I could add it to IBM Directory Server using idsldapadd:
# idsldapadd -D uid=admin -w 'Passw0rd!' -i schema.ids
Operation 0 modifying entry cn=schema
Operation 1 modifying entry cn=schema
Operation 2 modifying entry cn=schema
Operation 3 modifying entry cn=schema
Operation 4 modifying entry cn=schema
Operation 5 modifying entry cn=schema
Operation 6 modifying entry cn=schema
Operation 7 modifying entry cn=schema
Operation 8 modifying entry cn=schema
Operation 9 modifying entry cn=schema
Operation 10 modifying entry cn=schema
Yes, we have the schema! We can go to the next step and configure sudo in LDAP.
But before going further, I created an issue at sudo project and submitted my schema to be included officially with sudo.
Please click the link and set +1 to the issue or even add some comment.
Configuring sudo in IBM Directory Server
When you have local sudoers file, you usually (must) use visudo command. The command automatically checks if your syntax is correct. There are no such tools for LDAP sudoers. Everything is to be done manually. OK, you can automate it with Ansible - no magic here. I will show you the manual way today.
First we must create some container or organizationalUnit object to save all sudoers configuration. This is my sudoers.ldif to create such ou=sudoers
. Everything after it, like ou=aix,dc=ad,dc=power-devops,dc=cloud
is my base DN for the LDAP tree.
dn: ou=sudoers,ou=aix,dc=ad,dc=power-devops,dc=cloud
objectClass: top
objectClass: organizationalUnit
ou: sudoers
After your created the file you can add it into the LDAP tree using idsldapadd:
# idsldapadd -D uid=admin -w 'Passw0rd!' -i sudoers.ldif
Operation 0 adding new entry ou=sudoers,ou=aix,dc=ad,dc=power-devops,dc=cloud
Now you are ready to create some roles. Yes, your new sudo configuration is based on roles. You create objects of type sudoRole
in ou=sudoers
and must provide some name for the role in cn
attribute. In this object you can specify user names using the attribute sudoUser
. These users will be able to execute some commands which are specified in sudoCommand
attribute. The commands can be executed only on hosts which are specified in sudoHost
attribute and only using the credentials which are specified in sudoRunAsUser
attribute.
In the example below I create the role aixadm (cn
) and two users (sudoUser
) - aixadm and root - can execute all commands (sudoCommand
) on all hosts (sudoHost
) using any user account (sudoRunAsUser
), defined on the hosts. If you want to give privileges to some group, use %-notation in sudoUser
attribute. E.g., %system
will provide the specified privileges to all members of the group system.
dn: cn=aixadm,ou=sudoers,ou=aix,dc=ad,dc=power-devops,dc=cloud
objectClass: top
objectClass: sudoRole
cn: aixadm
sudoUser: aixadm
sudoUser: root
sudoRunAsUser: ALL
sudoHost: ALL
sudoCommand: ALL
After you described your sudoers roles in LDIF files like above, you add them into the LDAP tree using idsldapadd command.
Configuring AIX to use sudo with LDAP
After you installed sudo_ids on all your AIX servers there are only two things you must do additionally.
First you create /etc/sudo-ldap.conf configuration file.
This is the most basic (and worst) example I could do:
uri ldap://10.10.3.1
binddn uid=admin
bindpw Passw0rd!
sudoers_base ou=sudoers,ou=aix,dc=ad,dc=power-devops,dc=cloud
Of course, you are NOT allowed to use uid=admin (LDAP root!) for sudoers configuration, and use non-encrypted connection to LDAP. Please refer to the official documentation for the full list of options you can set.
Another configuration change is to enable sudo to search for sudoers entries in LDAP. Add the following line into /etc/netsvc.conf:
sudoers = files,ldap
Of course, you can change the order or drop files
from the line.
That’s it! Now you should see your new sudo privileges with sudo -l
command and be able to use them!
Have fun with sudo and IBM Directory Server!
Andrey