Projects/OpenLDAP DIT
From Mandriva
A proposal for a default DIT
This page is about the openldap-mandriva-dit package, used by default in the upcoming Corporate Server 4 product and also available for cooker users. This text is part of the package's documentation (several README files).
This documentation is correct for openldap-mandriva-dit-0.12-1{mlcs4,mdv2007.0}.
Introduction
This document aims to explain the Directory Information Tree (DIT) used in the openldap-mandriva-dit package. The package could be used in other distributions with small adjustments. Currently it relies on:
- openldap-2.3 features
- certain schema files placed in /usr/share/openldap/schemas/
- overlay modules built as .so objects and available in /usr/lib/openldap/
So, to make it work in other distributions, basically one would at most need to adapt these paths, include schema files or remove configuration lines that load modules if these are builtin. The configuration file to change would be the standard /etc/openldap/slapd.conf or, in the case of the template, /usr/share/openldap/mandriva-dit/mandriva-dit-slapd-template.conf.
The motivation for this new layout is the need for a better separation of privileges regarding access to the information stored in the directory. The super user account of the directory should be used rarely and delegation of privileges should be easier.
We think this proposed layout accomplishes that by providing several groups which have distinctive access rules we provide a clear separation of privileges. In order to give a user a new privilege, all that is needed is to add him/her to one of these specific groups.
These are the characteristics of the proposed DIT:
- several groups for common services
- most access control rules based on group membership
- several system accounts ready to use (just add a password) by many services such as:
- sudo
- dns
- accounts
- etc
- simple installation script which prepares the tree asking very few questions (just two, and one of them is just a password)
- easy support for OpenLDAP's password policy overlay
These accounts get their privileges by being associated to specific groups.
Administrators should note that we will probably find out that there are too few groups, or too many. Or that some ACLs are too restrictive, or too broad. It is difficult to come up with a one-size-fits-all DIT, but we can start here.
By the way, there is no password set for the rootdn account as it (the account) is not used.
If you just want to know how to use this DIT, skip to the end of the document to the section called "Enough with the theory: how to use this?".
The tree
dc=example,dc=com ou=Hosts ou=System Groups ou=System Accounts ou=Idmap cn=LDAP Admins uid=Ldap Admin ou=Address Book cn=Sudo Admins uid=Sudo Admin ou=dhcp cn=DNS Admins uid=DNS Admin ou=dns cn=DNS Readers uid=DNS Reader ou=People cn=DHCP Admins uid=DHCP Admin ou=Group cn=Address Book Admins uid=Address Book Admin ou=Password Policies cn=LDAP Replicators uid=LDAP Replicator ou=Sudoers cn=Account Admins uid=Account Admin cn=MTA Admins uid=MTA Admin cn=LDAP Monitors uid=LDAP Monitor cn=Idmap Admins uid=Idmap Admin uid=smbldap-tools uid=nssldap
The services
We created some entries for a few services that can use LDAP to store their information. More will probably be added in the future. For now, we have branches for:
- dns (ou=dns)
- sudo (ou=sudoers)
- dhcp (ou=dhcp)
The respective administrative groups have read/write access to these branches for specific entries.
The groups
Groups are the core of this proposed DIT layout, because most ACLs are constructed via group membership to allow for greater flexibility and delegation.
The current default groups that are born with the new DIT layout are as follows:
- LDAP Admins
- Sudo Admins
- DNS Admins
- DNS Readers
- DHCP Admins
- DHCP Readers
- Address Book Admins
- LDAP Replicators
- Account Admins
- MTA Admins
- LDAP Monitors
- Idmap Admins
Each entry has a description attribute filled in with a brief text describing the purpose of the members of each group. For example:
dn: cn=Sudo Admins,ou=System Groups,dc=example,dc=com description: Members can administer ou=sudoers entries and attributes
In order to use groups in ACLs, the objectClass used for these entries has to use attributes where membership is indicated distinguished names and not just names. In other words, the membership attribute has to use a full DN to indicate its member. The standard object class used for this by OpenLDAP is groupOfNames, and this is what we used. For example:
dn: cn=Sudo Admins,ou=System Groups,dc=example,dc=com member: uid=Sudo Admin,ou=System Accounts,dc=example,dc=com
A side effect of using groupOfNames is that we have to have at least one member in each group. So we needed to create standard accounts, which proved to be useful anyway. The previous example showed the standard account for adminstering sudo entries and attributes.
The accounts
As was the case with the groups, many standard system accounts were created. Each group has at least a corresponding system account as its membership. The current list is as follows:
- Account Admin
- smbldap-tools
- nssldap
- MTA Admin
- DHCP Admin
- DHCP Reader
- DNS Admin
- DNS Reader
- Sudo Admin
- Address Book Admin
- LDAP Admin
- LDAP Replicator
- LDAP Monitor
- Idmap Admin
The privileges
The idea is to give each group the privileges needed to complete it's administration tasks. This usually means having access to the respective ou=foo branch of the directory. For example, the Sudo Admins group has rights over the ou=sudoers branch of the directory.
Whenever possible, however, these rights are limited to that specific service, i.e. it's not any kind of entry that can be created but just those relevant to the service. For example, the Sudo Admins members can only create entries one level below ou=sudoers, and only with the attributes allowed by the sudoRole object class.
Other cases, however, can be more complicated. We will list them here and the reasoning behind the chosen ACLs.
Monitoring access
The LDAP Monitors group is the only group besides LDAP Admins which can read entries under cn=monitor. This base dn contains statistics about the server, such as operations performed, backends and overlays being used, etc. So, if you need a user to have read access to this kind of information, just put him/her in this group.
Samba, Unix and Kerberos admins
Samba needs to have corresponding unix accounts for it's users and machine accounts. It will not by itself create those, however. For example, when running "smbpasswd -a foo", the "foo" user account will only be created if samba can find the corresponding unix attributes. The same for group mappings and machine accounts.
Earlier versions of openldap-mandriva-dit had two separate privilege groups: one for Unix accounts and another for Samba accounts. This complicated ACLs, and it was worse when we later added Kerberos Admins to the mix because they also had to touch some of the account-related attributes.
So, since version 0.11, we merged these groups into one called Account Admins (and the respective Account Admin account). This made the ACLs simpler and faster, at the expense of some granularity in privileges.
The smbldap-tools account, uid=smbldap-tools,ou=System Accounts, still exists but is now a member of the Account Admins group.
MTA
As of this moment, there is no clear scenario for usage of this account. For now, it can administer just a few attributes: all the ones from the inetLocalMailRecipient object class plus the single mail attribute.
As more usage scenarios appear, these ACLs should be incremented.
LDAP Admins
Members of this group can write to and read from all entries and attributes of the directory and have no size or time limits.
Note that, contrary to rootdn, LDAP Admins are subject to password policies if these are being used.
LDAP Replicators
The members of the LDAP Replicators group have read access to all attributes and entries of the directory so that they can be used in a syncrepl replication setup. The bind dn used for the replication should be a member of this group. For example:
syncrepl rid=100 provider=ldap://dirserv.example.com type=refreshAndPersist retry="60 +" searchbase="dc=example,dc=com" starttls=critical bindmethod=simple binddn="uid=LDAP Replicator,ou=System Accounts,dc=example,dc=com" credentials="secret"
Here, uid=LDAP Replicator,ou=System Accounts,dc=example,dc=com is a member of the LDAP Replicators group and is automatically granted read rights to all entries of the directory (assuming the provider was also installed with this base DIT and ACLs).
Generic directory read accounts
A few accounts were created for specific read access. Some administrators prefer to block anonymous read access to the directory, in which case these accounts would then be used. For the moment we have:
- nssldap: nss_ldap can bind to the directory either anonymously or with a specific account. The uid=nssldap,ou=System Accounts was created for this purpose. Currently no ACLs make use of this account. Were the administrator to use it, he/she would also have to block anonymous read access to many attributes.
- DNS Readers: members of this group can read the ou=dns branch. Anonymous access is denied.
- DHCP Readers: members of this group can read the ou=dhcp branch, but note that anonymous access is still allowed. This group exists just for the convenience for the administrators who want to, later on, restrict anonymous access to this branch.
Currently anonymous read access is granted to many attributes. As of this moment, if the administrator wants to restrict anonymous access and use these accounts, the ACLs would have to be changed manually. This topic should be further improved in the package.
The installation script
The openldap-mandriva-dit package contains a shell script which can be used to install the accounts and ACLs described in this document. The script is installed at /usr/share/openldap/scripts/mandriva-dit-setup.sh and performs the following:
- asks the DNS domain (suggesting whatever was auto-detected)
- constructs the top-level directory entry from this domain using dc style attributes
- creates and imports an ldif file with the accounts and groups described here
- installs new slapd.conf and mandriva-dit-access.conf files (making backups of the previous ones) with the default ACLs and other useful configurations (like cache)
- loads the ldif file, backing up the previous database directory
Even though the script performs many tests and backups many files before overwriting them, administrators are advised to backup all data before running this script.
Enough with the theory: how to use this?
The sample installation script will overwrite some OpenLDAP files and directories. Specifically, it will backup and overwrite the following:
- /etc/openldap/slapd.conf
- /etc/openldap/ldap.conf
- /etc/openldap/mandriva-dit-access.conf (THIS ONE HAS NO BACKUP CURRENTLY)
- /var/lib/ldap contents
So, after you are satisfied that nothing important will be lost, run the script. It has a few command line options, use --help for a list. Below is a sample run using the example.com domain:
[root@cs4 ~]# /usr/share/openldap/scripts/mandriva-dit-setup.sh Please enter your DNS domain name [conectiva]: example.com Administrator account The administrator account for this directory is uid=LDAP Admin,ou=System Accounts,dc=example,dc=com Please choose a password for this account: New password: secretpass Re-enter new password: secretpass Summary __=__ Domain: example.com LDAP suffix: dc=example,dc=com Confirm? (Y/n) Y config file testing succeeded Stopping ldap service Finished, starting ldap service Running /usr/bin/db_recover on /var/lib/ldap removing /var/lib/ldap/alock Starting slapd (ldap + ldaps): [ OK ] Your previous database directory has been backed up as /var/lib/ldap.1145397294
Now, fire up an LDAP browser and use the LDAP Admin account shown above to set up some passwords for the other less privileged accounts that you are going to use. Note that the rootdn account is not used.
Examples
Here are some configuration examples for some of the services that can be hosted in this DIT. Don't forget that the only account that is enabled after running the script is the LDAP Admin one: all others are disabled and you need to use the LDAP Admin account to give a password and thus enable the other administration accounts you are going to use.
For example, to enable (i.e., give a password to) the DNS Admin account, one could use:
$ ldappasswd -x -D "uid=LDAP Admin,ou=System Accounts,dc=example,dc=com" \ -W -S "uid=DNS Admin,ou=System Accounts,dc=example,dc=com" New password: [choose password for DNS Admin] Re-enter new password: [retype it] Enter LDAP Password: [here is the password for the bind (-D) user: LDAP Admin] Result: Success (0)
DNS
Necessary steps:
- import zone into LDAP at ou=dns branch, which is where our ACLs expect the DNS information to be stored
- configure named.conf to use LDAP for each zone that was imported
- configure LDAP authentication parameters in named.conf (ou=dns can only be read by DNS Admins and DNS Readers' members)
Example
We will import the zone below into LDAP at ou=dns:
$TTL 86400 $ORIGIN example.com. @ IN SOA aurelio.example.com. hostmaster.example.com. ( 1 ; serial number 10800 ; refresh 3600 ; retry 604800 ; expires 86400 ) ; TTL @ IN NS aurelio.example.com. @ IN MX 10 mail.example.com. gateway IN A 10.0.1.1 dogs IN A 10.0.1.7 mail IN A 10.0.1.8 aurelio IN A 10.0.1.9 dhcp010 IN A 10.0.1.10 dhcp011 IN A 10.0.1.11 ns1 IN CNAME aurelio kdc IN CNAME dogs localhost IN A 127.0.0.1 ;_kerberos._udp IN SRV 0 0 88 dogs
The SRV record has to be commented because zonetoldap can't handle that yet. We will add it manually later.
The corresponding reverse zone is this:
$TTL 86400 $ORIGIN 1.0.10.in-addr.arpa. @ IN SOA aurelio.example.com. hostmaster.example.com. ( 1 ; serial 10800 ; refresh 3600 ; retry 604800 ; expires 86400 ) ; TTL @ IN NS aurelio.example.com. 1 IN PTR gateway.example.com. 7 IN PTR dogs.example.com. 8 IN PTR mail.example.com. 9 IN PTR aurelio.example.com. 10 IN PTR dhcp010.example.com. 11 IN PTR dhcp011.example.com.
After preparing our tree with the mandriva-setup.sh script, we can insert these zone files in LDAP at ou=dns (using the DNS Admin user credentials):
$ zonetoldap -D 'uid=DNS Admin,ou=System Accounts,dc=example,dc=com' -W \ -b ou=dns,dc=example,dc=com -z example.com -f example.com.zone -h localhost -c Enter LDAP Password: secretpass $ zonetoldap -D 'uid=DNS Admin,ou=System Accounts,dc=example,dc=com' -W \ -b ou=dns,dc=example,dc=com -z 1.0.10.in-addr.arpa \ -f 1.0.10.in-addr.arpa.zone -h localhost -c Enter LDAP Password: secretpass $
This will produce the following entries under ou=dns,dc=example,dc=com:
dc=com dc=example relativeDomainName=ns1+zoneName=example.com relativeDomainName=mail+zoneName=example.com relativeDomainName=localhost+zoneName=example.com relativeDomainName=kdc+zoneName=example.com relativeDomainName=gateway+zoneName=example.com relativeDomainName=dogs+zoneName=example.com relativeDomainName=dhcp011+zoneName=example.com relativeDomainName=dhcp010+zoneName=example.com relativeDomainName=aurelio+zoneName=example.com relativeDomainName=@+zoneName=example.com dc=arpa dc=in-addr dc=10 dc=0 dc=1 relativeDomainName=9+zoneName=1.0.10.in-addr.arpa relativeDomainName=8+zoneName=1.0.10.in-addr.arpa relativeDomainName=7+zoneName=1.0.10.in-addr.arpa relativeDomainName=11+zoneName=1.0.10.in-addr.arpa relativeDomainName=10+zoneName=1.0.10.in-addr.arpa relativeDomainName=1+zoneName=1.0.10.in-addr.arpa relativeDomainName=@+zoneName=1.0.10.in-addr.arpa
We sill have to add the SRV record from the zone file which we commented. The following LDIF can be used:
dn: relativeDomainName=_kerberos._udp+zoneName=example.com,dc=example,dc=com,ou=dns,dc=example,dc=com objectClass: dNSZone relativeDomainName: _kerberos._udp zoneName: example.com SRVRecord: 0 0 88 dogs
Let's add it:
$ ldapadd -x -D 'uid=DNS Admin,ou=System Accounts,dc=example,dc=com' \ -W -f srv.ldif Enter LDAP Password: secretpass adding new entry "relativeDomainName=_kerberos._udp+zoneName=example.com,dc=example,dc=com, ou=dns,dc=example,dc=com"
You should always review the entries produced by the zonetoldap tool, there may be other inconsistensies with other records.
Now we have to configure named.conf to consult LDAP for these two zones. Below is an example file.
Please remember that named runs in a chroot and won't use the system /etc/hosts file, but its own inside the chroot. Also avoid making loops: for example, don't use a server name that's in the ldap zone to specify the ldap server. In general, it's better to use IP addresses instead of hostnames in named.conf.
options { directory "/var/named"; allow-transfer { none; }; notify no; allow-query { any; }; }; zone "." { type hint; file "named.ca"; }; zone "0.0.127.in-addr.arpa" { type master; file "named.local"; }; zone "example.com" { type master; database "ldap ldap://127.0.0.1/ou=dns,dc=example,dc=com??sub??!bindname=uid=DNS%20Reader%2c ou=System%20Accounts%2cdc=example%2cdc=com,!x-bindpw=dnsreader 86400"; }; zone "1.0.10.in-addr.arpa" { type master; database "ldap ldap://127.0.0.1/ou=dns,dc=example,dc=com??sub??!bindname=uid=DNS%20Reader%2c ou=System%20Accounts%2cdc=example%2cdc=com,!x-bindpw=dnsreader 86400"; }; key "rndc-key" { algorithm hmac-md5; secret "U8C6P+RjbAM2udmutlz0Vw=="; }; controls { inet 127.0.0.1 port 953 allow { 127.0.0.1; } keys { "rndc-key"; }; };
The database parameter specifies the database to use for the zone file. In our case, it's LDAP. The URL seems a bit complicated, so let's explain it. The generic format of the URL follows RFC 2255 and is like this:
ldap://server/basedn?attributes?scope?filter?extensions
So, if we want to specify a subtree search on ou=dns on localhost with no default filter, attributes or extensions, it would be like this:
ldap://localhost?ou=dns,dc=example,dc=com??sub?
Our DIT, however, requires authenticated searches on ou=dns, so we have to add extensions. Extensions are a comma-separated list of names optionally preceeded by "!" indicating it's usage is critical. We will use bindname and x-bindpw (the latter one, being not standard, is prefixed by "x-"). The URL now looks like this:
ldap://localhost?ou=dns,dc=example,dc=com??sub??!bindname=uid=DNS Reader, ou=System Accounts,dc=example,dc=com,!x-bindpw=dnsreader
Since extensions are comma-separated, we have to espace the commas in the binddn. We also have to escape the spaces. We do this using standard URL encoding formats. In our case, the URL then finally becomes:
ldap://localhost/ou=dns,dc=example,dc=com??sub??!bindname=uid=DNS%20Reader%2c ou=System%20Accounts%2cdc=example%2cdc=com,!x-bindpw=dnsreader
Beware that /etc/named.conf has to be mode 0640 owner root:named because it contains two secrets now: the rndc key and the LDAP credentials used to bind to the directory (just because of the rndc key it should already have these permissions, but make sure).
This is all there is to it. Now start the daemon and do some tests with dig:
$ dig +short @127.0.0.1 -t mx example.com< 10 mail.example.com. $ dig +short @127.0.0.1 mail.example.com 10.0.1.8 $ dig +short @127.0.0.1 -x 10.0.1.8 mail.example.com. $ dig +short @127.0.0.1 -t srv _kerberos._udp.example.com 0 0 88 dogs.example.com.
Done!
Delegating
It's very easy to delegate DNS administrative privileges to someone else in your directory. Just put his/her dn in the "DNS Admins" group and he/she will be able to change anything under ou=dns. Here is an example command to add the uid=anderson,ou=People,dc=example,dc=com user to this group:
$ ldapadd -x -D 'uid=DNS Admin,ou=System Accounts,dc=example,dc=com' -W Enter LDAP Password: secretpassword dn: cn=DNS Admins,ou=System Groups,dc=example,dc=com changetype: modify add: member member: uid=anderson,ou=People,dc=example,dc=com modifying entry "cn=DNS Admins,ou=System Groups,dc=example,dc=com" ^D
Now this user can add new records, delete some, change others and even create new zones, although for new zones to be loaded the named.conf file on the DNS server has to be changed.
Samba
To use this DIT with Samba, the following configuration details have to be observed.
Layout in LDAP
The following layout is the one that has to be configured in /etc/samba/smb.conf and /etc/smbldap-tools/smbldap.conf:
- machine accounts: under ou=Hosts
- user accounts: under ou=People
- group accounts: under ou=Group
- idmap branch: under ou=Idmap
ldap admin dn
When it comes to the "ldap admin dn" /etc/samba/smb.conf configuration parameter, use a member of the Account Admins group. For example:
ldap admin dn = uid=Account Admin,ou=System Accounts,dc=example,dc=com
smbldap-tools
In /etc/smbldap-tools/smbldap_bind.conf, use the smbldap-tools user instead of the directory's rootdn:
masterDN="uid=smbldap-tools,ou=System Accounts,dc=example,dc=com"
This user is a member of the Account Admins group. If you want to use another account, then make sure it's a member of this same group or else the default OpenLDAP ACLs won't work.
smbldap-populate
The default smbldap-populate behaviour, at least with version 0.9.2, is to create an administrator account with the following attributes:
- uidNumber = 0
- gidNumber = 0
- name: root
- member of Domain Admins
This means that a root user is created in LDAP. We advise against that and suggest to use this command line with smbldap-populate:
# smbldap-populate -a Administrator -k 1000 -m 512
This will create an user with the name Administrator, uidNumber 1000 and gidNumber 512. You can also use uidNumber 500 if you want to match windows' RID for this kind of user, but you may already have a local user with this number.
Later on the Domain Admins group could be given privileges (see "net rights grant" command), or your shares could use the admin users parameter.
IDMAP
If using IDMAP's LDAP backend in a member server, set the ldap admin dn configuration parameter in /etc/samba/smb.conf to the dn of a member of the Idmap Admins group. For example:
ldap admin dn = uid=Idmap Admin,ou=System Accounts,dc=example,dc=com
In members servers, there is no need to use the full blown Account Admin user: the Idmap Admins group is the right one as it can only write to the ou=Idmap container.
WARNING: there is a potential security vulnerability with using Idmap in LDAP. Because all domain machines need to have write access to this branch of the directory (and thus need a clear text password stored somewhere), a malicious user with root privileges on such a machine could obtain this password and create any identity mapping in ou=Idmap. See this thread for more information: http://lists.samba.org/archive/samba/2006-March/119196.html
DHCP
This DIT has support for DHCP information stored under ou=dhcp. Necessary steps:
- import /etc/dhcpd.conf data into ou=dhcp
- configure /etc/dhcpd.conf to use LDAP (with or without authentication)
Please also read the README.ldap file in the documentation directory of the dhcp-common package.
Importing data
The dhcp-common package has a contrib script which can be used to import an existing /etc/dhcpd.conf file into LDAP. The script is located at the documentation directory inside the contrib directory:
/usr/share/doc/dhcp-common-<version>/contrib/dhcpd-conf-to-ldap.pl
More experienced administrators wanting to create an LDIF file from scratch should consult the README.ldap file mentioned before.
For this example, we will import the following simple configuration file:
ddns-update-style none; subnet 172.16.10.0 netmask 255.255.255.0 { option routers 172.16.10.1; option subnet-mask 255.255.255.0; option domain-name "example.com"; option domain-name-servers 10.0.0.5; default-lease-time 21600; max-lease-time 43200; deny unknown-clients; host test009.example.com { hardware ethernet 00:C0:DF:02:93:71; fixed-address 172.16.10.5; } }
The command below creates the ldif file corresponding to our current dhcpd.conf configuration. Please note that this script has not yet been tested with all possible dhcp configuration scenarios. Always review the resulting LDIF file.
$ perl /usr/share/doc/dhcp-common-3.0.3/contrib/dhcpd-conf-to-ldap.pl \ --basedn "ou=dhcp,dc=example,dc=com" \ --dhcpdn "cn=DHCP Config,ou=dhcp,dc=example,dc=com" \ --conf /etc/dhcpd.conf --server cs4.example.com --ldif dhcpd.ldif Creating LDAP Configuration with the following options: Base DN: ou=dhcp,dc=example,dc=com DHCP DN: cn=DHCP Config,ou=dhcp,dc=example,dc=com Server DN: cn=cs4.example.com, ou=dhcp,dc=example,dc=com Done.
The options we used are:
- basedn: branch where dhcp information will be stored
- dhcpdn: entry which will contain the configuration of our server
- conf: dhcpd.conf file which will be migrated to LDAP
- server: fqdn of the dhcp server (should match the output of the hostname command)
- ldif: output ldif file
dhcpd.ldif now has the data we will import. Let's take a look:
dn: cn=cs4.example.com, ou=dhcp,dc=example,dc=com cn: cs4.example.com objectClass: top objectClass: dhcpServer dhcpServiceDN: cn=DHCP Config,ou=dhcp,dc=example,dc=com dn: cn=DHCP Config,ou=dhcp,dc=example,dc=com cn: DHCP Config objectClass: top objectClass: dhcpService dhcpPrimaryDN: cn=cs4.example.com, ou=dhcp,dc=example,dc=com dhcpStatements: ddns-update-style none dn: cn=172.16.10.0, cn=DHCP Config,ou=dhcp,dc=example,dc=com cn: 172.16.10.0 objectClass: top objectClass: dhcpSubnet objectClass: dhcpOptions dhcpNetMask: 24 dhcpStatements: default-lease-time 21600 dhcpStatements: max-lease-time 43200 dhcpStatements: deny unknown-clients dhcpOption: routers 172.16.10.1 dhcpOption: subnet-mask 255.255.255.0 dhcpOption: domain-name "example.com" dhcpOption: domain-name-servers 10.0.0.5 dn: cn=test009.example.com, cn=172.16.10.0, cn=DHCP Config,ou=dhcp,dc=example,dc=com cn: test009.example.com objectClass: top objectClass: dhcpHost dhcpHWAddress: ethernet 00:c0:df:02:93:71 dhcpStatements: fixed-address 172.16.10.5
This data can now be imported. We will use the DHCP Admin account for this:
$ ldapadd -x -D "uid=DHCP Admin,ou=System Accounts,dc=example,dc=com" -W -f dhcpd.ldif Enter LDAP Password: secretpass adding new entry "cn=cs4.example.com, ou=dhcp,dc=example,dc=com" adding new entry "cn=DHCP Config,ou=dhcp,dc=example,dc=com" adding new entry "cn=172.16.10.0, cn=DHCP Config,ou=dhcp,dc=example,dc=com" adding new entry "cn=test009.example.com, cn=172.16.10.0, cn=DHCP Config,ou=dhcp,dc=example,dc=com"
Final adjustments to dhcpd.conf
We can now remove most of the configuration from /etc/dhcpd.conf, leaving only the LDAP part. This results in the following file:
ldap-server "cs4.conectiva"; ldap-port 389; ldap-username "uid=DHCP Reader,ou=System Accounts,dc=example,dc=com"; ldap-password "dhcpreader"; ldap-base-dn "ou=dhcp,dc=example,dc=com"; ldap-method dynamic;
Above we chose to use authenticated binds, but anonymous searches can also be used: juse leave ldap-username and ldap-password out. After this last change, the dhcp server can be started and it will be consulting the LDAP tree.
Delegation
If you want to give someone DHCP administrative privileges, just put his/her dn in the DHCP Admins group. For example, to give such privileges to the user joe:
$ ldapmodify -x -D 'uid=DHCP Admin,ou=System Accounts,dc=example,dc=com' -W Enter LDAP Password: secretpass dn: cn=DHCP Admins,ou=System Groups,dc=example,dc=com changetype: modify add: member member: uid=joe,ou=People,dc=example,dc=com modifying entry "cn=DHCP Admins,ou=System Groups,dc=example,dc=com" ^D
Sudo
Sudo has support for storing its rules in a branch in an LDAP tree. See the README.LDAP file in sudo's documentation directory for specific details. Here we will show some basic examples.
Importing data
The sudo rpm package contains a script called sudoers2ldif that can be used to convert an existing /etc/sudoers file to an ldif one which can be imported into LDAP. Alternatively, since the sudo schema is reasonable simple, it can be populated by hand.
Suppose you have /etc/sudoers like this:
Defaults authenticate %webadm web.example.com=NOPASSWD: /sbin/service httpd *,/usr/local/sbin/admindomains.sh %users gateway.example.com=/usr/local/sbin/upload.sh ROOT ALL=(ALL) ALL
Converting this file into an LDIF file that can be imported at the ou=sudoers,dc=example,dc=com branch is done like this:
# export SUDOERS_BASE=ou=sudoers,dc=example,dc=com # sudoers2ldif /etc/sudoers > sudoers.ldif
The resulting ldif file contains:
dn: cn=defaults,ou=sudoers,dc=example,dc=com objectClass: top objectClass: sudoRole cn: defaults description: Default sudoOption's go here sudoOption: authenticate dn: cn=root,ou=sudoers,dc=example,dc=com objectClass: top objectClass: sudoRole cn: root sudoUser: root sudoHost: ALL sudoCommand: (ALL) ALL dn: cn=%webadm,ou=sudoers,dc=example,dc=com objectClass: top objectClass: sudoRole cn: %webadm sudoUser: %webadm sudoHost: web.example.com sudoCommand: /sbin/service httpd * sudoCommand: /usr/local/sbin/admindomains.sh sudoOption: !authenticate dn: cn=%users,ou=sudoers,dc=example,dc=com objectClass: top objectClass: sudoRole cn: %users sudoUser: %users sudoHost: gateway.example.com sudoCommand: /usr/local/sbin/upload.sh
Note that the RDN of each entry was choosen by the script to be equal to the user/group who can execute the commands, because that's basically the layout of /etc/sudoers. With LDAP, however, we could give more meaningful names for these entries, such as "cn=Web Administration" or "cn=Upload rights" for the examples above.
Anyway, this LDIF can be easily imported using the Sudo Admin account:
# ldapadd -x -D 'uid=Sudo Admin,ou=System Accounts,dc=example,dc=com' -W -f sudoers.ldif Enter LDAP Password: secretpass adding new entry "cn=defaults,ou=sudoers,dc=example,dc=com" adding new entry "cn=%webadm,ou=sudoers,dc=example,dc=com" adding new entry "cn=%users,ou=sudoers,dc=example,dc=com"
Configuring sudo
Now we have just to configure sudo to actually use our LDAP server for its configuration (or just part of the configuration).
As of this writing, the sudo package by default uses /etc/ldap.conf for the ldap configuration. This file is shared with pam_ldap and nss_ldap, and there are pros and cons for this sharing. See the URL below for an unfinished thread about this: http://archives.mandrivalinux.com/cooker/2006-05/msg00527.php
The most important setting in this file is sudoers_base, which we must point to ou=sudoers,dc=example,dc=com in our case. By default, anonymous searches of ou=sudoers are allowed, so we don't have to worry about authentication credentials now.
Finally, if you want sudo to completely ignore the contents of /etc/sudoers, then add the attribute below to the cn=defaults entry:
sudoOption: ignore_local_sudoers
Delegating
To give administrative rights over the ou=sudoers branch in LDAP, just include the user's dn in the Sudo Admins group. For example, to give such rights to the martins user:
$ ldapmodify -x -D 'uid=Sudo Admin,ou=System Accounts,dc=example,dc=com' -W Enter LDAP Password: >secretpass dn: cn=Sudo Admins,ou=System Groups,dc=example,dc=com changetype: modify add: member member: uid=martins,ou=People,dc=example,dc=com modifying entry "cn=Sudo Admins,ou=System Groups,dc=example,dc=com" ^D
Caveats
The default ACLs allow anonymous access to the ou=sudoers branch. This means that any user with network access to the LDAP server can list everybody's sudo privileges. Depending on the security policies of the site, this may or may not be wanted.
To avoid this, the ACLs could be changed to disallow anonymous access to this branch, but each machine with sudo would need to have the sudo ldap configuration file changed to have a binddn and a password. And, since this configuration file and its options are also shared among pam_ldap and nss_ldap, this means that nss_ldap would also start to use authenticated binds. See http://archives.mandrivalinux.com/cooker/2006-05/msg00527.php for a small discussion about the pros and cons.
Heimdal (kerberos)
OpenLDAP can be used as a backend for heimdal's database, meaning principal accounts can be stored in LDAP. This text will document the steps needed to integrate Heimdal's LDAP backend with OpenLDAP and openldap-mandriva-dit.
Introduction
We will start with a new realm which we will call EXAMPLE.COM. The rest of this text assumes that openldap-mandriva-dit is installed and that the supplied installation script was executed, either manually or via Fibric.
When using the LDAP backend, it's advisable to have a script to create users, because Heimdal by default will use the account structural objectClass. Since it's more common to use inetOrgPerson (or a derived class), the principal entry would need to be removed and re-added later with inetOrgPerson.
Another approach would be to first create the user with whatever means are standard (smbldap-tools, manual script, a template in gq or luma, etc.) and then add the kerberos attributes later. We will document both approaches here.
Packages
Due to conflicts with MIT's Kerberos packages, Heimdal is packaged as follows in CS4:
- heimdal-libs
- heimdal-server
- heimdal-workstation
Conflicts have been added where needed. Only heimdal-libs can be installed concurrently with MIT's libraries.
Overview of the changes
Here is a quick overview of the needed changes so that Heimdal can user OpenLDAP as its database backend, as well as use the openldap-mandriva-dit DIT:
- configure Heimdal to use LDAP for its backend
- configure OpenLDAP to accept connections from Heimdal via ldapi://
- configure OpenLDAP to map the Heimdal ldapi:// connection to an account administration DN
- test this mapping
- initialize the database
- managing user accounts
Heimdal with OpenLDAP
In order to have a database in LDAP, the following [kdc] section has to be used in Heimdal's /etc/krb5.conf:
[kdc] database = { dbname = ldap:ou=People,dc=example,dc=com mkey_file = /var/heimdal/mkey acl_file = /var/heimdal/kadmind.acl }
This will instruct Heimdal to use the OpenLDAP server installed on the same host and to use the ou=People branch for its principals. The access method Heimdal uses is ldapi://, which is a unix socket on the local filesystem, and authentication is handled by SASL EXTERNAL which we will configure in a moment.
Using ldapi://
OpenLDAP needs to be configured to accept conections via ldapi://, a local unix socket. This is done in the /etc/sysconfig/ldap file. Change the SLAPD URL list to the following:
# SLAPD URL list SLAPDURLLIST="ldap:/// ldaps:/// ldapi:///"
OpenLDAP will need to be restarted, of course.
Using SASL EXTERNAL
Heimdal uses SASL EXTERNAL to authenticate itself to the OpenLDAP server when connecting via the ldapi:// socket. When doing this, the bind dn becomes:
[root@cs4 ~]# ldapwhoami -Y EXTERNAL -H ldapi:///var/run/ldap/ldapi SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 dn:gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth Result: Success (0)
We are going to map this dn to a more meaningful binddn via authz-regexp. The slapd.conf file provided with openldap-mandriva-dit already does this, but here it is anyway for completeness:
(...) ppolicy_default "cn=default,ou=Password Policies,dc=example,dc=com" authz-regexp "gidNumber=0\\\+uidNumber=0,cn=peercred,cn=external,cn=auth" "uid=Account Admin,ou=System Accounts,dc=example,dc=com" authz-regexp ^uid=([^,]+),cn=[^,]+,cn=auth$ uid=$1,ou=People,dc=example,dc=com
With this change, and after restarting OpenLDAP, ldapwhoami now says we are an Account Admin:
[root@cs4 ~]# ldapwhoami -Y EXTERNAL -H ldapi:///var/run/ldap/ldapi SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 dn:uid=account admin,ou=system accounts,dc=example,dc=com Result: Success (0)
Note that any process connecting to the ldapi:// socket as root (uid=0, gid=0) will be treated as an Account Administrator after this change!
Initializing the realm
We can now initialize the Kerberos realm. After OpenLDAP has been restarted, run the following:
[root@cs4 ~]# kadmin -l kadmin> init EXAMPLE.COM Realm max ticket life [unlimited]:7d< Realm max renewable ticket life [unlimited]:7d kadmin>
This will create some default principals under ou=People:
ou=People krb5PrincipalName=krbtgt/[email protected],ou=People,dc=example,dc=com krb5PrincipalName=kadmin/[email protected],ou=People,dc=example,dc=com krb5PrincipalName=kadmin/[email protected],ou=People,dc=example,dc=com krb5PrincipalName=changepw/[email protected],ou=People,dc=example,dc=co krb5PrincipalName=kadmin/[email protected],ou=People,dc=example,dc=com [email protected],ou=People,dc=example,dc=com
Managing user and principal accounts
The Heimdal schema allows for principal accounts to be stored in a separate branch from the user accounts. For example, one could have the principal accounts under ou=KerberosPrincipals and user accounts under ou=People.
This has the obvious disadvantage of creating a problem with user management: when an user is removed, for example, the corresponding principal account has to be removed also. In other words, we would need a pointer in the user entry for the principal account (the seeAlso attribute is commonly used for things like this). And a script would need to follow this attribute and delete the principal account.
The advantage would be that one user could be associated with many kerberos principals using the seeAlso attribute, like john@REALM and john/admin@REALM.
But the biggest disadvantage of this scheme where principals are separated from users is integration with Samba and Ldap simple binds: it's lost. Heimdal will only update the samba password hash if it's stored in the same entry. The same with userPassword: with OpenLDAP using the smbk5pwd module (built with kerberos support), simple binds will only be able to use the kerberos password if everything is in the same entry.
Another option would be to store the principal keys and related attributes right under the user entry. We can do this because the kerberos object classes are auxiliary. So, user John would be, for example, uid=john,ou=people,dc=example,dc=com and the kerberos keys would be stored in this same entry. When this user is removed, so is the principal account. The disadvantage is that one user can only have one principal, and not several as in the previous case (where john could have john@REALM and john/admin@REALM associated with the same uid=john,ou=people,dc=example,dc=com entry).
But one issue comes up: what do we use to create this user in the first place? If we use kadmin, then it will create an entry of the form [email protected],ou=People,dc=example,dc=com with account being the structural object class. Since we tend to use a class derived from person as the structural class (such as inetOrgPerson), there is a conflict. If we use kadmin, we would have to remove the entry and re-add it with inetOrgPerson (and its mandatory attributes).
We can change the structural class that Heimdal will use, but it doesn't add the mandatory attributes so we can't just switch to inetOrgPerson in Heimdal's configuration: it will not work.
Another better option would be to first create the user with another tool, such as smbldap or another script, and later add the kerberos attributes. The main advantages are:
- RDN naming consistent with the rest of the entries (no krb5PrincipalName in the RDN if we don't want it)
- structural object class as we want it (for example, inetOrgPerson)
- user and principal accounts together under ou=People
The biggest disadvantage is that the mapping between users and principals would be 1:1, that is, one user could have at most only one kerberos principal associated with its entry.
Both schemes can be used together, however. It's actually more a question about how the accounts will be administered. So, regular users could have their kerberos keys stored in the user's entry, while administration and service keys would be stored under the same branch, but have no user associated with them. It's not very consistent with the tree (after all, ou=People was meant to host actual persons), but it works.
We will now give examples of two possibilities: using kadmin directly and using another script to first create the user account and then add kerberos attributes.
Using kadmin directly
We will create a kerberos account for the user "john" using kadmin directly. We don't even have to start heimdal at this stage because we will be using kadmin in local mode:
[root@cs4 ~]# kadmin -l kadmin> add john Max ticket life [1 day]:10h Max renewable life [1 week]:1w Principal expiration time [never]: Password expiration time [never]: Attributes []: [email protected]'s Password: secretpassword Verifying - [email protected]'s Password: secretpassword kadmin>
This creates the following entry:
dn: [email protected],ou=People,dc=example,dc=com objectClass: top objectClass: account objectClass: krb5Principal objectClass: krb5KDCEntry krb5PrincipalName: [email protected] uid: john krb5KeyVersionNumber: 0 krb5MaxLife: 36000 krb5MaxRenew: 604800 krb5KDCFlags: 126 (...)
We can obtain a ticket for this user:
# service heimdal start Starting kdc: [ OK ] [root@cs4 ~]# kinit john [email protected]'s Password: secretpassword [root@cs4 ~]# klist Credentials cache: FILE:/tmp/krb5cc_0 Principal: [email protected] Issued Expires Principal Jun 20 15:43:23 Jun 20 22:23:23 krbtgt/[email protected] [root@cs4 ~]#
Notice, however that this "john" user doesn't have the necessary posix attributes to become a system user. We will need something else to create this posix user anyway: Heimdal's role here is over.
Adding kerberos attributes to an existing user entry
If the user account already exists in the directory, then all we need to do is add the necessary Heimdal object classes to this account. Being auxiliary, this makes perfect sense.
So, for this example, we will use a pre-configured smbldap-tools package to create a sample user and then add the kerberos classes and attributes to it, but any posix user that already exists would work.
Notice we don't add the samba attributes just yet:
[root@cs4 ~]# smbldap-useradd mary [root@cs4 ~]# getent passwd mary mary:x:1001:513:System User:/home/mary:/bin/bash
The user looks like this in the directory:
dn: uid=mary,ou=People,dc=example,dc=com objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount cn: mary sn: mary givenName: mary uid: mary uidNumber: 1001 gidNumber: 513 homeDirectory: /home/mary loginShell: /bin/bash gecos: System User userPassword: {crypt}x
We will use the following LDAP modification to add the kerberos attributes and classes to this user:
[andreas@cs4 ~]$ ldapmodify -x -D 'uid=Account Admin,ou=System Accounts,dc=example,dc=com' -W Enter LDAP Password: somepass dn: uid=mary,ou=people,dc=example,dc=com changetype: modify add: objectClass objectClass: krb5Principal objectClass: krb5KDCEntry - add: krb5PrincipalName krb5PrincipalName: [email protected] - add: krb5KDCFlags krb5KDCFlags: 126 - add: krb5KeyVersionNumber krb5KeyVersionNumber: 0 modifying entry "uid=mary,ou=people,dc=example,dc=com"
Now "mary" is recognized as a kerberos principal and we can have Heimdal add the keys and other missing attributes by just invoking the password change command from as an administrator or in local admin mode:
[root@cs4 ~]# kadmin -l kadmin> passwd mary [email protected]'s Password: newpass Verifying - [email protected]'s Password: newpass kadmin>
This adds the remaining attributes and now "mary" is a full kerberos principal:
[andreas@cs4 ~]$ kinit mary [email protected]'s Password: newpass [andreas@cs4 ~]$ klist Credentials cache: FILE:/tmp/krb5cc_500 Principal: [email protected] Issued Expires Principal Jun 20 16:14:48 Jun 20 22:54:48 krbtgt/[email protected] [andreas@cs4 ~]$
And with the added bonus of being a posix account as well.
Password integration
Probably the most wanted feature of a setup where Heimdal uses OpenLDAP as its database backend is the password integration.
Three very common authentication sources in a network are Samba passwords, posix passwords and kerberos passwords. Just using LDAP doesn't magically integrate these three passwords: LDAP is just a storage and, in fact, each application uses it for itself:
- samba: sambaNTPassword, sambaLMPassword
- heimdal: krb5Key
- posix: userPassword
So, pam_ldap can change the userPassword when the user runs the password command at the console, but the heimdal key and samba hashes won't be changed. And thus we have a syncronization problem.
Some administrators run scripts to solve this, or only allow the user to change his/her password via some sort of front-end which will take care of the details of updating all password hashes. Another option that is available is to use the contributed smbk5pwd module.
smbk5pwd
The smbk5pwd module is available in the contribs directory of the OpenLDAP tarball and, when built with Samba and Kerberos support, allows for this password integration to work automatically. The module is available by default in the openldap-servers package.
This integration happens in three ways:
a) EXOP password modifications
This module intercepts OpenLDAP EXOP password modifications and updates both the Kerberos key and the Samba hashes of the same entry, if they are present. This means that a ldappasswd command, for example, will also end up changing the Samba and Kerberos passwords. Samba, when using the ldap passwd sync option in smb.conf, also ends up performing an EXOP password modification and will thus update the Kerberos key without even knowing it.
b) kpasswd
When Heimdal receives a password change request via kadmin or kpasswd, it will check if the target entry contains Samba password hashes. If it does, these hashes will also be updated. The userPassword attribute, used for simple binds, is not touched, but see below.
c) simple binds (userPassword)
Simple binds use the userPassword attribute for password verification. If this attribute contains the special hash specified {K5KEY}, then the password verification will be performed against the kerberos key of the same entry. So, in order to make simple binds use the kerberos password, all we have to do is replace the userPassword attribute with {K5KEY}.
Using smbk5pwd
The following configuration changes are necessary in order to use the smbk5pwd module:
(...) modulepath /usr/lib/openldap moduleload back_monitor.la moduleload syncprov.la moduleload ppolicy.la moduleload smbk5pwd.so password-hash {K5KEY} (...) database bdb (...) overlay ppolicy ppolicy_default "cn=default,ou=Password Policies,dc=example,dc=com" overlay smbk5pwd (...)
Openldap will now need to be able to enter the /var/heimdal directory, so change its permissions to something like this:
# chmod g+rx /var/heimdal # chgrp ldap /var/heimdal
If this permissions change is not done, openldap startup will fail.
Note we need to change the server password hash mechanism to {K5KEY}. If we don't do it, then password changes via EXOP will overwrite the userPassword attribute with the new hash instead of leaving it at {K5KEY} and we will loose our password integration.
The smbk5pwd module accepts some configuration directives like smbk5pwd-enable and smbk5pwd-must-change, please see its README file in the openldap-servers documentation directory for details.
If Samba is being used, then the ldap passwd sync option should be set to Only. With this option, Samba will only perform the EXOP password modification and expect the OpenLDAP server to update the Samba hashes, which is exactly what smbk5pwd will do:
To the [global] section of /etc/samba/smb.conf, add:
ldap passwd sync = Only
Now, test ldappasswd, smbpasswd and kpasswd: a password change performed by any of these should change all three authentication sources.
TODO
- better MTA Admins definitions/roles
- have a good MTA schema (virtual domains ready perhaps?) and ACLs for it
- sort out the read-only accounts: use a generic one instead of creating one for each service (like nssldap today). Or not. There are other services which need read access, like for example Postfix when using LDAP maps.
- always keep in mind the possibility that we may have too many system accounts: don't let this get out of control.
- add minssf support for password access?
- come up with an easy way to switch between a profile of anonymous-can-read and only-authenticated-users-can-read. Would be nice if we could add the anonynous user to a group and then just have the ACLs end in something like "by anongroup read by * none".
- provide personal address books for each user (
Bug #22658)
- try to get rid of anonymous access to ou=sudoers. We may need to rebuild sudo pointing it to another configuration file to avoid sharing stuff with nss_ldap and pam_ldap, since by default it uses /etc/ldap.conf. We can then make this other file mode 0600 owned by root:root and voilĂ , same behaviour as with the regular /etc/sudoers.
- use a global switch in the installation script so that the administrator can choose to have anonymous access enabled or not. A slightly different set of ACLs would then be used.
- heimdal uses only one branch to create its principals. Here we configured ou=People, but that is not very nice because service principals (like ldap/ldap.example.com@REALM) would also be stored there. When searching for principals, heimdal can find them anywhere on the tree, so perhaps we can get away with creating another branch for these service principals?
- benchmark to see the impact of all these ACLs!
- add support for autfs maps in LDAP