diff --git a/README.md b/README.md index d4d0a3aa..715c6fbe 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,15 @@ running system. * Mailinglist: ([groups.io Webinterface](https://groups.io/g/voxpupuli/topics)) +## Upgrading from puppet-selinux 0.8.x + +* Previously, module building always used the refpolicy framework. The default + module builder is now 'simple', which uses only checkmodule. Not all features are + supported with this builder. + + To build modules using the refpolicy framework like previous versions did, + specify the 'refpolicy' builder either explicitly per module or globally + via the main class ## Known problems / limitations @@ -51,8 +60,6 @@ running system. does) the order is important. If you add /my/folder before /my/folder/subfolder only /my/folder will match (limitation of SELinux). There is no such limitation to file-contexts defined in SELinux modules. (GH-121) -* `selinux::module` only allows to add a type enforcment file (`*.te`) but no - interfaces (`*.if`) or file-contexts (`*.fc`). * While SELinux is disabled the defined types `selinux::boolean`, `selinux::fcontext`, `selinux::port` will produce puppet agent runtime errors because the used tools fail. @@ -97,12 +104,15 @@ are `target`, `minimum`, and `mls`). Note that disabling SELinux requires a rebo to fully take effect. It will run in `permissive` mode until then. -### Deploy a custom module +### Deploy a custom module using the refpolicy framework ```puppet selinux::module { 'resnet-puppet': - ensure => 'present', - source => 'puppet:///modules/site_puppet/site-puppet.te', + ensure => 'present', + source_te => 'puppet:///modules/site_puppet/site-puppet.te', + source_fc => 'puppet:///modules/site_puppet/site-puppet.fc', + source_if => 'puppet:///modules/site_puppet/site-puppet.if', + builder => 'refpolicy' } ``` @@ -131,9 +141,9 @@ selinux::boolean { 'puppetagent_manage_all_files': } * run acceptance tests: ``` -BEAKER_debug=yes BEAKER_set="centos-6-x64" PUPPET_INSTALL_TYPE="agent" bundle exec rake beaker -BEAKER_debug=yes BEAKER_set="centos-7-x64" PUPPET_INSTALL_TYPE="agent" bundle exec rake beaker -BEAKER_debug=yes BEAKER_set="fedora-24-x64" PUPPET_INSTALL_TYPE="agent" bundle exec rake beaker +BEAKER_debug=yes BEAKER_set="centos-6-x64" PUPPET_INSTALL_TYPE="agent" bundle exec rake beaker && +BEAKER_debug=yes BEAKER_set="centos-7-x64" PUPPET_INSTALL_TYPE="agent" bundle exec rake beaker && +BEAKER_debug=yes BEAKER_set="fedora-24-x64" PUPPET_INSTALL_TYPE="agent" bundle exec rake beaker && BEAKER_debug=yes BEAKER_set="fedora-25-x64" PUPPET_INSTALL_TYPE="agent" bundle exec rake beaker ``` diff --git a/files/selinux_build_module_simple.sh b/files/selinux_build_module_simple.sh new file mode 100644 index 00000000..4107281f --- /dev/null +++ b/files/selinux_build_module_simple.sh @@ -0,0 +1,16 @@ +#!/bin/sh +module_name="$1" +module_dir="$2" + +set -e + +cd $module_dir +test -d tmp || mkdir tmp + +checkmodule -M -m -o tmp/${module_name}.mod ${module_name}.te +package_args="-o ${module_name}.pp -m tmp/${module_name}.mod" +if [ -s "${module_name}.fc" ]; then + package_args="${package_args} --fc ${module_name}.fc" +fi + +semodule_package ${package_args} diff --git a/lib/facter/selinux_agent_vardir.rb b/lib/facter/selinux_agent_vardir.rb new file mode 100644 index 00000000..ab82bfd3 --- /dev/null +++ b/lib/facter/selinux_agent_vardir.rb @@ -0,0 +1,6 @@ +require 'puppet' +Facter.add(:selinux_agent_vardir) do + setcode do + Puppet.settings['vardir'] + end +end diff --git a/manifests/config.pp b/manifests/config.pp index d4bc7536..e4431f33 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -7,30 +7,38 @@ # # It is included in the main class ::selinux # +# +# +# Config for module building +# -------------------------- +# +# The module building requires the following file structure: +# +# ``` +# $module_build_root/ +# bin/ # for simple module build script +# modules/ # module source files and compiled policies +# modules/tmp # repolicy tempfiles (created by scripts) +# ``` +# # @param mode See main class # @param type See main class # @param manage_package See main class # @param package_name See main class -# @param sx_mod_dir See main class +# @param module_build_root See main class # class selinux::config ( - $mode = $::selinux::mode, - $type = $::selinux::type, - $sx_mod_dir = $::selinux::sx_mod_dir, - $manage_package = $::selinux::manage_package, - $package_name = $::selinux::package_name, + $mode = $::selinux::mode, + $type = $::selinux::type, + $manage_package = $::selinux::manage_package, + $package_name = $::selinux::package_name, + $module_build_root = $::selinux::module_build_root ) { if $caller_module_name != $module_name { fail("Use of private class ${name} by ${caller_module_name}") } - file { $sx_mod_dir: - ensure => directory, - owner => 'root', - group => 'root', - } - if ($mode == 'enforcing' and !$::selinux) { notice('SELinux is disabled. Forcing configuration to permissive to avoid problems. To disable this warning, explicitly set selinux::mode to permissive or disabled.') $_real_mode = 'permissive' @@ -86,4 +94,45 @@ match => '^SELINUXTYPE=\w+', } } + + # Module build config: + validate_absolute_path($module_build_root) + + file {$module_build_root: + ensure => 'directory', + owner => 'root', + group => 'root', + mode => '0755', + } + + file {"${module_build_root}/bin": + ensure => 'directory', + owner => 'root', + group => 'root', + mode => '0755', + } + + # put helper in place: + file {"${module_build_root}/bin/selinux_build_module_simple.sh": + ensure => 'present', + owner => 'root', + group => 'root', + mode => '0755', + source => "puppet:///modules/${module_name}/selinux_build_module_simple.sh", + } + + $module_build_dir = "${module_build_root}/modules" + + file {$module_build_dir: + ensure => 'directory', + owner => 'root', + group => 'root', + recurse => true, + purge => true, + force => true, + } + + # created by refpolicy builder and our simple builder + # ensure it does not get purged + file {"${module_build_dir}/tmp": selinux_ignore_defaults => true } } diff --git a/manifests/init.pp b/manifests/init.pp index 0d96a266..0181a293 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -14,16 +14,18 @@ # @param type sets the selinux type # Default value: undef # Allowed values: (targeted|minimum|mls|undef) -# @param sx_mod_dir directory where to store puppet managed selinux modules -# Default value: /usr/share/selinux -# Allowed values: absolute path -# @param makefile the path to the systems SELinux makefile +# @param refpolicy_makefile the path to the system's SELinux makefile for the refpolicy framework # Default value: /usr/share/selinux/devel/Makefile # Allowed value: absolute path -# @param manage_package manage the package for selinux tools +# @param manage_package manage the package for selinux tools and refpolicy # Default value: true # @param package_name sets the name for the selinux tools package # Default value: OS dependent (see params.pp) +# @param refpolicy_package_name sets the name for the refpolicy development package, required for the +# refpolicy module builder +# Default value: OS dependent (see params.pp) +# @param default_builder which builder to use by default with selinux::module +# Default value: simple # @param boolean Hash of selinux::boolean resource parameters # @param fcontext Hash of selinux::fcontext resource parameters # @param module Hash of selinux::module resource parameters @@ -31,12 +33,14 @@ # @param port Hash of selinux::port resource parameters # class selinux ( - $mode = $::selinux::params::mode, - $type = $::selinux::params::type, - $sx_mod_dir = $::selinux::params::sx_mod_dir, - $makefile = $::selinux::params::makefile, - $manage_package = $::selinux::params::manage_package, - $package_name = $::selinux::params::package_name, + Optional[Enum['enforcing', 'permissive', 'disabled']] $mode = $::selinux::params::mode, + Optional[Enum['targeted', 'minimum', 'mls']] $type = $::selinux::params::type, + String $refpolicy_makefile = $::selinux::params::refpolicy_makefile, + Boolean $manage_package = $::selinux::params::manage_package, + String $package_name = $::selinux::params::package_name, + String $refpolicy_package_name = $::selinux::params::refpolicy_package_name, + String $module_build_root = $::selinux::params::module_build_root, + Enum['refpolicy', 'simple'] $default_builder = 'simple', ### START Hiera Lookups ### $boolean = undef, @@ -48,23 +52,6 @@ ) inherits selinux::params { - $mode_real = $mode ? { - /\w+/ => $mode, - default => 'undef', - } - - $type_real = $type ? { - /\w+/ => $type, - default => 'undef', - } - - validate_absolute_path($sx_mod_dir) - validate_re($mode_real, ['^enforcing$', '^permissive$', '^disabled$', '^undef$'], "Valid modes are enforcing, permissive, and disabled. Received: ${mode}") - validate_re($type_real, ['^targeted$', '^minimum$', '^mls$', '^undef$'], "Valid types are targeted, minimum, and mls. Received: ${type}") - validate_string($makefile) - validate_bool($manage_package) - validate_string($package_name) - class { '::selinux::package': manage_package => $manage_package, package_name => $package_name, diff --git a/manifests/module.pp b/manifests/module.pp index 5e4c2ed2..94a89d13 100644 --- a/manifests/module.pp +++ b/manifests/module.pp @@ -7,77 +7,126 @@ # Concepts incorporated from: # http://stuckinadoloop.wordpress.com/2011/06/15/puppet-managed-deployment-of-selinux-modules/ # -# @example compile and load the apache module +# @example compile and load the apache module - does not require make or the policy +# devel package # selinux::module{ 'apache': -# ensure => 'present', -# source => 'puppet:///modules/selinux/apache.te', +# ensure => 'present', +# source_te => 'puppet:///modules/selinux/apache.te', +# builder => 'simple' +# } +# +# @example compile a module the refpolicy way. It will install the policy devel and +# dependent packages like make. +# selinux::module{ 'mymodule': +# ensure => 'present', +# source_te => 'puppet:///modules/profile/selinux/mymodule.te', +# source_fc => 'puppet:///modules/profile/selinux/mymodule.fc', +# source_if => 'puppet:///modules/profile/selinux/mymodule.if', +# builder => 'simple' # } # # @param ensure present or absent -# @param sx_mod_dir path where source is stored and the module built. -# Valid values: absolute path -# @param source the source file (either a puppet URI or local file) of the SELinux .te file -# @param content content of the source .te file -# @param makefile absolute path to the selinux-devel Makefile -# @param prefix (DEPRECATED) the prefix to add to the loaded module. Defaults to ''. -# Does not work with CentOS >= 7.2 and Fedora >= 24 SELinux tools. +# @param source_te the source file (either a puppet URI or local file) of the SELinux .te file +# @param source_fc the source file (either a puppet URI or local file) of the SELinux .fc file +# @param source_if the source file (either a puppet URI or local file) of the SELinux .if file +# @param builder either 'simple' or 'refpolicy'. The simple builder attempts to use checkmodule +# to build the module, whereas 'refpolicy' uses the refpolicy framework, but requires 'make' # @param syncversion selmodule syncversion param define selinux::module( - $source = undef, - $content = undef, - $ensure = 'present', - $makefile = '/usr/share/selinux/devel/Makefile', - $prefix = '', - $sx_mod_dir = '/usr/share/selinux', - $syncversion = undef, + Optional[String] $source_te = undef, + Optional[String] $source_fc = undef, + Optional[String] $source_if = undef, + Enum['absent', 'present'] $ensure = 'present', + Optional[Enum['simple', 'refpolicy']] $builder = undef, ) { - include ::selinux + if $builder == 'refpolicy' { + require ::selinux::refpolicy_package + } + + if ($builder == 'simple' and $source_if != undef) { + fail("The simple builder does not support the 'source_if' parameter") + } + + # let's just make doubly sure that this is an absolute path: + validate_absolute_path($::selinux::config::module_build_dir) + validate_absolute_path($::selinux::refpolicy_makefile) + + $module_dir = $::selinux::config::module_build_dir + $module_file = "${module_dir}/${title}" + + $build_command = pick($builder, $::selinux::default_builder, 'none') ? { + 'simple' => shellquote("${::selinux::module_build_root}/bin/selinux_build_module_simple.sh", $title, $module_dir), + 'refpolicy' => shellquote('make', '-f', $::selinux::refpolicy_makefile, "${title}.pp"), + 'none' => fail('No builder or default builder specified') + } + Anchor['selinux::module pre'] -> Selinux::Module[$title] -> Anchor['selinux::module post'] + $has_source = (pick($source_te, $source_fc, $source_if, false) != false) - validate_re($ensure, [ '^present$', '^absent$' ], '$ensure must be "present" or "absent"') - if $ensure == 'present' and $source == undef and $content == undef { - fail("You must provide 'source' or 'content' field for selinux module") - } - if $source != undef { - validate_string($source) - } - if $content != undef { - validate_string($content) - } - validate_string($prefix) - validate_absolute_path($sx_mod_dir) - validate_absolute_path($makefile) - if $syncversion != undef { - validate_bool($syncversion) - } + if $has_source and $ensure == 'present' { + file {"${module_file}.te": + ensure => 'file', + source => $source_te, + notify => Exec["clean-module-${title}"], + } - ## Begin Configuration - file { "${sx_mod_dir}/${prefix}${name}.te": - ensure => $ensure, - owner => 'root', - group => 'root', - mode => '0644', - source => $source, - content => $content, + $content_fc = $source_fc ? { undef => '', default => undef } + file {"${module_file}.fc": + ensure => 'file', + source => $source_fc, + content => $content_fc, + notify => Exec["clean-module-${title}"], + } + + $content_if = $source_if ? { undef => '', default => undef } + file {"${module_file}.if": + ensure => 'file', + source => $source_if, + content => $content_if, + notify => Exec["clean-module-${title}"], + } + # ensure it doesn't get purged if it exists + file {"${module_file}.pp": selinux_ignore_defaults => true } + + exec { "clean-module-${title}": + path => '/bin:/usr/bin', + cwd => $module_dir, + command => "rm -f '${module_file}.pp' '${module_file}.loaded'", + refreshonly => true, + notify => Exec["build-module-${title}"], + } + + exec { "build-module-${title}": + path => '/bin:/usr/bin', + cwd => $module_dir, + command => "${build_command} || (rm -f ${module_file}.pp ${module_file}.loaded && exit 1)", + creates => "${module_file}.pp", + notify => Exec["install-module-${title}"], + } + # we need to install the module manually because selmodule is kind of dumb. It ends up + # working fine, though. + exec { "install-module-${title}": + path => '/sbin:/usr/sbin:/bin:/usr/bin', + cwd => $module_dir, + command => "semodule -i ${module_file}.pp && touch ${module_file}.loaded", + creates => "${module_file}.loaded", + before => Selmodule[$title], + } + + # ensure it doesn't get purged if it exists + file { "${module_file}.loaded": } } - ~> - exec { "${sx_mod_dir}/${prefix}${name}.pp": - # Only allow refresh in the event that the initial .te file is updated. - command => shellquote('make', '-f', $makefile, "${prefix}${name}.pp"), - path => '/bin:/sbin:/usr/bin:/usr/sbin', - refreshonly => true, - cwd => $sx_mod_dir, + $module_path = $has_source ? { + true => "${module_file}.pp", + false => undef } - -> - selmodule { $name: - # Load the module if it has changed or was not loaded - # Warning: change the .te version! + + selmodule { $title: ensure => $ensure, - selmodulepath => "${sx_mod_dir}/${prefix}${name}.pp", - syncversion => $syncversion, + selmodulepath => $module_path, } } diff --git a/manifests/params.pp b/manifests/params.pp index 78ec87c0..9b10b31f 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -6,12 +6,16 @@ # This class provides default parameters for the selinux class # class selinux::params { - $makefile = '/usr/share/selinux/devel/Makefile' - $sx_mod_dir = '/usr/share/selinux' + $refpolicy_makefile = '/usr/share/selinux/devel/Makefile' $mode = undef $type = undef $manage_package = true + $refpolicy_package_name = 'selinux-policy-devel' + + validate_absolute_path($::selinux_agent_vardir) + $module_build_root = "${::selinux_agent_vardir}/puppet-selinux" + if $::operatingsystemmajrelease { $os_maj_release = $::operatingsystemmajrelease } else { @@ -32,7 +36,7 @@ $package_name = 'policycoreutils-devel' } '24', '25' : { - $package_name = 'selinux-policy-devel' + $package_name = 'policycoreutils-python-utils' } default: { fail("${::operatingsystem}-${::os_maj_release} is not supported") @@ -43,7 +47,7 @@ case $os_maj_release { '7': { $sx_fs_mount = '/sys/fs/selinux' - $package_name = 'selinux-policy-devel' + $package_name = 'policycoreutils-python' } '6': { $sx_fs_mount = '/selinux' diff --git a/manifests/refpolicy_package.pp b/manifests/refpolicy_package.pp new file mode 100644 index 00000000..ad232dee --- /dev/null +++ b/manifests/refpolicy_package.pp @@ -0,0 +1,21 @@ +# selinux::package +# +# THIS IS A PRIVATE CLASS +# ======================= +# +# This module manages additional packages required to support some of the functions. +# +# @param manage_package See main class +# @param package_name See main class +# +class selinux::refpolicy_package ( + $manage_package = $::selinux::manage_package, + $package_name = $::selinux::refpolicy_package_name, +) inherits ::selinux { + if $caller_module_name != $module_name { + fail("Use of private class ${name} by ${caller_module_name}") + } + if $manage_package { + ensure_packages ($package_name) + } +} diff --git a/spec/acceptance/class_spec.rb b/spec/acceptance/class_spec.rb index 8c00345b..7b7f1494 100644 --- a/spec/acceptance/class_spec.rb +++ b/spec/acceptance/class_spec.rb @@ -15,12 +15,44 @@ class { 'selinux': mode => 'enforcing' } protocol => 'tcp', } - # with puppet4 I would use a HERE DOC to make this pretty, - # but with puppet3 it's not possible. + # just something simple I found via Google: + file {'/tmp/selinux_simple_policy.te': + ensure => 'file', + content => @("EOF") + module puppet_selinux_simple_policy 1.0; + require { + type httpd_log_t; + type postfix_postdrop_t; + class dir getattr; + class file { read getattr }; + } + allow postfix_postdrop_t httpd_log_t:file getattr; + | EOF + } + + file {'/tmp/selinux_test_policy.te': + ensure => 'file', + content => @("EOF") + policy_module(puppet_selinux_test_policy, 1.0.0) + gen_tunable(puppet_selinux_test_policy_bool, false) + type puppet_selinux_test_policy_t; + type puppet_selinux_test_policy_exec_t; + init_daemon_domain(puppet_selinux_test_policy_t, puppet_selinux_test_policy_exec_t) + type puppet_selinux_test_policy_port_t; + corenet_port(puppet_selinux_test_policy_port_t) + | EOF + } + + selinux::module { 'puppet_selinux_simple_policy': + source_te => 'file:///tmp/selinux_simple_policy.te', + builder => 'simple', + require => File['/tmp/selinux_simple_policy.te'] + } + selinux::module { 'puppet_selinux_test_policy': - content => "policy_module(puppet_selinux_test_policy, 1.0.0)\ngen_tunable(puppet_selinux_test_policy_bool, false)\ntype puppet_selinux_test_policy_t;\ntype puppet_selinux_test_policy_exec_t;\ninit_daemon_domain(puppet_selinux_test_policy_t, puppet_selinux_test_policy_exec_t)\ntype puppet_selinux_test_policy_port_t;\ncorenet_port(puppet_selinux_test_policy_port_t)\n", - prefix => '', - syncversion => undef, + source_te => 'file:///tmp/selinux_test_policy.te', + builder => 'refpolicy', + require => File['/tmp/selinux_test_policy.te'] } Class['selinux'] -> @@ -76,14 +108,13 @@ class { 'selinux': mode => 'enforcing' } its(:stdout) { is_expected.to match(%r{^Enforcing$}) } end - context 'the test module source should exist and the module should be loaded' do - describe file('/usr/share/selinux/puppet_selinux_test_policy.te') do - it { is_expected.to be_file } - end - + context 'the compiled modules should be loaded' do describe command('semodule -l | grep puppet_selinux_test_policy') do its(:stdout) { is_expected.to match(%r{puppet_selinux_test_policy}) } end + describe command('semodule -l | grep puppet_selinux_simple_policy') do + its(:stdout) { is_expected.to match(%r{puppet_selinux_simple_policy}) } + end end context 'the test file should have the specified file context' do diff --git a/spec/acceptance/selinux_module_refpolicy_spec.rb b/spec/acceptance/selinux_module_refpolicy_spec.rb new file mode 100644 index 00000000..588c65f1 --- /dev/null +++ b/spec/acceptance/selinux_module_refpolicy_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper_acceptance' + +# +# Test if refpolicy builder can build module A and B +# where module B depends on an interface provided by +# module A. +# +describe 'selinux module refpolicy' do + let(:pp) do + <<-EOS + class { 'selinux': } + + file {'/tmp/selinux_test_a.te': + ensure => 'file', + content => @("EOF") + policy_module(puppet_test_a, 1.0.0) + gen_tunable(puppet_test_a_bool, false) + type puppet_test_a_t; + type puppet_test_a_exec_t; + init_daemon_domain(puppet_test_a_t, puppet_test_a_exec_t) + type puppet_test_a_port_t; + corenet_port(puppet_test_a_port_t) + | EOF + } -> + file {'/tmp/selinux_test_a.if': + ensure => 'file', + content => @(EOF) + interface(`puppet_test_a_domtrans',` + gen_require(` + type puppet_test_a_t, puppet_test_a_exec_t; + ') + + corecmd_search_bin($1) + domtrans_pattern($1, puppet_test_a_exec_t, puppet_test_a_t) + ') + | EOF + } -> + file {'/tmp/selinux_test_b.te': + ensure => 'file', + content => @("EOF") + policy_module(puppet_test_b, 1.0.0) + type puppet_test_b_t; + application_type(puppet_test_b_t) + puppet_test_a_domtrans(puppet_test_b_t) + | EOF + } -> + selinux::module { 'puppet_test_a': + source_te => 'file:///tmp/selinux_test_a.te', + source_if => 'file:///tmp/selinux_test_a.if', + builder => 'refpolicy', + } -> + selinux::module { 'puppet_test_b': + source_te => 'file:///tmp/selinux_test_b.te', + builder => 'refpolicy', + } + EOS + end + + it_behaves_like 'a idempotent resource' +end diff --git a/spec/classes/selinux_config_mode_spec.rb b/spec/classes/selinux_config_mode_spec.rb index 7ee75856..f4b37a45 100644 --- a/spec/classes/selinux_config_mode_spec.rb +++ b/spec/classes/selinux_config_mode_spec.rb @@ -15,14 +15,17 @@ context 'config' do context 'invalid mode' do let(:params) { { mode: 'invalid' } } - it { expect { is_expected.to create_class('selinux') }.to raise_error(%r{Valid modes are enforcing, permissive, and disabled. Received: invalid}) } + it { expect { is_expected.to create_class('selinux') }.to raise_error(Puppet::Error, %r{Enum}) } end context 'undef mode' do - it { is_expected.to have_file_resource_count(1) } + it { is_expected.to have_file_resource_count(5) } it { is_expected.to have_file_line_resource_count(0) } it { is_expected.to have_exec_resource_count(0) } - it { is_expected.to contain_file('/usr/share/selinux') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/bin') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/bin/selinux_build_module_simple.sh') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/modules') } it { is_expected.not_to contain_file_line('set-selinux-config-to-enforcing') } it { is_expected.not_to contain_file_line('set-selinux-config-to-permissive') } it { is_expected.not_to contain_file_line('set-selinux-config-to-disabled') } @@ -35,7 +38,7 @@ context 'enforcing' do let(:params) { { mode: 'enforcing' } } - it { is_expected.to contain_file('/usr/share/selinux').with(ensure: 'directory') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux') } it { is_expected.to contain_file_line('set-selinux-config-to-enforcing').with(line: 'SELINUX=enforcing') } it { is_expected.to contain_exec('change-selinux-status-to-enforcing').with(command: 'setenforce 1') } it { is_expected.not_to contain_file('/.autorelabel') } @@ -43,7 +46,7 @@ context 'permissive' do let(:params) { { mode: 'permissive' } } - it { is_expected.to contain_file('/usr/share/selinux').with(ensure: 'directory') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux') } it { is_expected.to contain_file_line('set-selinux-config-to-permissive').with(line: 'SELINUX=permissive') } it { is_expected.to contain_exec('change-selinux-status-to-permissive').with(command: 'setenforce 0') } it { is_expected.not_to contain_file('/.autorelabel') } @@ -51,7 +54,7 @@ context 'disabled' do let(:params) { { mode: 'disabled' } } - it { is_expected.to contain_file('/usr/share/selinux').with(ensure: 'directory') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux') } it { is_expected.to contain_file_line('set-selinux-config-to-disabled').with(line: 'SELINUX=disabled') } it { is_expected.to contain_exec('change-selinux-status-to-disabled').with(command: 'setenforce 0') } it { is_expected.not_to contain_file('/.autorelabel') } diff --git a/spec/classes/selinux_config_type_spec.rb b/spec/classes/selinux_config_type_spec.rb index ec31ee10..df65433c 100755 --- a/spec/classes/selinux_config_type_spec.rb +++ b/spec/classes/selinux_config_type_spec.rb @@ -9,13 +9,15 @@ context 'config' do context 'invalid type' do let(:params) { { type: 'invalid' } } - it { expect { is_expected.to create_class('selinux') }.to raise_error(%r{Valid types are targeted, minimum, and mls. Received: invalid}) } + it { expect { is_expected.to create_class('selinux') }.to raise_error(Puppet::Error, %r{Enum}) } end context 'undef type' do - it { is_expected.to have_file_resource_count(1) } + it { is_expected.to have_file_resource_count(5) } it { is_expected.to have_file_line_resource_count(0) } it { is_expected.to have_exec_resource_count(0) } - it { is_expected.to contain_file('/usr/share/selinux') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/modules') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/bin/selinux_build_module_simple.sh') } it { is_expected.not_to contain_file_line('set-selinux-config-type-to-targeted') } it { is_expected.not_to contain_file_line('set-selinux-config-type-to-minimum') } it { is_expected.not_to contain_file_line('set-selinux-config-type-to-mls') } @@ -23,20 +25,26 @@ context 'targeted' do let(:params) { { type: 'targeted' } } - it { is_expected.to contain_file('/usr/share/selinux').with(ensure: 'directory') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/modules') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/bin/selinux_build_module_simple.sh') } it { is_expected.to contain_file_line('set-selinux-config-type-to-targeted').with(line: 'SELINUXTYPE=targeted') } end context 'minimum' do let(:params) { { type: 'minimum' } } - it { is_expected.to contain_file('/usr/share/selinux').with(ensure: 'directory') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/modules') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/bin/selinux_build_module_simple.sh') } it { is_expected.to contain_file_line('set-selinux-config-type-to-minimum').with(line: 'SELINUXTYPE=minimum') } end context 'mls' do let(:params) { { type: 'mls' } } - it { is_expected.to contain_file('/usr/share/selinux').with(ensure: 'directory') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/modules') } + it { is_expected.to contain_file('/var/lib/puppet/puppet-selinux/bin/selinux_build_module_simple.sh') } it { is_expected.to contain_file_line('set-selinux-config-type-to-mls').with(line: 'SELINUXTYPE=mls') } end end diff --git a/spec/classes/selinux_package_spec.rb b/spec/classes/selinux_package_spec.rb index 4716d8aa..2a99d60f 100644 --- a/spec/classes/selinux_package_spec.rb +++ b/spec/classes/selinux_package_spec.rb @@ -2,7 +2,7 @@ describe 'selinux' do context 'package' do - %w(6).each do |majrelease| + %w(6 7).each do |majrelease| context "On RedHat #{majrelease} based OSes" do let(:facts) do { @@ -17,21 +17,6 @@ end end - %w(7).each do |majrelease| - context "On RedHat #{majrelease} based OSes" do - let(:facts) do - { - osfamily: 'RedHat', - operatingsystem: 'RedHat', - operatingsystemmajrelease: majrelease, - selinux_current_mode: 'enforcing' - } - end - - it { is_expected.to contain_package('selinux-policy-devel').with(ensure: 'present') } - end - end - %w(24 25).each do |majrelease| context "On Fedora #{majrelease}" do let(:facts) do @@ -42,7 +27,7 @@ selinux_current_mode: 'enforcing' } end - it { is_expected.to contain_package('selinux-policy-devel').with(ensure: 'present') } + it { is_expected.to contain_package('policycoreutils-python-utils').with(ensure: 'present') } end end @@ -60,7 +45,7 @@ } end - it { is_expected.not_to contain_package('policycoreutils').with(ensure: 'present') } + it { is_expected.not_to contain_package('policycoreutils-python').with(ensure: 'present') } end context 'install a different package name' do diff --git a/spec/classes/selinux_spec.rb b/spec/classes/selinux_spec.rb index 5f1666fc..a950dba0 100644 --- a/spec/classes/selinux_spec.rb +++ b/spec/classes/selinux_spec.rb @@ -25,8 +25,8 @@ let(:params) do { module: { - 'mymodule1' => { 'content' => 'dummy' }, - 'mymodule2' => { 'content' => 'dummy' } + 'mymodule1' => { 'source_te' => 'dummy' }, + 'mymodule2' => { 'source_te' => 'dummy' } } } end diff --git a/spec/default_module_facts.yml b/spec/default_module_facts.yml index 5b9d0daf..914dbea7 100644 --- a/spec/default_module_facts.yml +++ b/spec/default_module_facts.yml @@ -5,3 +5,5 @@ operatingsystemmajrelease: '7' # concat facts id: 0 path: /tmp +# custom fact for module building: +selinux_agent_vardir: /var/lib/puppet diff --git a/spec/defines/selinux_module_spec.rb b/spec/defines/selinux_module_spec.rb index 5139f7c6..ffb754a1 100644 --- a/spec/defines/selinux_module_spec.rb +++ b/spec/defines/selinux_module_spec.rb @@ -8,57 +8,119 @@ let(:facts) do facts end + let(:workdir) do + '/var/lib/puppet/puppet-selinux/modules' + end + let(:module_basepath) do + '/var/lib/puppet/puppet-selinux/modules/mymodule' + end context 'ordering' do let(:params) do { - source: 'puppet:///modules/mymodule/selinux/mymodule.te' + source_te: 'puppet:///modules/mymodule/selinux/mymodule.te' } end it { is_expected.to contain_selinux__module('mymodule').that_requires('Anchor[selinux::module pre]') } it { is_expected.to contain_selinux__module('mymodule').that_comes_before('Anchor[selinux::module post]') } end - context 'present case' do + context 'present case with refpolicy builder and with te file only' do let(:params) do { - source: 'puppet:///modules/mymodule/selinux/mymodule.te' + source_te: 'puppet:///modules/mymodule/selinux/mymodule.te', + builder: 'refpolicy' } end - it do - is_expected.to contain_file('/usr/share/selinux/mymodule.te').that_notifies('Exec[/usr/share/selinux/mymodule.pp]') - is_expected.to contain_exec('/usr/share/selinux/mymodule.pp').with(command: 'make -f /usr/share/selinux/devel/Makefile mymodule.pp') - is_expected.to contain_selmodule('mymodule').with_ensure('present') + it { is_expected.to contain_file(workdir) } + it { is_expected.to contain_file("#{workdir}/mymodule.te").that_notifies('Exec[clean-module-mymodule]') } + it { is_expected.to contain_file("#{workdir}/mymodule.fc").with(source: nil, content: '') } + it { is_expected.to contain_file("#{workdir}/mymodule.if").with(source: nil, content: '') } + it { is_expected.to contain_exec('clean-module-mymodule').with(command: "rm -f '#{module_basepath}.pp' '#{module_basepath}.loaded'", cwd: workdir) } + it { is_expected.to contain_exec('build-module-mymodule').with(command: "make -f /usr/share/selinux/devel/Makefile mymodule.pp || (rm -f #{module_basepath}.pp #{module_basepath}.loaded && exit 1)", creates: "#{module_basepath}.pp") } + it { is_expected.to contain_exec('install-module-mymodule').with(command: "semodule -i #{module_basepath}.pp && touch #{module_basepath}.loaded", cwd: workdir, creates: "#{module_basepath}.loaded") } + it { is_expected.to contain_selmodule('mymodule').with_ensure('present', selmodulepath: workdir) } + end + + context 'present case with refpolicy builder and with te and fc file' do + let(:params) do + { + source_te: 'puppet:///modules/mymodule/selinux/mymodule.te', + source_fc: 'puppet:///modules/mymodule/selinux/mymodule.fc', + builder: 'refpolicy' + } end + + it { is_expected.to contain_file(workdir) } + it { is_expected.to contain_file("#{workdir}/mymodule.te").that_notifies('Exec[clean-module-mymodule]') } + it { is_expected.to contain_file("#{workdir}/mymodule.fc").that_notifies('Exec[clean-module-mymodule]') } + it { is_expected.to contain_file("#{workdir}/mymodule.if").with(source: nil, content: '') } + it { is_expected.to contain_exec('clean-module-mymodule').with(command: "rm -f '#{module_basepath}.pp' '#{module_basepath}.loaded'", cwd: workdir) } + it { is_expected.to contain_exec('build-module-mymodule').with(command: "make -f /usr/share/selinux/devel/Makefile mymodule.pp || (rm -f #{module_basepath}.pp #{module_basepath}.loaded && exit 1)", creates: "#{module_basepath}.pp") } + it { is_expected.to contain_exec('install-module-mymodule').with(command: "semodule -i #{module_basepath}.pp && touch #{module_basepath}.loaded", cwd: workdir, creates: "#{module_basepath}.loaded") } + it { is_expected.to contain_selmodule('mymodule').with_ensure('present', selmodulepath: workdir) } end - context 'present case and prefix set' do + context 'present case with refpolicy builder and with te, fc and if file' do let(:params) do { - source: 'puppet:///modules/mymodule/selinux/mymodule.te', - prefix: 'local_' + source_te: 'puppet:///modules/mymodule/selinux/mymodule.te', + source_if: 'puppet:///modules/mymodule/selinux/mymodule.if', + source_fc: 'puppet:///modules/mymodule/selinux/mymodule.fc', + builder: 'refpolicy' } end - it do - is_expected.to contain_file('/usr/share/selinux/local_mymodule.te').that_notifies('Exec[/usr/share/selinux/local_mymodule.pp]') - is_expected.to contain_exec('/usr/share/selinux/local_mymodule.pp').with(command: 'make -f /usr/share/selinux/devel/Makefile local_mymodule.pp') - is_expected.to contain_selmodule('mymodule').with_ensure('present') + it { is_expected.to contain_file(workdir) } + it { is_expected.to contain_file("#{workdir}/mymodule.te").that_notifies('Exec[clean-module-mymodule]') } + it { is_expected.to contain_file("#{workdir}/mymodule.if").that_notifies('Exec[clean-module-mymodule]') } + it { is_expected.to contain_file("#{workdir}/mymodule.fc").that_notifies('Exec[clean-module-mymodule]') } + it { is_expected.to contain_exec('clean-module-mymodule').with(command: "rm -f '#{module_basepath}.pp' '#{module_basepath}.loaded'", cwd: workdir) } + it { is_expected.to contain_exec('build-module-mymodule').with(command: "make -f /usr/share/selinux/devel/Makefile mymodule.pp || (rm -f #{module_basepath}.pp #{module_basepath}.loaded && exit 1)", creates: "#{module_basepath}.pp") } + it { is_expected.to contain_exec('install-module-mymodule').with(command: "semodule -i #{module_basepath}.pp && touch #{module_basepath}.loaded", cwd: workdir, creates: "#{module_basepath}.loaded") } + it { is_expected.to contain_selmodule('mymodule').with_ensure('present', selmodulepath: workdir) } + end + + context 'present case with simple builder with te' do + let(:params) do + { + source_te: 'puppet:///modules/mymodule/selinux/mymodule.te', + builder: 'simple' + } end + + it { is_expected.to contain_file(workdir) } + it { is_expected.to contain_file("#{workdir}/mymodule.te").that_notifies('Exec[clean-module-mymodule]') } + it { is_expected.to contain_file("#{workdir}/mymodule.fc").with(source: nil, content: '') } + it { is_expected.to contain_file("#{workdir}/mymodule.if").with(source: nil, content: '') } + it { is_expected.to contain_exec('clean-module-mymodule').with(command: "rm -f '#{module_basepath}.pp' '#{module_basepath}.loaded'", cwd: workdir) } + it { is_expected.to contain_exec('build-module-mymodule').with(command: "/var/lib/puppet/puppet-selinux/bin/selinux_build_module_simple.sh mymodule #{workdir} || (rm -f #{module_basepath}.pp #{module_basepath}.loaded && exit 1)", creates: "#{module_basepath}.pp") } + it { is_expected.to contain_exec('install-module-mymodule').with(command: "semodule -i #{module_basepath}.pp && touch #{module_basepath}.loaded", cwd: workdir, creates: "#{module_basepath}.loaded") } + it { is_expected.to contain_selmodule('mymodule').with_ensure('present', selmodulepath: workdir) } end - context 'absent case' do + context 'unsupported source with simple builder' do let(:params) do { - ensure: 'absent' + source_if: 'puppet:///modules/mymodule/selinux/mymodule.te', + builder: 'simple' } end it do - is_expected.to contain_selmodule('mymodule').with_ensure('absent') + is_expected.to raise_error(Puppet::Error, %r{simple builder does not support}) end end + context 'absent case' do + let(:params) do + { + ensure: 'absent' + } + end + + it { is_expected.to contain_selmodule('mymodule').with_ensure('absent') } + end end end end