Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING: Selinux permissive type #183

Merged
merged 4 commits into from
Jan 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
63 changes: 63 additions & 0 deletions lib/puppet/provider/selinux_permissive/semanage.rb
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions lib/puppet/type/selinux_permissive.rb
Original file line number Diff line number Diff line change
@@ -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
31 changes: 17 additions & 14 deletions manifests/permissive.pp
Original file line number Diff line number Diff line change
@@ -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,
}
}
2 changes: 1 addition & 1 deletion spec/acceptance/class_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
70 changes: 70 additions & 0 deletions spec/acceptance/selinux_permissive_spec.rb
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions spec/classes/selinux_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
let(:params) do
{
permissive: {
'domain1' => { 'context' => 'domain1' },
'domain2' => { 'context' => 'domain2' }
'domain1' => { 'seltype' => 'domain1' },
'domain2' => { 'seltype' => 'domain2' }
}
}
end
Expand Down
30 changes: 24 additions & 6 deletions spec/defines/selinux_permissive_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
100 changes: 100 additions & 0 deletions spec/unit/puppet/provider/selinux_permissive/semanage_spec.rb
Original file line number Diff line number Diff line change
@@ -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 = described_class.new
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
described_class.expects(:semanage).with('permissive', '-a', 'test_t')
provider.create
end
end
context 'Deleting' do
it 'runs semanage permissive -d' do
described_class.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