Deploying sudo rules via puppet, the Kumina way

We wanted to give some of our customers the ability to restart some of their own services on their development environment. To be able to do this we made a puppet module. Augeas can also alter the /etc/sudoers file, but it didn’t fit into our way of working with puppet.

The module is called gen_sudo, as per our puppet module naming conventions and works on Debian (and should be easily hacked to suit your needs). You can add a rule as follows:

class web::dev_sites {
  include gen_sudo
  [..........]
  gen_sudo::rule { "Devs can semi-control apache":
    entity => "%devs",
    as_user => "root",
    password_required => false,
    command => ["/etc/init.d/apache2 restart",
      "/etc/init.d/apache2 reload",
      "/etc/init.d/apache2 start"]
  [..........]
}


Depending on the Debian version it either uses puppet-concat to create fragment for /etc/sudoers (Lenny and older) or it puts a file in /etc/sudoers.d/ (Squeeze and newer) containing:

# Devs can semi-control apache
%devs ALL=(root) NOPASSWD: /etc/init.d/apache2 restart
%devs ALL=(root) NOPASSWD: /etc/init.d/apache2 reload
%devs ALL=(root) NOPASSWD: /etc/init.d/apache2 start

This module has 2 features which make life easy, namely: removal of a gen_sudo::rule from a manifest implies ensure => absent, so the rule will be removed after the next puppet run. The second is that the command parameter accepts a string or an array of strings, that way you can give the same entity a set of commands it can run.

As you can see the host statement is ALL. This is done on purpose, because the rules are deployed only on the nodes that have included the web::dev_sites and won’t show up on production systems.

The code is available on GitHub.

Tags: , ,


2 Responses to “Deploying sudo rules via puppet, the Kumina way”

  1. Marcellods says:

    Just wondering…What was exactly your problem with augeas ?
    Wouldn’t you achieve a similar result with:

    augeas { “dev_sites”:
    context => “/files/etc/sudoers”,
    changes => [
    “set spec[user = ‘%devs’]/user %devs”,
    “set spec[user = ‘%devs’]/host_group/host ALL”,
    “set spec[user = ‘%devs’]/host_group/command[1] \”/etc/init.d/apache2 restart\””,
    “set spec[user = ‘%devs’]/host_group/command[2] \”/etc/init.d/apache2 reload\””,
    “set spec[user = ‘%devs’]/host_group/command[3] \”/etc/init.d/apache2 start\””,
    “set spec[user = ‘%devs’]/host_group/command[1]/tag NOPASSWD”,
    ],
    }

    Cheers,
    Marcello

  2. Pieter Lexis says:

    Hi Marcello,

    We could probably get the same result with the recipe you posted, however: we have customers with very specific rules for certain servers(in the order of 30 commands per user, principle of least privilege), which makes the ability to specify an array of commands less cumbersome than using “set spec..” for every command(could be done in a define though), in the example in the blogpost the recipe has 247 characters, compared to 502 in your example.

    Plus, I don’t know if augeas has an implied ensure=>absent when you later remove a rule from your manifest.

    You are free to fork the repo, write a define that uses augeas and send us a pull request :).

    Cheers,
    Pieter

Leave a Reply