This is a followup on my previous work on merging all the computer related LDAP objects in Debian Edu.
As a step to try to see if it possible to merge the DNS and DHCP LDAP objects, I have had a look at how the packages pdns-backend-ldap and dhcp3-server-ldap in Debian use the LDAP server. The two implementations are quite different in how they use LDAP.
To get this information, I started slapd with debugging enabled and dumped the debug output to a file to get the LDAP searches performed on a Debian Edu main-server. Here is a summary.powerdns
Clues on how to set up PowerDNS to use a LDAP backend is available on the web.PowerDNS have two modes of operation using LDAP as its backend. One "strict" mode where the forward and reverse DNS lookups are done using the same LDAP objects, and a "tree" mode where the forward and reverse entries are in two different subtrees in LDAP with a structure based on the DNS names, as in tjener.intern and 2.2.0.10.in-addr.arpa.
In tree mode, the server is set up to use a LDAP subtree as its base, and uses a "base" scoped search for the DNS name by adding "dc=tjener,dc=intern," to the base with a filter for "(associateddomain=tjener.intern)" for the forward entry and "dc=2,dc=2,dc=0,dc=10,dc=in-addr,dc=arpa," with a filter for "(associateddomain=2.2.0.10.in-addr.arpa)" for the reverse entry. For forward entries, it is looking for attributes named dnsttl, arecord, nsrecord, cnamerecord, soarecord, ptrrecord, hinforecord, mxrecord, txtrecord, rprecord, afsdbrecord, keyrecord, aaaarecord, locrecord, srvrecord, naptrrecord, kxrecord, certrecord, dsrecord, sshfprecord, ipseckeyrecord, rrsigrecord, nsecrecord, dnskeyrecord, dhcidrecord, spfrecord and modifytimestamp. For reverse entries it is looking for the attributes dnsttl, arecord, nsrecord, cnamerecord, soarecord, ptrrecord, hinforecord, mxrecord, txtrecord, rprecord, aaaarecord, locrecord, srvrecord, naptrrecord and modifytimestamp. The equivalent ldapsearch commands could look like this:
ldapsearch -h ldap \ -b dc=tjener,dc=intern,ou=hosts,dc=skole,dc=skolelinux,dc=no \ -s base -x '(associateddomain=tjener.intern)' dNSTTL aRecord nSRecord \ cNAMERecord sOARecord pTRRecord hInfoRecord mXRecord tXTRecord \ rPRecord aFSDBRecord KeyRecord aAAARecord lOCRecord sRVRecord \ nAPTRRecord kXRecord certRecord dSRecord sSHFPRecord iPSecKeyRecord \ rRSIGRecord nSECRecord dNSKeyRecord dHCIDRecord sPFRecord modifyTimestamp ldapsearch -h ldap \ -b dc=2,dc=2,dc=0,dc=10,dc=in-addr,dc=arpa,ou=hosts,dc=skole,dc=skolelinux,dc=no \ -s base -x '(associateddomain=2.2.0.10.in-addr.arpa)' dnsttl, arecord, nsrecord, cnamerecord soarecord ptrrecord \ hinforecord mxrecord txtrecord rprecord aaaarecord locrecord \ srvrecord naptrrecord modifytimestamp
In Debian Edu/Lenny, the PowerDNS tree mode is used with ou=hosts,dc=skole,dc=skolelinux,dc=no as the base, and these are two example LDAP objects used there. In addition to these objects, the parent objects all th way up to ou=hosts,dc=skole,dc=skolelinux,dc=no also exist.
dn: dc=tjener,dc=intern,ou=hosts,dc=skole,dc=skolelinux,dc=no objectclass: top objectclass: dnsdomain objectclass: domainrelatedobject dc: tjener arecord: 10.0.2.2 associateddomain: tjener.intern dn: dc=2,dc=2,dc=0,dc=10,dc=in-addr,dc=arpa,ou=hosts,dc=skole,dc=skolelinux,dc=no objectclass: top objectclass: dnsdomain2 objectclass: domainrelatedobject dc: 2 ptrrecord: tjener.intern associateddomain: 2.2.0.10.in-addr.arpa
In strict mode, the server behaves differently. When looking for forward DNS entries, it is doing a "subtree" scoped search with the same base as in the tree mode for a object with filter "(associateddomain=tjener.intern)" and requests the attributes dnsttl, arecord, nsrecord, cnamerecord, soarecord, ptrrecord, hinforecord, mxrecord, txtrecord, rprecord, aaaarecord, locrecord, srvrecord, naptrrecord and modifytimestamp. For reverse entires it also do a subtree scoped search but this time the filter is "(arecord=10.0.2.2)" and the requested attributes are associateddomain, dnsttl and modifytimestamp. In short, in strict mode the objects with ptrrecord go away, and the arecord attribute in the forward object is used instead.
The forward and reverse searches can be simulated using ldapsearch like this:
ldapsearch -h ldap -b ou=hosts,dc=skole,dc=skolelinux,dc=no -s sub -x \ '(associateddomain=tjener.intern)' dNSTTL aRecord nSRecord \ cNAMERecord sOARecord pTRRecord hInfoRecord mXRecord tXTRecord \ rPRecord aFSDBRecord KeyRecord aAAARecord lOCRecord sRVRecord \ nAPTRRecord kXRecord certRecord dSRecord sSHFPRecord iPSecKeyRecord \ rRSIGRecord nSECRecord dNSKeyRecord dHCIDRecord sPFRecord modifyTimestamp ldapsearch -h ldap -b ou=hosts,dc=skole,dc=skolelinux,dc=no -s sub -x \ '(arecord=10.0.2.2)' associateddomain dnsttl modifytimestamp
In addition to the forward and reverse searches , there is also a search for SOA records, which behave similar to the forward and reverse lookups.
A thing to note with the PowerDNS behaviour is that it do not specify any objectclass names, and instead look for the attributes it need to generate a DNS reply. This make it able to work with any objectclass that provide the needed attributes.
The attributes are normally provided in the cosine (RFC 1274) and dnsdomain2 schemas. The latter is used for reverse entries like ptrrecord and recent DNS additions like aaaarecord and srvrecord.
In Debian Edu, we have created DNS objects using the object classes dcobject (for dc), dnsdomain or dnsdomain2 (structural, for the DNS attributes) and domainrelatedobject (for associatedDomain). The use of structural object classes make it impossible to combine these classes with the object classes used by DHCP.
There are other schemas that could be used too, for example the dnszone structural object class used by Gosa and bind-sdb for the DNS attributes combined with the domainrelatedobject object class, but in this case some unused attributes would have to be included as well (zonename and relativedomainname).
My proposal for Debian Edu would be to switch PowerDNS to strict mode and not use any of the existing objectclasses (dnsdomain, dnsdomain2 and dnszone) when one want to combine the DNS information with DHCP information, and instead create a auxiliary object class defined something like this (using the attributes defined for dnsdomain and dnsdomain2 or dnszone):
objectclass ( some-oid NAME 'dnsDomainAux' SUP top AUXILIARY MAY ( ARecord $ MDRecord $ MXRecord $ NSRecord $ SOARecord $ CNAMERecord $ DNSTTL $ DNSClass $ PTRRecord $ HINFORecord $ MINFORecord $ TXTRecord $ SIGRecord $ KEYRecord $ AAAARecord $ LOCRecord $ NXTRecord $ SRVRecord $ NAPTRRecord $ KXRecord $ CERTRecord $ A6Record $ DNAMERecord ))
This will allow any object to become a DNS entry when combined with the domainrelatedobject object class, and allow any entity to include all the attributes PowerDNS wants. I've sent an email to the PowerDNS developers asking for their view on this schema and if they are interested in providing such schema with PowerDNS, and I hope my message will be accepted into their mailing list soon.
ISC dhcp
The DHCP server searches for specific objectclass and requests all the object attributes, and then uses the attributes it want. This make it harder to figure out exactly what attributes are used, but thanks to the working example in Debian Edu I can at least get an idea what is needed without having to read the source code.
In the DHCP server configuration, the LDAP base to use and the search filter to use to locate the correct dhcpServer entity is stored. These are the relevant entries from /etc/dhcp3/dhcpd.conf:
ldap-base-dn "dc=skole,dc=skolelinux,dc=no"; ldap-dhcp-server-cn "dhcp";
The DHCP server uses this information to nest all the DHCP configuration it need. The cn "dhcp" is located using the given LDAP base and the filter "(&(objectClass=dhcpServer)(cn=dhcp))". The search result is this entry:
dn: cn=dhcp,dc=skole,dc=skolelinux,dc=no cn: dhcp objectClass: top objectClass: dhcpServer dhcpServiceDN: cn=DHCP Config,dc=skole,dc=skolelinux,dc=no
The content of the dhcpServiceDN attribute is next used to locate the subtree with DHCP configuration. The DHCP configuration subtree base is located using a base scope search with base "cn=DHCP Config,dc=skole,dc=skolelinux,dc=no" and filter "(&(objectClass=dhcpService)(|(dhcpPrimaryDN=cn=dhcp,dc=skole,dc=skolelinux,dc=no)(dhcpSecondaryDN=cn=dhcp,dc=skole,dc=skolelinux,dc=no)))". The search result is this entry:
dn: cn=DHCP Config,dc=skole,dc=skolelinux,dc=no cn: DHCP Config objectClass: top objectClass: dhcpService objectClass: dhcpOptions dhcpPrimaryDN: cn=dhcp, dc=skole,dc=skolelinux,dc=no dhcpStatements: ddns-update-style none dhcpStatements: authoritative dhcpOption: smtp-server code 69 = array of ip-address dhcpOption: www-server code 72 = array of ip-address dhcpOption: wpad-url code 252 = text
Next, the entire subtree is processed, one level at the time. When all the DHCP configuration is loaded, it is ready to receive requests. The subtree in Debian Edu contain objects with object classes top/dhcpService/dhcpOptions, top/dhcpSharedNetwork/dhcpOptions, top/dhcpSubnet, top/dhcpGroup and top/dhcpHost. These provide options and information about netmasks, dynamic range etc. Leaving out the details here because it is not relevant for the focus of my investigation, which is to see if it is possible to merge dns and dhcp related computer objects.
When a DHCP request come in, LDAP is searched for the MAC address of the client (00:00:00:00:00:00 in this example), using a subtree scoped search with "cn=DHCP Config,dc=skole,dc=skolelinux,dc=no" as the base and "(&(objectClass=dhcpHost)(dhcpHWAddress=ethernet 00:00:00:00:00:00))" as the filter. This is what a host object look like:
dn: cn=hostname,cn=group1,cn=THINCLIENTS,cn=DHCP Config,dc=skole,dc=skolelinux,dc=no cn: hostname objectClass: top objectClass: dhcpHost dhcpHWAddress: ethernet 00:00:00:00:00:00 dhcpStatements: fixed-address hostname
There is less flexiblity in the way LDAP searches are done here. The object classes need to have fixed names, and the configuration need to be stored in a fairly specific LDAP structure. On the positive side, the invidiual dhcpHost entires can be anywhere without the DN pointed to by the dhcpServer entries. The latter should make it possible to group all host entries in a subtree next to the configuration entries, and this subtree can also be shared with the DNS server if the schema proposed above is combined with the dhcpHost structural object class.
Conclusion
The PowerDNS implementation seem to be very flexible when it come to which LDAP schemas to use. While its "tree" mode is rigid when it come to the the LDAP structure, the "strict" mode is very flexible, allowing DNS objects to be stored anywhere under the base cn specified in the configuration.
The DHCP implementation on the other hand is very inflexible, both regarding which LDAP schemas to use and which LDAP structure to use. I guess one could implement ones own schema, as long as the objectclasses and attributes have the names used, but this do not really help when the DHCP subtree need to have a fairly fixed structure.
Based on the observed behaviour, I suspect a LDAP structure like this might work for Debian Edu:
ou=services cn=machine-info (dhcpService) - dhcpServiceDN points here cn=dhcp (dhcpServer) cn=dhcp-internal (dhcpSharedNetwork/dhcpOptions) cn=10.0.2.0 (dhcpSubnet) cn=group1 (dhcpGroup/dhcpOptions) cn=dhcp-thinclients (dhcpSharedNetwork/dhcpOptions) cn=192.168.0.0 (dhcpSubnet) cn=group1 (dhcpGroup/dhcpOptions) ou=machines - PowerDNS base points here cn=hostname (dhcpHost/domainrelatedobject/dnsDomainAux)
This is not tested yet. If the DHCP server require the dhcpHost entries to be in the dhcpGroup subtrees, the entries can be stored there instead of a common machines subtree, and the PowerDNS base would have to be moved one level up to the machine-info subtree.
The combined object under the machines subtree would look something like this:
One could even add the LTSP configuration associated with a given machine, as long as the required attributes are available in a auxiliary object class.dn: dc=hostname,ou=machines,cn=machine-info,dc=skole,dc=skolelinux,dc=no dc: hostname objectClass: top objectClass: dhcpHost objectclass: domainrelatedobject objectclass: dnsDomainAux associateddomain: hostname.intern arecord: 10.11.12.13 dhcpHWAddress: ethernet 00:00:00:00:00:00 dhcpStatements: fixed-address hostname.intern