homeblogmodule of week saz sudo manage sudo configuration

Module of the Week: saz/sudo – Manage sudo configuration

Purpose Manage sudo configuration
Module saz/sudo
Puppet Version 2.6+
Platforms Debian, Ubuntu

This week we’ll be reviewing a sudo module contributed by Steffen Zieger (saz), a contributor with over 20 well maintained modules on the Forge. The saz/sudo module provides support for managing sudo configuration using Puppet.

Resource Overview

The saz/sudo module manages the following resources:
Files /etc/sudoers
Directories /etc/sudoers.d
Packages sudo
Defined types sudo::conf

Installing the module

Complexity Easy
Installation Time 2 minutes
Installing the saz/sudo module is as simple as using the puppet module tool (available as a rubygem on github; and coming soon in Puppet and Puppet Enterprise):
$ puppet config print modulepath

$ cd /etc/puppet/modules
$ puppet-module install saz/sudo
You should now have a sudo directory in your module path.

Testing the module

The saz/sudo module comes with tests—let’s run them.

$ puppet apply tests/init.pp --noop
notice: /Stage[main]/Sudo/Package[sudo]/ensure: current_value absent, should be present (noop)
notice: /Stage[main]/Sudo/File[/etc/sudoers.d/]/ensure: current_value absent, should be directory (noop)
notice: /Stage[main]/Sudo/File[/etc/sudoers]/ensure: current_value absent, should be file (noop)
notice: Class[Sudo]: Would have triggered 'refresh' from 3 events
notice: Stage[main]: Would have triggered 'refresh' from 1 events
notice: Finished catalog run in 0.06 seconds

Notice the use of the --noop flag here. Puppet’s noop, or simulation mode, allows us to see the changes that would occur without actually applying them. Without the tag we would have made actual changes to our system—in this case, our Puppet Master!

Configuring the module

Complexity Easy
Installation Time 5 minutes
This module exposes seven (7) configuration parameters using parameterized classes with defaults defined in the sudo::params class:
class sudo(
  $ensure = 'present',
  $autoupgrade = false,
  $package = $sudo::params::package,
  $config_file = $sudo::params::config_file,
  $config_file_replace = true,
  $config_dir = $sudo::params::config_dir,
  $source = $sudo::params::source
) inherits sudo::params {

This a common pattern used to create reusable modules. Let’s take a look at the manifest where the sudo::params class is defined:

# cat /etc/puppet/modules/sudo/manifests/params.pp 
class sudo::params {
  case $::operatingsystem {
    ubuntu, debian: {
      $package = 'sudo'
      $config_file = '/etc/sudoers'
      $config_dir = '/etc/sudoers.d/'
      $source = 'puppet:///modules/sudo/sudoers'
    default: {
      fail("Unsupported platform: ${::operatingsystem}")

If we step through the sudo::params class, we see on line #2 a Puppet case statement that checks the operatingsystem fact. If the operatingsystem is Ubuntu or Debian, then the $package, $config_file, $config_dir, and $source variables are set. Otherwise we get an error that the operating system is not supported.

This is a classic use of “Smart Parameter Defaults” as described in the docs on using parameterized classes. Basically the saz/sudo module manages all default values in one place, shielding the consumer of the primary sudo class from the gory details.

Example usage

Using the saz/sudo module with default values is pretty straight forward. Using a node declaration we declare the parameterized class like this:

$ cat /etc/puppet/manifest/site.pp
node 'pmotw.puppetlabs.com' {
  class { 'sudo': }
Then on the agent we issue a Puppet run:
$ puppet agent -t
info: Caching catalog for pmotw.puppetlabs.com
info: Applying configuration version '1329932829'
notice: /Stage[main]/Sudo/Package[sudo]/ensure: ensure changed 'purged' to 'present'
notice: /Stage[main]/Sudo/File[/etc/sudoers.d/]/mode: mode changed '0755' to '0550'
notice: /Stage[main]/Sudo/File[/etc/sudoers]/content:
--- /etc/sudoers	2012-02-22 12:47:50.000000000 -0500
+++ /tmp/puppet-file20120222-19626-yy7j0r-0	2012-02-22 12:47:50.000000000 -0500

At this point we have the sudo package installed and a sudo configuration file that doesn’t do very much.

If I try to use the sudo command as a non-root user, I run in to the following problem:

kelseyhightower@pmotw:~$ sudo su -

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for kelseyhightower:
kelseyhightower is not in the sudoers file.  This incident will be reported.

I’m not in the sudoers file! This is an easy fix. First we update our node definition with a sudo::conf declaration:

# cat /etc/puppet/manifests/site.pp 
node 'pmotw.puppetlabs.com' {
  class { 'sudo': }
  sudo::conf { 'kelseyhightower':
    priority => 10,
    content  => 'kelseyhightower ALL=(ALL) NOPASSWD: ALL',

After a Puppet run on the agent, we end up with the following sudo configuration file:

# cat /etc/sudoers.d/10_kelseyhightower
kelseyhightower ALL=(ALL) NOPASSWD: ALL

Lets try using the sudo command to switch to the root user:

kelseyhightower@pmotw:~$ sudo su -

That works! If I wanted to use a more complex configuration for the kelseyhightower user, I could have used a source attribute in-place of content:

# cat /etc/puppet/manifests/site.pp 
node 'pmotw.puppetlabs.com' {
  class { 'sudo': }
  sudo::conf { 'kelseyhightower':
    priority => 10,
    source   => 'puppet:///files/etc/sudoers.d/users/kelseyhightower',


The saz/sudo module sports all the characteristics of a great Puppet module:

  • Uses “Smart Parameter Defaults”
  • Exposes the right amount of class parameters and leaves more complex configuration to templates and files.
  • Serves a single purpose, managing sudo.

If you’re looking to manage sudo and related configuration, the saz/sudo module fits the bill nicely. Until next time, keep adding to the Puppet Forge!

Learn More