Managing /etc/sudoers with CFEngine

Single sudoers Copy | Assembling sudoers Data

This page describes methods to manage the sudo configuration file /etc/sudoers under CFEngine. Small sites can use a single copy: statement to maintain /etc/sudoers. Larger sites may have trouble with incompatible system configuration needs or a massive sudoers file to edit. One solution: break the sudoers data into different files, then assemble /etc/sudoers on each system.

Single sudoers Copy

Store the master sudoers file under a version control repository, for example at masterfiles/etc/sudoers.

Use a copy: statement to distribute the sudoers file. If different operating systems store the sudoers data at different locations, set a variable for the dest= option. Some of the classes and variables in the following example would be defined elsewhere.

control:
any::
AddInstallable = ( check_sudoers )

some_old_system::
sudoers_file = ( /usr/local/etc/sudoers )

!some_old_system::
sudoers_file = ( /etc/sudoers )

copy:
any::
${masterfiles}/etc/sudoers
dest=${sudoers_file}
owner=root group=${zerogroup} mode=0440
backup=false
type=checksum
inform=true syslog=true
define=check_sudoers

shellcommands:
check_sudoers::
"/usr/sbin/visudo -c"

The visudo -c sanity check run on each system will report sudoers configuration errors. These could also be checked for under the version control repository, though admins may forget to run a manual test command. For simple tests, setup a Makefile with a test rule. The test could also be run automatically via a version control commit hook.

Assembling sudoers Data

The Managing Blocks of Code with Editfiles method may work, though in practice I no longer use block edits due to the complexity: encoding data into Append statements, worrying about repository-specific RCSRev variables, and data ordering problems due to a complex import: order. Instead, when using editfiles: to create the sudoers configuration file, use InsertFile statements to assemble easily edited and verified files together.

The following example assumes all sudoers configuration will be stored under the masterfiles/etc/sudoers directory under a version control repository. These files must be staged to a preperation area on each client system via a copy: statement. The editfiles block assembles the sudoers as appropriate for the defined classes on the client system. Finally, the files: statement ensures correct permissions on the sudoers file.

copy:
any::
${masterfiles}/etc/sudoers/
dest=${prep_dir}/etc/sudoers
recurse=1 backup=false purge=true
type=checksum
server=${policyhost}

editfiles:
any::
{
${sudoers_file}
AutoCreate
Backup "false"

EmptyEntireFilePlease
AppendIfNoSuchLine "# CFEngine manages this file"

InsertFile "${prep_dir}/etc/sudoers/any.conf"

BeginGroupIfDefined "some_class"
InsertFile "${prep_dir}/etc/sudoers/some_class.conf"
EndGroup

Inform "true"
DefineClasses "check_sudoers"
}

files:
any::
${sudoers_file} owner=root group=${zerogroup} mode=0440 r=0 action=fixall

Use a Makefile to check the different sudoers files for problems. If different classes need conflicting configuration, use a BeginGroupIfDefined to pull in the appropriate data. Otherwise, use other editfiles: statements to adjust the contents, though be aware that Append and so forth make testing the sudoers data in advance needlessly difficult.