Our UNIX/Linux directory services (nss) data was migrated from NIS in LDAP in 2009. Those who never administered a NIS domain may not even know what a netgroup[1] is, but we continue to make significant use of netgroups (stored in LDAP) to define groups of users and hosts for various needs. They’ve worked great for decades and serve a good purpose.
Specifically, as it relates to this blog post, we use netgroups to control the definition of user accounts for a given host.
In /etc/nsswitch.conf
... passwd: compat passwd_compat: ldap ...
For /etc/password
:
... +@mtc_users:x::::: +:x:::::/usr/rcf/bin/noaccess
and the information for /etc/shadow
to complete the definition:
... +@mtc_users::99999:::::: +::99999::::::
Since matching of usernames in /etc/passwd
is sequential, the previous states “Assuming we have not matched the username/UID above these lines already… If the username is in the netgroup mtc_users
, make use of the user’s LDAP naming services information (shell, UID, etc). If not, force the shell to be /usr/rcf/bin/noaccess
(which spits out a message and exits).”
As part of our migration from CFEngine 2 to Chef implementation, we wanted to continue with this setup. It works great for us – all information is centralized and we already have netgroups as a mechanism for defining groups of things. We don’t want or need a configuration management tool to take over our data management, nor are we interested in synching data from LDAP into some CM tool’s data world-view. To us, the user
resource in any CM tool is pretty much useless. We’re not managing 10-50000 nodes that are all identical with 3 accounts on them.
At this point, when you ask about managing files in place with CM tools other than CFEngine (which has this as a strong point[2]), the common response is “Don’t do that. Manage the whole file under CM.” We’re not about to manage /etc/passwd
and /etc/shadow
from every host on our network as unique host-specific files stored within our CM tool’s configuration code + data. These files are dynamic based on packages installed on modern Linuxes (you install the mysql-server package and you get a mysql
user account), so whatever we stored under our CM tool would be out of date quickly. Nevermind the understood insanity of the overall idea to begin with.
As the footnote[2] shows, we did this with CFEngine 2’s built-in functionality. Chef has no real in-place file editing capabilities out of the box, but when I asked about it on Twitter I found that Sean O’Meara had written an append_if_no_line
library cookbook 2 months prior. After pushing his cookbook up to our chef server, submitting a fix, and testing a few things out, I had a working solution:
if node['netgroups'].include?('mtc') [3] append_if_no_line "Allow mtc_users passwd" do path '/etc/passwd' line "+@mtc_users:x:::::" end append_if_no_line "Allow mtc_users shadow" do path '/etc/shadow' line "+@mtc_users::99999::::::" end end
I welcome any thoughts or concerns with what’s spelled out here. File editing is a real need in any flexible CM tool.
Footnotes
- That SunOS 5.10 man page is the most accurate description of netgroups in the modern world that I can find.
- In CFEngine 2, we do this in-place file editing with the
editfiles
directive and the sub-directivesDeleteLinesMatching
,AppendIfNoLineMatching
,LocateLineMatching
,ReplaceLineWith
, and other friends. - We use a separate cookbook to populate the
node['netgroups']
attribute from LDAP early in our run list. The attribute is set to the list of netgroups the node is found to be a member of.