You probably know that Linux stores its user accounts in the /etc/passwd file, but can also be configured to retrieve them from a central network server such as LDAP, NIS, or Active Directory. (But everybody just deploys local accounts via Ansible.) But there's one more choice here, one that practically nobody in their right mind will use, and yet support for which
comes built in to any Glibc-based Linux system, and it's called Hesiod.
Hesiod comes from MIT's Athena computing environment – the same one which brought us the X11 graphics system, the Kerberos authentication protocol, and Zephyr as one of the oldest TCP/IP instant messaging systems.
The Hesiod protocol is very simple: information is stored in standard DNS TXT records, which – at least for users and groups – look mostly identical to /etc/passwd or /etc/group entries. All DNS entries are named
<key>.<map>.ns.<domain>, where "key" is the name or ID that you're looking up and "map" (to borrow a NIS term) is the database such as 'passwd' or 'group'.
These entries are normally kept under a subdomain to keep things tidy, and the subdomain is always named "ns" for reasons that will be explained later on.
Hesiod is also notable for having had its very own DNS record class, abbreviated
HS, next to only two other classes in existence (the
IN for Internet and the
CH that once meant Chaosnet but is now abused for metadata).
The four basic maps supported by Glibc are
service which work just like the corresponding files under /etc. (But unlike NIS, Hesiod does not carry /etc/shadow data such as password hashes – authentication is the job of Kerberos.) If you are editing your domain's zone file right now, a basic user and group entry would look like this:
$ORIGIN ns.example.com. fred.passwd TXT "fred:*:1000:100:Fred Foobar:/home/fred:/bin/sh" users.group TXT "users:*:100:fred" admins.group TXT "admins:*:101:fred"
To allow reverse queries (i.e. translate IDs back to names), for each by-name map there is also a corresponding by-ID map:
port. Entries under these maps can be just aliased to their main entry:
1000.uid CNAME fred.passwd 100.gid CNAME users.group 101.gid CNAME admins.group
Finally, groups need to have a second kind of reverse query: it needs to be possible to list all groups which have someone as a member. It seems that different implementations disagree as to whether the result should contain group names or only group IDs, but it does not hurt to include both:
fred.grplist TXT "users:100:admins:101"
Okay, this is no longer as simple as it looked in the beginning – these DNS records should really be machine-generated, as otherwise it is easy to end up with inconsistent forward and reverse maps.
It is also possible to store custom names for TCP/UDP port numbers, similar to the IANA database found in /etc/services. The format for services is slightly different in that the protocol and port are usually space-separated:
nicname.service TXT "nicname tcp 43 whois" whois.service CNAME nicname.service 43.port CNAME nicname.service
Otherwise, the same goes for IP protocols using
As mentioned earlier, support for Hesiod is always present in the Glibc library with no additional software needed (its only dependency is a DNS resolver, which Glibc needs to have anyway). However, it is not actually enabled by default – you must first load the
hesiod module in /etc/nsswitch.conf:
passwd: files hesiod group: files hesiod protocols: files hesiod services: files hesiod
Second, you must create /etc/hesiod.conf which specifies the DNS domain to use:
And… that's it. Running id fred will now perform a DNS query if the username is not found locally. Hopefully you didn't enable this on a production system.
(Older Glibc versions also allowed the
classes=IN,HS parameter to be included, to let you choose whether to make DNS queries using the IN/Internet or HS/Hesiod class, but no DNS servers accept HS queries anymore and Glibc support has been ripped out accordingly.
But wait, why is the domain configuration split into a "lhs" and a "rhs"? Why is the subdomain always
ns? That's another dubious feature, the ability to look up usernames from domains other than your default domain. If the lookup key includes an
@ character, the part that follows will override your configured "rhs" domain, while still inserting the same "lhs" subdomain.
For example, no matter what "rhs" you have configured, you can always look up
firstname.lastname@example.org as long as the "hesiod" module is active:
$ getent passwd email@example.com root:*:0:101:Wizard A Root,,,:/mit/root:/bin/csh
This might be a security issue, or it might not – your authentication system will probably prevent such "foreign" accounts from gaining any access. (I mean, don't use Hesiod in the first place. Unless you DNSSEC-sign it?)
Although Glibc has all the Hesiod stuff built in, you might have noticed that some distributions carry a stand-alone "libhesiod" package nevertheless. This library has a slightly different purpose – it allows applications to query various additional maps, and not just the four listed above.
One such map is
sloc which describes "service locations", essentially a predecesor of SRV and NAPTR records:
zephyr.sloc TXT "ARILINN.MIT.EDU"
Another Athena-specific map is
filsys which assigns shortcut names to AFS filesystem paths, allowing them to be easily mounted using
attach – this functionality appears to be called "lockers". (I think AFS itself is interesting enough that it needs a separate article.)
zephyr.filsys TXT "AFS /afs/athena.mit.edu/astaff/project/zephyr w /mit/zephyr"
The MIT email system also appears to use a
pobox map which distributes incoming mail across different servers.
Just a month after I wrote this article, Glibc 2.32 was released with the announcement that the
hesiod NSS module has been deprecated and “will be removed in a future version of glibc. System administrators are encouraged to switch to other approaches for networked account databases, such as LDAP.” This might be the right decision, but I'm still sad to see it go.