indexwritings › hesiod

The Hesiod name service

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 protocol

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).

Accounts and groups

The four basic maps supported by Glibc are passwd, group, protocol, and 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: uid, gid, protonum, and 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.

Services and protocols

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 protocol and protonum maps.

Client configuration

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:

lhs=ns
rhs=example.com

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.

Inter-domain queries

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 root@athena.mit.edu as long as the "hesiod" module is active:

$ getent passwd root@athena.mit.edu
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?)

More maps

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.

Afterword

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.