Merge pull request #5463 from inspec/vasundhara/add_selinux_modules

Add selinux resource support for modules and booleans
This commit is contained in:
Clinton Wolfe 2021-04-27 08:58:28 -04:00 committed by GitHub
commit 3940a6374a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 244 additions and 17 deletions

View file

@ -11,9 +11,9 @@ platform = "linux"
parent = "inspec/resources/os" parent = "inspec/resources/os"
+++ +++
Use the `selinux` Chef InSpec audit resource to test the state and mode of SELinux policy. Use the `selinux` Chef InSpec audit resource to test the configuration data of the SELinux policy, SELinux modules and SELinux booleans.
The `selinux` resource extracts and exposes data reported by the `sestatus` command. The `selinux` resource extracts and exposes data reported by the `sestatus`, `semodule -lfull`, and `semanage boolean -l -n` command.
## Availability ## Availability
@ -23,6 +23,8 @@ This resource is distributed along with Chef InSpec itself. You can use it autom
### Version ### Version
This resource first became available in v4.35.1 of InSpec.
## Syntax ## Syntax
The `selinux` Chef InSpec resource block tests the state and mode of SELinux policy. The `selinux` Chef InSpec resource block tests the state and mode of SELinux policy.
@ -34,23 +36,65 @@ The `selinux` Chef InSpec resource block tests the state and mode of SELinux pol
it { should_not be_permissive } it { should_not be_permissive }
end end
The `selinux` resource block also allows you to write tests for multiple modules:
describe selinux.modules.where("zebra") do
it { should exist }
it { should be_installed }
it { should be_enabled }
end
or:
describe selinux.modules.where(status: "installed") do
it { should exist }
its('count') { should cmp 404 }
end
where:
- `.where()` specifies the parameter and expected value.
- `name`, `status`, `state`, and `priority` are valid parameters.
The `selinux` resource block also allows you to write tests for multiple booleans:
describe selinux.booleans.where(name: "httpd_enable_homedirs") do
it { should_not be_on }
end
or:
describe selinux.booleans.where(name: "xend_run_blktap", state: "on") do
it { should exist }
its('defaults') { should cmp "on" }
end
- `.where()` specifies the parameter and expected value.
- `name`, `state`, and `default` are valid parameters for `booleans`.
## Examples ## Examples
The following examples show how to use this Chef InSpec selinux resource. The following examples show how to use this Chef InSpec selinux resource.
### Test if SELinux is installed and enabled ### Test if SELinux is installed and enabled
describe selinux do describe selinux do
it { should be_installed } it { should be_installed }
it { should_not be_disabled } it { should_not be_disabled }
end end
### Test if SELinux is enabled and running in enforcing mode ### Test if SELinux is enabled and running in enforcing mode
describe selinux do describe selinux do
it { should_not be_disabled } it { should_not be_disabled }
it { should be_enforcing } it { should be_enforcing }
end end
### Test the selinux policy type
describe selinux do
its('policy') { should eq "targeted"}
end
## Matchers ## Matchers
@ -58,7 +102,7 @@ For a full list of available matchers, please visit our [matchers page](/inspec/
### be_installed ### be_installed
The `be_installed` matcher tests if the SELinux is installed on the system: The `be_installed` matcher tests if the SElinux policy or SElinux modules are installed on the system:
it { should be_installed } it { should be_installed }
@ -79,3 +123,51 @@ The `be_enforcing` matcher tests if the SELinux mode is set to enforcing:
The `be_permissive` matcher tests if the SELinux mode is set to permissive: The `be_permissive` matcher tests if the SELinux mode is set to permissive:
it { should be_permissive } it { should be_permissive }
### be_on
The `be_on` matcher tests if the SELinux boolean is on:
it { should be_on }
### be_enabled
The `be_enabled` matcher tests if the SElinux module is enabled:
it { should be_enabled }
## Resource Parameters
- `names`, `status`, `states`, and `priorities` are valid parameters for SELinux policy modules.
- `names`, `status`, `states`, and `defaults` are valid parameters for SELinux `booleans`.
## Resource Parameter Examples
### modules
`modules` returns information about SELinux modules using the [semodule -lfull](https://man7.org/linux/man-pages/man8/semodule.8.html) command.
Note: The `semodule -l` command [does not provide version information](https://access.redhat.com/solutions/2760071) for newer versions of Linux-based systems like RHEL8 and Centos8, so we do not support that option.
```ruby
describe selinux.modules do
its("names") { should include "zebra" }
its("status") { should include "installed" }
its("states") { should include "enabled" }
its("priorities") { should include "100" }
end
```
### booleans
`booleans` returns information about SELinux booleans using the [semanage boolean -l -n](https://man7.org/linux/man-pages/man8/semanage-boolean.8.html) command.
```ruby
describe selinux.booleans do
its("names") { should include "httpd_enable_homedirs" }
its("states") { should include "on" }
its("states") { should include "off" }
its("defaults") { should include "on" }
its("defaults") { should include "off" }
end
```

View file

@ -1,11 +1,54 @@
require "inspec/resources/command" require "inspec/resources/command"
require "inspec/utils/filter"
module Inspec::Resources module Inspec::Resources
class SelinuxModuleFilter
# use filtertable for SELinux Modules
filter = FilterTable.create
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
filter.register_column(:names, field: :name)
filter.register_column(:status, field: :status)
filter.register_column(:states, field: :state)
filter.register_column(:priorities , field: :priority)
filter.register_custom_matcher(:enabled?) { |x| x.states[0] == "enabled" }
filter.register_custom_matcher(:installed?) { |x| x.status[0] == "installed" }
filter.install_filter_methods_on_resource(self, :modules)
attr_reader :modules
def initialize(modules)
@modules = modules
end
def to_s
"SELinux modules"
end
end
class SelinuxBooleanFilter
# use filtertable for SELinux Booleans
filter = FilterTable.create
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
filter.register_column(:names, field: :name)
filter.register_column(:states, field: :state)
filter.register_column(:defaults, field: :default)
filter.register_custom_matcher(:on?) { |x| x.states[0] == "on" }
filter.install_filter_methods_on_resource(self, :booleans)
attr_reader :booleans
def initialize(booleans)
@booleans = booleans
end
def to_s
"SELinux booleans"
end
end
class Selinux < Inspec.resource(1) class Selinux < Inspec.resource(1)
name "selinux" name "selinux"
supports platform: "linux" supports platform: "linux"
desc "Use selinux Inspec resource to test state/mode of the selinux policy." desc "Use the selinux Chef InSpec resource to test the configuration data of the SELinux policy, SELinux modules, and SELinux booleans."
example <<~EXAMPLE example <<~EXAMPLE
describe selinux do describe selinux do
@ -14,6 +57,29 @@ module Inspec::Resources
it { should be_permissive } it { should be_permissive }
it { should be_enforcing } it { should be_enforcing }
end end
describe selinux do
its('policy') { should eq "targeted"}
end
describe selinux.modules.where("zebra") do
it { should exist }
it { should be_installed }
it { should be_enabled }
end
describe selinux.modules.where(status: "installed") do
it { should exist }
its('count') { should cmp 404 }
end
describe selinux.booleans.where(name: "xend_run_blktap") do
it { should be_on }
end
describe selinux.booleans.where { name == "xend_run_blktap" && state == "on" } do
it { should exist }
end
EXAMPLE EXAMPLE
def initialize(selinux_path = "/etc/selinux/config") def initialize(selinux_path = "/etc/selinux/config")
@ -46,8 +112,43 @@ module Inspec::Resources
@data["currentmode"] == "permissive" @data["currentmode"] == "permissive"
end end
def policy
@data["loadedpolicyname"]
end
def modules
SelinuxModuleFilter.new(parse_modules)
end
def booleans
SelinuxBooleanFilter.new(parse_booleans)
end
def to_s def to_s
"SELinux" "SELinux"
end end
private
def parse_modules
raw_modules = inspec.command("semodule -lfull").stdout
r_modules = []
raw_modules.each_line do |entry|
data = entry.split.map(&:strip)
state = data.length == 4 ? data[3] : "enabled"
r_modules.push({ name: data[1], status: "installed", state: state, priority: data[0] })
end
r_modules
end
def parse_booleans
raw_booleans = inspec.command("semanage boolean -l -n").stdout
r_booleans = []
raw_booleans.each_line do |entry|
data = entry.scan(/([^(,)]+)/).flatten.map(&:strip)
r_booleans.push({ name: data[0], state: data[1], default: data[2] })
end
r_booleans
end
end end
end end

3
test/fixtures/cmd/semanage-boolean vendored Normal file
View file

@ -0,0 +1,3 @@
xen_use_nfs (off , off) Allow xen to use nfs
xend_run_blktap (on , on) Allow xend to run blktap
zebra_write_config (off , off) Allow zebra to write config

3
test/fixtures/cmd/semodule-lfull vendored Normal file
View file

@ -0,0 +1,3 @@
100 foo pp
100 bar pp disabled
100 baz pp

View file

@ -558,11 +558,15 @@ class MockLoader
# filesystem command # filesystem command
"2e7e0d4546342cee799748ec7e2b1c87ca00afbe590fa422a7c27371eefa88f0" => cmd.call("get-wmiobject-filesystem"), "2e7e0d4546342cee799748ec7e2b1c87ca00afbe590fa422a7c27371eefa88f0" => cmd.call("get-wmiobject-filesystem"),
"sestatus" => cmd.call("sestatus"), "sestatus" => cmd.call("sestatus"),
"semodule -lfull" => cmd.call("semodule-lfull"),
"semanage boolean -l -n" => cmd.call("semanage-boolean"),
} }
if @platform && (@platform[:name] == "windows" || @platform[:name] == "freebsd") if @platform && (@platform[:name] == "windows" || @platform[:name] == "freebsd")
mock_cmds.merge!( mock_cmds.merge!(
"sestatus" => empty.call "sestatus" => empty.call,
"semodule -lfull" => empty.call,
"semanage boolean -l -n" => empty.call
) )
end end

View file

@ -3,26 +3,29 @@ require "inspec/resource"
require "inspec/resources/selinux" require "inspec/resources/selinux"
describe "Inspec::Resources::Selinux" do describe "Inspec::Resources::Selinux" do
let(:resource) { load_resource("selinux") }
it "verify selinux is installed" do it "verify selinux is installed" do
resource = load_resource("selinux", "/etc/selinux/selinux_conf") resource = load_resource("selinux", "/etc/selinux/selinux_conf")
_(resource.installed?).must_equal true _(resource.installed?).must_equal true
end end
it "verify selinux state - enforcing" do it "verify selinux state - enforcing" do
resource = load_resource("selinux")
_(resource.enforcing?).must_equal true _(resource.enforcing?).must_equal true
end end
it "verify selinux state - permissive" do it "verify selinux state - permissive" do
resource = load_resource("selinux")
_(resource.permissive?).must_equal false _(resource.permissive?).must_equal false
end end
it "verify selinux disabled " do it "verify selinux disabled" do
resource = load_resource("selinux")
_(resource.disabled?).must_equal false _(resource.disabled?).must_equal false
end end
it "verify selinux policy type is targeted" do
_(resource.policy).must_equal "targeted"
end
it "verify selinux on linux" do it "verify selinux on linux" do
resource = MockLoader.new(:linux).load_resource("selinux") resource = MockLoader.new(:linux).load_resource("selinux")
_(resource.enforcing?).must_equal true _(resource.enforcing?).must_equal true
@ -41,4 +44,25 @@ describe "Inspec::Resources::Selinux" do
_(resource.installed?).must_equal false _(resource.installed?).must_equal false
_(resource.enforcing?).must_equal false _(resource.enforcing?).must_equal false
end end
it "verify selinux.modules is exist" do
_(resource.modules.exist?).must_equal true
end
it "verify selinux.modules parsing" do
_(resource.modules.names).must_equal %w{foo bar baz}
_(resource.modules.states).must_equal %w{enabled disabled enabled}
_(resource.modules.status).must_equal %w{installed installed installed}
_(resource.modules.priorities).must_equal %w{100 100 100}
end
it "verify selinux.booleans is exist" do
_(resource.booleans.exist?).must_equal true
end
it "verify selinux.booleans parsing" do
_(resource.booleans.names).must_equal %w{xen_use_nfs xend_run_blktap zebra_write_config}
_(resource.booleans.states).must_equal %w{off on off}
_(resource.booleans.defaults).must_equal %w{off on off}
end
end end