Another case for in-place file editing in CM tools

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

  1. That SunOS 5.10 man page is the most accurate description of netgroups in the modern world that I can find.
  2. In CFEngine 2, we do this in-place file editing with the editfiles directive and the sub-directives DeleteLinesMatching, AppendIfNoLineMatching, LocateLineMatching, ReplaceLineWith, and other friends.
  3. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *