From 272de9b946f2b05bfb9e8e3ce1c011bffa8bea89 Mon Sep 17 00:00:00 2001 From: Jarkko Oranen Date: Sun, 15 Jan 2017 22:33:52 +0200 Subject: [PATCH 1/4] Add selinux_permissive type and provider --- README.md | 4 +- .../provider/selinux_permissive/semanage.rb | 63 +++++++++++++++++++ lib/puppet/type/selinux_permissive.rb | 15 +++++ 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 lib/puppet/provider/selinux_permissive/semanage.rb create mode 100644 lib/puppet/type/selinux_permissive.rb diff --git a/README.md b/README.md index 3a11155a..31277bb9 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ running system. because the used tools fail. * `selinux::port` has the `action` parameter which if you specify `-d` or `--delete` silently does nothing. (GH-164) -* `selinux::permissive` allows only to set a domain to permissive but not to - remove it. (GH-165) +* If you try to remove a built-in permissive type, the operation will appear to succeed + but will actually have no effect, making your puppet runs non-idempotent. ## Usage diff --git a/lib/puppet/provider/selinux_permissive/semanage.rb b/lib/puppet/provider/selinux_permissive/semanage.rb new file mode 100644 index 00000000..33e7a8f0 --- /dev/null +++ b/lib/puppet/provider/selinux_permissive/semanage.rb @@ -0,0 +1,63 @@ +Puppet::Type.type(:selinux_permissive).provide(:semanage) do + desc 'Support managing SELinux permissive types definitions via semanage' + + defaultfor kernel: 'Linux' + # SELinux must be enabled. Is there a way to get a better error message? + confine selinux: true + + commands semanage: 'semanage' + + mk_resource_methods + + def self.instances + lines = semanage('permissive', '--list').split("\n") + res = {} + # we need this logic because semanage on older systems doesn't support + # --locallist and returns things in different order. + local = true + lines.each do |line| + if line =~ %r{Builtin} + local = false + next + end + if line =~ %r{Custom} + local = true + next + end + next if line.strip.empty? + name = line.strip + # do not use built-in provider if we find a customized type + next if res[name] && !local + + # If I remove name: from here purging will not work. + res[name] = new(name: name, seltype: name, ensure: :present, local: local) + end + res.values + end + + def self.prefetch(resources) + debug(resources.keys) + instances.each do |provider| + resource = resources[provider.seltype] + # consider built-in resources absent for purposes of purging + next unless resource + resource.provider = provider + if resource.purging? && !provider.local + debug("Can't purge built-in resource #{resource[:seltype]}") + resource[:ensure] = :present + end + end + end + + def create + semanage('permissive', '-a', @resource[:seltype]) + end + + def destroy + semanage('permissive', '-d', @resource[:seltype]) + end + + def exists? + @property_hash[:ensure] == :present + end +end diff --git a/lib/puppet/type/selinux_permissive.rb b/lib/puppet/type/selinux_permissive.rb new file mode 100644 index 00000000..bc5ab949 --- /dev/null +++ b/lib/puppet/type/selinux_permissive.rb @@ -0,0 +1,15 @@ +Puppet::Type.newtype(:selinux_permissive) do + @doc = 'Manage SELinux permissive types.' + + ensurable + + newparam(:seltype) do + desc 'The SELinux type that should be permissive' + isnamevar + end + + newparam(:local) do + desc 'A read-only attribue indicating whether the type is locally customized' + newvalues(true, false) + end +end From 22541f11a7721538be7e7c9acaa300a33163cfda Mon Sep 17 00:00:00 2001 From: Jarkko Oranen Date: Sat, 21 Jan 2017 19:59:20 +0200 Subject: [PATCH 2/4] Use the selinux_permissive type in the manifest --- manifests/permissive.pp | 31 ++++++++++++++----------- spec/acceptance/class_spec.rb | 2 +- spec/classes/selinux_spec.rb | 4 ++-- spec/defines/selinux_permissive_spec.rb | 30 +++++++++++++++++++----- 4 files changed, 44 insertions(+), 23 deletions(-) diff --git a/manifests/permissive.pp b/manifests/permissive.pp index fed92582..6f9a0476 100644 --- a/manifests/permissive.pp +++ b/manifests/permissive.pp @@ -1,28 +1,31 @@ # selinux::permissive # -# This method will set a context to permissive +# This define will set an SELinux type to permissive # -# @param context A particular domain-context, like "oddjob_mkhomedir_t" +# @param seltype A particular selinux type to make permissive, like "oddjob_mkhomedir_t" # # @example Mark oddjob_mkhomedir_t permissive -# selinux::permissive { 'allow-oddjob_mkhomedir_t': -# context => 'oddjob_mkhomedir_t', +# selinux::permissive { 'oddjob_mkhomedir_t': +# ensure => 'present' # } # define selinux::permissive ( - $context, + String $seltype = $title, + Enum['present', 'absent'] $ensure = 'present', ) { include ::selinux + if $ensure == 'present' { + Anchor['selinux::module post'] -> + Selinux::Permissive[$title] -> + Anchor['selinux::end'] + } else { + Anchor['selinux::start'] -> + Selinux::Permissive[$title] -> + Anchor['selinux::module pre'] + } - Anchor['selinux::module post'] -> - Selinux::Permissive[$title] -> - Anchor['selinux::end'] - - exec { "add_${context}": - command => shellquote('semanage', 'permissive', '-a', $context), - unless => sprintf('semanage permissive -l | grep -Fx %s', shellquote($context)), - path => '/bin:/sbin:/usr/bin:/usr/sbin', - require => Class['selinux::package'], + selinux_permissive {$seltype: + ensure => $ensure, } } diff --git a/spec/acceptance/class_spec.rb b/spec/acceptance/class_spec.rb index 41799cef..a88b90bd 100644 --- a/spec/acceptance/class_spec.rb +++ b/spec/acceptance/class_spec.rb @@ -7,7 +7,7 @@ class { 'selinux': mode => 'enforcing' } selinux::boolean { 'puppet_selinux_test_policy_bool': } - selinux::permissive { 'puppet_selinux_test_policy_t': context => 'puppet_selinux_test_policy_t', } + selinux::permissive { 'puppet_selinux_test_policy_t': } selinux::port { 'puppet_selinux_test_policy_port_t/tcp': context => 'puppet_selinux_test_policy_port_t', diff --git a/spec/classes/selinux_spec.rb b/spec/classes/selinux_spec.rb index b1a904ca..234a0e06 100644 --- a/spec/classes/selinux_spec.rb +++ b/spec/classes/selinux_spec.rb @@ -67,8 +67,8 @@ let(:params) do { permissive: { - 'domain1' => { 'context' => 'domain1' }, - 'domain2' => { 'context' => 'domain2' } + 'domain1' => { 'seltype' => 'domain1' }, + 'domain2' => { 'seltype' => 'domain2' } } } end diff --git a/spec/defines/selinux_permissive_spec.rb b/spec/defines/selinux_permissive_spec.rb index c5b38bfd..e305ee78 100644 --- a/spec/defines/selinux_permissive_spec.rb +++ b/spec/defines/selinux_permissive_spec.rb @@ -7,18 +7,36 @@ let(:facts) do facts end + context 'ensure selinux_permissive oddjob_mkhomedir_t is present' do + let(:params) do + { + seltype: 'oddjob_mkhomedir_t' + } + end + it { is_expected.to contain_selinux_permissive('oddjob_mkhomedir_t').with(ensure: 'present') } + it { is_expected.to contain_selinux__permissive('mycontextp').that_requires('Anchor[selinux::module post]') } + it { is_expected.to contain_selinux__permissive('mycontextp').that_comes_before('Anchor[selinux::end]') } + end - context 'context allow-oddjob_mkhomedir_t to permissive' do + context 'ensure selinux_permissive oddjob_mkhomedir_t is absent' do let(:params) do { - context: 'oddjob_mkhomedir_t' + seltype: 'oddjob_mkhomedir_t', + ensure: 'absent' } end - it do - is_expected.to contain_exec('add_oddjob_mkhomedir_t').with(command: 'semanage permissive -a oddjob_mkhomedir_t') - is_expected.to contain_selinux__permissive('mycontextp').that_requires('Anchor[selinux::module post]') - is_expected.to contain_selinux__permissive('mycontextp').that_comes_before('Anchor[selinux::end]') + it { is_expected.to contain_selinux_permissive('oddjob_mkhomedir_t').with(ensure: 'absent') } + it { is_expected.to contain_selinux__permissive('mycontextp').that_requires('Anchor[selinux::start]') } + it { is_expected.to contain_selinux__permissive('mycontextp').that_comes_before('Anchor[selinux::module pre]') } + end + + context 'selinux_permissive oddjob_mkhomedir_t with title only' do + let(:title) do + 'oddjob_mkhomedir_t' end + it { is_expected.to contain_selinux_permissive('oddjob_mkhomedir_t').with(ensure: 'present') } + it { is_expected.to contain_selinux__permissive('oddjob_mkhomedir_t').that_requires('Anchor[selinux::module post]') } + it { is_expected.to contain_selinux__permissive('oddjob_mkhomedir_t').that_comes_before('Anchor[selinux::end]') } end end end From 1229e4c09d236959a501835f8419c45f4150e121 Mon Sep 17 00:00:00 2001 From: Jarkko Oranen Date: Wed, 18 Jan 2017 22:18:19 +0200 Subject: [PATCH 3/4] Add some spec tests for the semanage provider. --- spec/defines/selinux_permissive_spec.rb | 2 +- .../selinux_permissive/semanage_spec.rb | 100 ++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb diff --git a/spec/defines/selinux_permissive_spec.rb b/spec/defines/selinux_permissive_spec.rb index e305ee78..886a0466 100644 --- a/spec/defines/selinux_permissive_spec.rb +++ b/spec/defines/selinux_permissive_spec.rb @@ -32,7 +32,7 @@ context 'selinux_permissive oddjob_mkhomedir_t with title only' do let(:title) do - 'oddjob_mkhomedir_t' + 'oddjob_mkhomedir_t' end it { is_expected.to contain_selinux_permissive('oddjob_mkhomedir_t').with(ensure: 'present') } it { is_expected.to contain_selinux__permissive('oddjob_mkhomedir_t').that_requires('Anchor[selinux::module post]') } diff --git a/spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb b/spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb new file mode 100644 index 00000000..6d82decb --- /dev/null +++ b/spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' + +semanage_provider = Puppet::Type.type(:selinux_permissive).provider(:semanage) +permissive = Puppet::Type.type(:selinux_permissive) + +describe semanage_provider do + let(:semanage_output) do + <<-EOS + + Customized Permissive Types + + + Builtin Permissive Types + + tlp_t + EOS + end + let(:semanage_output_custom) do + <<-EOS + + Customized Permissive Types + + test_t + Builtin Permissive Types + + tlp_t + EOS + end + on_supported_os.each do |_os, _facts| + let(:resource) do + permissive.new(seltype: 'test_t', + ensure: :present) + end + let(:provider) do + resource.provider + end + context 'semanage list' do + context 'without custom types' do + before do + described_class.expects(:semanage).with('permissive', '--list').returns(semanage_output) + end + it 'returns one resource' do + expect(described_class.instances.size).to eq(1) + end + it 'has a name tlp_t and ensure present' do + expect(described_class.instances[0].instance_variable_get('@property_hash')).to eq( + name: 'tlp_t', + seltype: 'tlp_t', + ensure: :present, + local: false + ) + end + end + context 'With a custom type' do + before do + described_class.expects(:semanage).with('permissive', '--list').returns(semanage_output_custom) + end + it 'returns two resources' do + expect(described_class.instances.size).to eq(2) + end + end + end + context 'Creating' do + it 'runs semanage permissive -a' do + provider.expects(:semanage).with('permissive', '-a', 'test_t') + provider.create + end + end + context 'Deleting' do + it 'runs semanage permissive -d' do + provider.expects(:semanage).with('permissive', '-d', 'test_t') + provider.destroy + end + end + context 'Prefetch' do + before do + described_class.expects(:semanage).with('permissive', '--list').returns(semanage_output_custom) + end + it 'matches the provider' do + semanage_provider.prefetch('test_t' => resource, 'tlp_t' => permissive.new(seltype: 'tlp_t', ensure: :present)) + expect(resource.provider.exists?).to eq(true) + end + end + context 'Prefetch when purging' do + let(:built_in) do + b = permissive.new(seltype: 'tlp_t') + b.purging + b + end + let(:custom) do + permissive.new(seltype: 'test_t') + end + it 'forces built-ins to be present' do + described_class.expects(:semanage).with('permissive', '--list').returns(semanage_output_custom) + semanage_provider.prefetch('test_t' => custom, 'tlp_t' => built_in) + expect(built_in[:ensure]).to eq(:present) + end + end + end +end From 8094b16666b21fb7160bba194f3963c1e1887a64 Mon Sep 17 00:00:00 2001 From: Jarkko Oranen Date: Sat, 21 Jan 2017 19:55:55 +0200 Subject: [PATCH 4/4] Acceptance tests --- spec/acceptance/selinux_permissive_spec.rb | 70 +++++++++++++++++++ .../selinux_permissive/semanage_spec.rb | 6 +- 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 spec/acceptance/selinux_permissive_spec.rb diff --git a/spec/acceptance/selinux_permissive_spec.rb b/spec/acceptance/selinux_permissive_spec.rb new file mode 100644 index 00000000..6230d5cc --- /dev/null +++ b/spec/acceptance/selinux_permissive_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper_acceptance' + +describe 'selinux::permissive define' do + context 'ensure present for passwd_t' do + let(:result) do + manifest = "selinux::permissive {'passwd_t':}" + apply_manifest(manifest, catch_failures: true) + end + after :all do + # cleanup + shell('semanage permissive -d passwd_t', acceptable_exit_codes: [0, 1]) + end + it 'runs without errors' do + expect(result.exit_code).to eq 2 + end + it 'makes passwd_t permissive' do + shell('semanage permissive -l | grep -q passwd_t') + end + end + context 'purge with ensure present for passwd_t, when kernel_t is permissive' do + before :all do + shell('semanage permissive -a kernel_t') + end + let(:result) do + manifest = <<-EOS + selinux::permissive {'passwd_t':} + resources {'selinux_permissive': purge => true } + EOS + apply_manifest(manifest, catch_failures: true) + end + after :all do + # clean up + shell('semanage permissive -d passwd_t', acceptable_exit_codes: [0, 1]) + shell('semanage permissive -d kernel_t', acceptable_exit_codes: [0, 1]) + end + it 'runs without errors' do + expect(result.exit_code).to eq 2 + end + it 'purges kernel_t' do + shell('semanage permissive -l | grep -q kernel_t', acceptable_exit_codes: [1]) + end + it 'makes passwd_t permissive' do + shell('semanage permissive -l | grep -q passwd_t') + end + end + context 'ensure absent for kernel_t only, when passwd_t is also permissive' do + before :all do + shell('semanage permissive -a kernel_t') + shell('semanage permissive -a passwd_t') + end + let(:result) do + manifest = "selinux::permissive {'kernel_t': ensure => 'absent'}" + apply_manifest(manifest, catch_failures: true) + end + after :all do + # clean up + shell('semanage permissive -d passwd_t', acceptable_exit_codes: [0, 1]) + shell('semanage permissive -d kernel_t', acceptable_exit_codes: [0, 1]) + end + it 'runs without errors' do + expect(result.exit_code).to eq 2 + end + it 'makes kernel_t not permissive' do + shell('semanage permissive -l | grep -q kernel_t', acceptable_exit_codes: [1]) + end + it 'does not remove passwd_t' do + shell('semanage permissive -l | grep -q passwd_t') + end + end +end diff --git a/spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb b/spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb index 6d82decb..7c2bbe17 100644 --- a/spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb +++ b/spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb @@ -32,7 +32,7 @@ ensure: :present) end let(:provider) do - resource.provider + resource.provider = described_class.new end context 'semanage list' do context 'without custom types' do @@ -62,13 +62,13 @@ end context 'Creating' do it 'runs semanage permissive -a' do - provider.expects(:semanage).with('permissive', '-a', 'test_t') + described_class.expects(:semanage).with('permissive', '-a', 'test_t') provider.create end end context 'Deleting' do it 'runs semanage permissive -d' do - provider.expects(:semanage).with('permissive', '-d', 'test_t') + described_class.expects(:semanage).with('permissive', '-d', 'test_t') provider.destroy end end