mirror of
https://github.com/inspec/inspec
synced 2024-11-23 21:23:29 +00:00
Merge pull request #5463 from inspec/vasundhara/add_selinux_modules
Add selinux resource support for modules and booleans
This commit is contained in:
commit
3940a6374a
6 changed files with 244 additions and 17 deletions
|
@ -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
|
||||||
|
```
|
|
@ -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
3
test/fixtures/cmd/semanage-boolean
vendored
Normal 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
3
test/fixtures/cmd/semodule-lfull
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
100 foo pp
|
||||||
|
100 bar pp disabled
|
||||||
|
100 baz pp
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue