Skip to content
June 15, 2011 / jonathanunderwood

Puppet managed deployment of SELinux modules

Today I needed to work out how to deploy a custom SELinux module across machines using Puppet.

For background, this was a small module to make an adjustment to the targetted policy as shipped with RHEL 6 to allow the Kerberos admin daemon to communicate with openldap via a unix socket (i.e. over ldapi://). I went through the usual drill on a sample machine using audit2allow to generate an SELinux module like this:

grep kadmin /var/log/audit/audit.log | audit2allow -M kadminldapilocal

This output two files: kadminldapilocal.te which is a text file describing the policy, and kadminldapilocal.pp which is a compiled binary module. The latter is the thing you need to then load with semodule -i kadminldapilocal.pp.

So, how to deploy this module across machines with puppet? Well, the first way would be to use the native puppet selmodule resource type to deploy the binary SELinux module like this:

file {'/usr/share/selinux/targeted/kadminldapilocal.pp':
  ensure  => present,
  owner   => 'root',
  group   => 'root',
  mode    => 644,
  source  => 'puppet:///kerberos/kadminldapilocal.pp',
}

selmodule {'kadminldapilocal':
  ensure => present,
  syncversion => true,
  require => File ['/usr/share/selinux/targeted/kadminldapilocal.pp'],
}

But as Benjamin Rose points out in his blog deploying managing binary SELinux has disadvantages. In particular, if you have your puppet manifests under cversion control, storing a lump of binary in there isn’t particularly meaningful. Also you become subject to binary compatibility of your SELinux module with the SELinux policy on the machines you’re deploying to. I don’t actually know how much of an issue this is in practice, but I could imagine it might be a problem if you have a heterogeneous operating system environment. Anyway, I liked the approach that Ben outlined in his blog post for deploying the SELinux module via the text (.te) files rather than the binary modules, but wanted to reinvent it such that it was self contained within puppet rather than relying on a client side script. So this is what I came up with:

class semodloader ($moddir = '/usr/local/share/selinux') {

  package { ['policycoreutils',
             'checkpolicy',
             ]: ensure => latest}

  file {$moddir:
    ensure  => directory,
    owner   => 'root',
    group   => 'root',
    mode    => 755,
    require => [ Package['policycoreutils'],
                 Package['checkpolicy'],
                 ],
  }

  define semodule ($source, $status = 'present') {
    case $status {
      present: {
        file {"${semodloader::moddir}/${name}.te":
          owner    => 'root',
          group    => 'root',
          mode     => 644,
          source   => $source,
          require => File ["${semodloader::moddir}"],
        }

        file {"${semodloader::moddir}/${name}.mod":
          owner    => 'root',
          group    => 'root',
          mode     => 644,
          require => File ["${semodloader::moddir}"],
        }

        file {"${semodloader::moddir}/${name}.pp":
          owner    => 'root',
          group    => 'root',
          mode     => 644,
          require => File ["${semodloader::moddir}"],
        }

        exec {"${name}-buildmod":
          command     => "checkmodule -M -m -o ${name}.mod ${name}.te",
          path        => ['/sbin', '/usr/sbin', '/bin', '/usr/bin'],
          cwd         => "${semodloader::moddir}",
          subscribe   => File ["${semodloader::moddir}/${name}.te"],
          refreshonly => true,
        }

        exec {"${name}-buildpp":
          command     => "semodule_package -m ${name}.mod -o ${name}.pp",
          path        => ['/sbin', '/usr/sbin', '/bin', '/usr/bin'],
          cwd         => "${semodloader::moddir}",
          subscribe   => File ["${semodloader::moddir}/${name}.mod"],
          refreshonly => true,
        }

        exec {"${name}-install":
          command     => "semodule -i ${name}.pp",
          path        => ['/sbin', '/usr/sbin', '/bin', '/usr/bin'],
          cwd         => "${semodloader::moddir}",
          subscribe   => File ["${semodloader::moddir}/${name}.pp"],
          refreshonly => true,
        }
        # Alternatively:
        # selmodule {$module:
        #   ensure => $ensure,
        #   syncversion => true,
        #   require => File ["$moddir/$name.pp"],
        # }
      }

      absent: {
        file {"${semodloader::moddir}/${name}.te":
          ensure => absent,
        }
        file {"${semodloader::moddir}/${name}.mod":
          ensure => absent,
        }
        file {"${semodloader::moddir}/${name}.pp":
          ensure => absent,
        }

       exec {"${name}-remove":
          command     => "semodule -r ${name}.pp > /dev/null 2>&1",
          path        => ['/sbin', '/usr/sbin', '/bin', '/usr/bin'],
        }
      }

      default: {
        fail("status variable not recognized")
      }
    }
  }
}

So, to use this to load my kadminldapilocal module, I simply use:

  class {'semodloader': }
  semodloader::semodule {'kadminldapilocal':
   source => 'puppet:///kerberos/kadminldapilocal.te',
   status => 'present',
 }

And, changing status => 'present' to status => 'absent' will trigger unloading and removing the module.

Would welcome comments on this, as I am new to puppet (started using it this week), and so can’t help thinking there must be a simpler way of doing this!

Advertisement

4 Comments

Leave a Comment
  1. James Fryman / Aug 17 2011 5:03 pm

    Hi,

    I actually really like this code block. Thanks for posting it. I’m incorporating it into a SELinux management module I’m building. I’ve put a link to some of the optimizations that I’ve made.

    https://gist.github.com/1152021

    Take a look and let me know if you have any questions or feedback.

    Thanks.

    -James
    james@frymanet.com

  2. jonathanunderwood / Aug 17 2011 11:05 pm

    Hi James,

    Your work looks very interesting, thanks for the pointer, I’ll have a look through it carefully when I get chance. I should say though that the code block I posted had a number of problems with it such that I refined it over the following few weeks. I’ll make a new post with the updated code.

Trackbacks

  1. Deploying SELinux modules with Puppet (reprise) « Coding blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.