summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorneha-p6 <neha.pansare@progress.com>2022-04-11 11:00:57 +0530
committerGitHub <noreply@github.com>2022-04-11 11:00:57 +0530
commitd3dda786b2a6d61025288c002ae7b5b60c499f92 (patch)
tree2e48a0e853c05ca404be357ba2c928d491ec7b01
parentd3e922a67abec71825d6f6c6ac8fd4ef9fc53019 (diff)
downloadchef-d3dda786b2a6d61025288c002ae7b5b60c499f92.tar.gz
SELinux integration to infra client (#12694)
* 1. Add resources for SELlinux 2. Add common helper for SELinux under a new subdirectory 3. Wire files together with corresponding changes Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 3. Include SElinux CommonHelper under action_class for corresponding resources as it uses shell_out! 4. Add SELinux config file templates for debian and default versions Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 5.Add local mode true to correctly parse template from selinux_state resource Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 6. Remove SELinux cookbook dependency from kitchen-tests as SELinux resources are now part of core chef client, update linux.rb recipe to use corresponding SELinux resources instead of include_recipe Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 7. Add unit test cases for SELinux resources 8. Add documentation for SELinux resources Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 9. Obvious fix: code linting and spellcheck Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 10. Add code linting changes. 11. Add missing comma in cspell.json resulting in issue Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 12. Add linting and spellcheck changes Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 13. Add documentation for SELinux resources for all properties, actions with examples 14. Added permissive SELinux policy for en_to_end kitchen test Signed-off-by: Neha Pansare <neha.pansare@progress.com> * 15. Fix chefstyle linting 16. Update few shell_out calls to use array format of input parameters Signed-off-by: Neha Pansare <neha.pansare@progress.com>
-rw-r--r--cspell.json26
-rw-r--r--kitchen-tests/cookbooks/end_to_end/metadata.rb1
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/linux.rb6
-rw-r--r--lib/chef/resource/selinux/common_helpers.rb47
-rw-r--r--lib/chef/resource/selinux/selinux_debian.erb18
-rw-r--r--lib/chef/resource/selinux/selinux_default.erb15
-rw-r--r--lib/chef/resource/selinux_boolean.rb101
-rw-r--r--lib/chef/resource/selinux_fcontext.rb160
-rw-r--r--lib/chef/resource/selinux_install.rb107
-rw-r--r--lib/chef/resource/selinux_module.rb143
-rw-r--r--lib/chef/resource/selinux_permissive.rb64
-rw-r--r--lib/chef/resource/selinux_port.rb118
-rw-r--r--lib/chef/resource/selinux_state.rb166
-rw-r--r--lib/chef/resources.rb7
-rw-r--r--spec/unit/resource/selinux_boolean_spec.rb92
-rw-r--r--spec/unit/resource/selinux_fcontext_spec.rb65
-rw-r--r--spec/unit/resource/selinux_install_spec.rb60
-rw-r--r--spec/unit/resource/selinux_module_spec.rb55
-rw-r--r--spec/unit/resource/selinux_permissive_spec.rb39
-rw-r--r--spec/unit/resource/selinux_port_spec.rb42
-rw-r--r--spec/unit/resource/selinux_state_spec.rb46
21 files changed, 1376 insertions, 2 deletions
diff --git a/cspell.json b/cspell.json
index 681d67fc76..2d09fea171 100644
--- a/cspell.json
+++ b/cspell.json
@@ -21,6 +21,7 @@
"activationkey",
"ADAP",
"addlock",
+ "addormodify",
"addrs",
"ADMINI",
"adminonly",
@@ -321,6 +322,7 @@
"ecparam",
"edir",
"egid",
+ "egrep",
"ELOOP",
"EMBEDDEDNT",
"EMBEDDEDSERVER",
@@ -366,7 +368,9 @@
"faststart",
"faststop",
"fatals",
+ "Fcontext",
"fcntl",
+ "fcontext",
"featurename",
"FFLAGS",
"fflags",
@@ -408,6 +412,7 @@
"getaddrinfo",
"getbinaryfile",
"getc",
+ "getenforce",
"GETFD",
"GETFL",
"getgrgid",
@@ -422,6 +427,7 @@
"getprint",
"getprocaddr",
"getremotelogin",
+ "getsebool",
"getspnam",
"gettext",
"GETTHUMBPRINTCODE",
@@ -583,6 +589,7 @@
"KEYNAME",
"keyname",
"keyscan",
+ "keysign",
"KEYTAB",
"keytab",
"kool",
@@ -610,6 +617,7 @@
"lgrpi",
"LGRPID",
"LHND",
+ "libselinux",
"linuxmint",
"LISTBOX",
"listprop",
@@ -730,6 +738,7 @@
"MONTHLYDATE",
"MONTHLYDOW",
"monthlydow",
+ "moodle",
"mountpoint",
"mounttab",
"mpkg",
@@ -919,6 +928,7 @@
"PDWORD",
"PDWORDLONG",
"performant",
+ "permissives",
"PFILETIME",
"PFLOAT",
"PGENERICMAPPING",
@@ -946,8 +956,10 @@
"plutil",
"PNAME",
"Policybuilder",
+ "policycoreutils",
"POLICYNAME",
"portageq",
+ "portcon",
"PORTVERSION",
"POSI",
"POST'ing",
@@ -1026,6 +1038,7 @@
"rcscript",
"rcvar",
"RDWR",
+ "readdir",
"readlink",
"realloc",
"realname",
@@ -1049,6 +1062,7 @@
"Referer",
"regen",
"regexes",
+ "regextype",
"REGSAM",
"reimplement",
"reimplementing",
@@ -1125,6 +1139,7 @@
"Scriptable",
"SCROLLBAR",
"SCROLLBARS",
+ "secontext",
"secoption",
"secopts",
"secp",
@@ -1134,7 +1149,12 @@
"secvalue",
"SEGDPL",
"SEGLIM",
+ "seinfo",
"selinuxenabled",
+ "SELINUXTYPE",
+ "selinuxtype",
+ "semanage",
+ "semodule",
"SEPCHARS",
"SERENUM",
"sertelon",
@@ -1148,13 +1168,17 @@
"SESSIONCHANGE",
"SESSIONID",
"SETCOUNT",
+ "setenforce",
"SETFD",
"SETFL",
+ "SETLOCALDEFS",
"setlocal",
"SETMARK",
"setobj",
"setprop",
"setremotelogin",
+ "setools",
+ "setsebool",
"setsid",
"settimezone",
"SETTINGCHANGE",
@@ -1269,6 +1293,7 @@
"swappable",
"symlinking",
"syntaxcache",
+ "sysadm",
"sysconfig",
"sysctld",
"sysctls",
@@ -1437,6 +1462,7 @@
"wchar",
"wchars",
"WEBHOSTING",
+ "webroot",
"webserver",
"whatavailable",
"whateverd",
diff --git a/kitchen-tests/cookbooks/end_to_end/metadata.rb b/kitchen-tests/cookbooks/end_to_end/metadata.rb
index 71ddd0d6e3..2f623df90c 100644
--- a/kitchen-tests/cookbooks/end_to_end/metadata.rb
+++ b/kitchen-tests/cookbooks/end_to_end/metadata.rb
@@ -11,7 +11,6 @@ depends "nscd"
depends "ntp"
depends "openssh"
depends "resolver"
-depends "selinux"
depends "users", "< 7.1" # 7.1 breaks macos / opensuse
depends "git"
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
index f306a22164..eb4f4fe546 100644
--- a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
@@ -28,7 +28,11 @@ timezone "America/Los_Angeles"
include_recipe "::_yum" if platform_family?("rhel")
if platform_family?("rhel", "fedora", "amazon")
- include_recipe "selinux::disabled"
+ selinux_install "selinux"
+
+ selinux_state "permissive" do
+ action :permissive
+ end
end
build_essential do
diff --git a/lib/chef/resource/selinux/common_helpers.rb b/lib/chef/resource/selinux/common_helpers.rb
new file mode 100644
index 0000000000..a67943abad
--- /dev/null
+++ b/lib/chef/resource/selinux/common_helpers.rb
@@ -0,0 +1,47 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+class Chef
+ module SELinux
+ module CommonHelpers
+ def selinux_disabled?
+ selinux_state.eql?(:disabled)
+ end
+
+ def selinux_enforcing?
+ selinux_state.eql?(:enforcing)
+ end
+
+ def selinux_permissive?
+ selinux_state.eql?(:permissive)
+ end
+
+ def state_change_reboot_required?
+ (selinux_disabled? && %i{enforcing permissive}.include?(action)) || ((selinux_enforcing? || selinux_permissive?) && action == :disabled)
+ end
+
+ def selinux_state
+ state = shell_out!("getenforce").stdout.strip.downcase.to_sym
+ raise "Got unknown SELinux state #{state}" unless %i{disabled enforcing permissive}.include?(state)
+
+ state
+ end
+
+ def selinux_activate_required?
+ return false unless platform_family?("debian")
+
+ !File.read("/etc/default/grub").match?("security=selinux")
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/selinux/selinux_debian.erb b/lib/chef/resource/selinux/selinux_debian.erb
new file mode 100644
index 0000000000..07a11a0239
--- /dev/null
+++ b/lib/chef/resource/selinux/selinux_debian.erb
@@ -0,0 +1,18 @@
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand.
+#
+
+# This file controls the state of SELinux on the system.
+# SELINUX= can take one of these three values:
+# enforcing - SELinux security policy is enforced.
+# permissive - SELinux prints warnings instead of enforcing.
+# disabled - No SELinux policy is loaded.
+SELINUX=<%= @selinux %>
+# SELINUXTYPE= can take one of these three values:
+# default - equivalent to the old strict and targeted policies
+# mls - Multi-Level Security (for military and educational use)
+# src - Custom policy built from source
+SELINUXTYPE=<%= @selinuxtype %>
+
+# SETLOCALDEFS= Check local definition changes
+SETLOCALDEFS=0 \ No newline at end of file
diff --git a/lib/chef/resource/selinux/selinux_default.erb b/lib/chef/resource/selinux/selinux_default.erb
new file mode 100644
index 0000000000..0fec407493
--- /dev/null
+++ b/lib/chef/resource/selinux/selinux_default.erb
@@ -0,0 +1,15 @@
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand.
+#
+
+# This file controls the state of SELinux on the system.
+# SELINUX= can take one of these three values:
+# enforcing - SELinux security policy is enforced.
+# permissive - SELinux prints warnings instead of enforcing.
+# disabled - No SELinux policy is loaded.
+SELINUX=<%= @selinux %>
+# SELINUXTYPE= can take one of these three values:
+# targeted - Targeted processes are protected,
+# minimum - Modification of targeted policy. Only selected processes are protected.
+# mls - Multi Level Security protection.
+SELINUXTYPE=<%= @selinuxtype %> \ No newline at end of file
diff --git a/lib/chef/resource/selinux_boolean.rb b/lib/chef/resource/selinux_boolean.rb
new file mode 100644
index 0000000000..919c1d274a
--- /dev/null
+++ b/lib/chef/resource/selinux_boolean.rb
@@ -0,0 +1,101 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require_relative "../resource"
+require_relative "selinux/common_helpers"
+
+class Chef
+ class Resource
+ class SelinuxBoolean < Chef::Resource
+ unified_mode true
+
+ provides :selinux_boolean
+
+ description "Use **selinux_boolean** resource to set SELinux boolean values."
+ introduced "18.0"
+ examples <<~DOC
+ **Set ssh_keysign to true**:
+
+ ```ruby
+ selinux_boolean 'ssh_keysign' do
+ value true
+ end
+ ```
+
+ **Set ssh_sysadm_login to 'on'**:
+
+ ```ruby
+ selinux_boolean 'ssh_sysadm_login' do
+ value 'on'
+ end
+ ```
+ DOC
+
+ property :boolean, String,
+ name_property: true,
+ description: "SELinux boolean to set."
+
+ property :value, [Integer, String, true, false],
+ required: true,
+ equal_to: %w{on off},
+ coerce: proc { |p| selinux_bool(p) },
+ description: "SELinux boolean value."
+
+ property :persistent, [true, false],
+ default: true,
+ desired_state: false,
+ description: "Set to true for value setting to survive reboot."
+
+ load_current_value do |new_resource|
+ value shell_out!("getsebool", new_resource.boolean).stdout.split("-->").map(&:strip).last
+ end
+
+ action_class do
+ include Chef::SELinux::CommonHelpers
+ end
+
+ action :set , description: "Set the state of the boolean." do
+ if selinux_disabled?
+ Chef::Log.warn("Unable to set SELinux boolean #{new_resource.name} as SELinux is disabled")
+ return
+ end
+
+ converge_if_changed do
+ cmd = "setsebool"
+ cmd += " -P" if new_resource.persistent
+ cmd += " #{new_resource.boolean} #{new_resource.value}"
+
+ shell_out!(cmd)
+ end
+ end
+
+ private
+
+ #
+ # Validate and return input boolean value in required format
+ # @param bool [String, Integer, Boolean] Input boolean value in allowed formats
+ #
+ # @return [String] [description] Boolean value in required format
+ def selinux_bool(bool)
+ if ["on", "true", "1", true, 1].include?(bool)
+ "on"
+ elsif ["off", "false", "0", false, 0].include?(bool)
+ "off"
+ else
+ raise ArgumentError, "selinux_bool: Invalid selinux boolean value #{bool}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/selinux_fcontext.rb b/lib/chef/resource/selinux_fcontext.rb
new file mode 100644
index 0000000000..243b180f3c
--- /dev/null
+++ b/lib/chef/resource/selinux_fcontext.rb
@@ -0,0 +1,160 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require_relative "../resource"
+require_relative "selinux/common_helpers"
+
+class Chef
+ class Resource
+ class SelinuxFcontext < Chef::Resource
+ unified_mode true
+
+ provides :selinux_fcontext
+
+ description "Use **selinux_fcontext** resource to set the SELinux context of files with semanage fcontext."
+ introduced "18.0"
+ examples <<~DOC
+ **Allow http servers (e.g. nginx/apache) to modify moodle files**:
+
+ ```ruby
+ selinux_fcontext '/var/www/moodle(/.*)?' do
+ secontext 'httpd_sys_rw_content_t'
+ end
+ ```
+
+ **Adapt a symbolic link**:
+
+ ```ruby
+ selinux_fcontext '/var/www/symlink_to_webroot' do
+ secontext 'httpd_sys_rw_content_t'
+ file_type 'l'
+ end
+ ```
+ DOC
+
+ property :file_spec, String,
+ name_property: true,
+ description: "Path to or regex matching the files or directories to label."
+
+ property :secontext, String,
+ required: %i{add modify manage},
+ description: "SELinux context to assign."
+
+ property :file_type, String,
+ default: "a",
+ equal_to: %w{a f d c b s l p},
+ description: "The type of the file being labeled."
+
+ action_class do
+ include Chef::SELinux::CommonHelpers
+ def current_file_context
+ file_hash = {
+ "a" => "all files",
+ "f" => "regular file",
+ "d" => "directory",
+ "c" => "character device",
+ "b" => "block device",
+ "s" => "socket",
+ "l" => "symbolic link",
+ "p" => "named pipe",
+ }
+
+ contexts = shell_out!("semanage fcontext -l").stdout.split("\n")
+ # pull out file label from user:role:type:level context string
+ contexts.grep(/^#{Regexp.escape(new_resource.file_spec)}\s+#{file_hash[new_resource.file_type]}/) do |c|
+ c.match(/.+ (?<user>.+):(?<role>.+):(?<type>.+):(?<level>.+)$/)[:type]
+ # match returns ['foo'] or [], shift converts that to 'foo' or nil
+ end.shift
+ end
+
+ # Run restorecon to fix label
+ # https://github.com/sous-chefs/selinux_policy/pull/72#issuecomment-338718721
+ def relabel_files
+ spec = new_resource.file_spec
+ escaped = Regexp.escape spec
+
+ # find common path between regex and string
+ common = if spec == escaped
+ spec
+ else
+ index = spec.size.times { |i| break i if spec[i] != escaped[i] }
+ ::File.dirname spec[0...index]
+ end
+
+ # if path is not absolute, ignore it and search everything
+ common = "/" if common[0] != "/"
+
+ if ::File.exist? common
+ shell_out!("find #{common.shellescape} -ignore_readdir_race -regextype posix-egrep -regex #{spec.shellescape} -prune -print0 | xargs -0 restorecon -iRv")
+ end
+ end
+ end
+
+ action :manage, description: "Assign the file to the right context regardless of previous state." do
+ run_action(:add)
+ run_action(:modify)
+ end
+
+ action :addormodify, description: "Assign the file context if not set. Update the file context if previously set." do
+ Chef::Log.warn("The :addormodify action for selinux_fcontext is deprecated and will be removed in a future release. Use the :manage action instead.")
+ run_action(:manage)
+ end
+
+ # Create if doesn't exist, do not touch if fcontext is already registered
+ action :add, description: "Assign the file context if not set." do
+ if selinux_disabled?
+ Chef::Log.warn("Unable to add SELinux fcontext #{new_resource.name} as SELinux is disabled")
+ return
+ end
+
+ unless current_file_context
+ converge_by "adding label #{new_resource.secontext} to #{new_resource.file_spec}" do
+ shell_out!("semanage fcontext -a -f #{new_resource.file_type} -t #{new_resource.secontext} '#{new_resource.file_spec}'")
+ relabel_files
+ end
+ end
+ end
+
+ # Only modify if fcontext exists & doesn't have the correct label already
+ action :modify, description: "Update the file context if previously set." do
+ if selinux_disabled?
+ Chef::Log.warn("Unable to modify SELinux fcontext #{new_resource.name} as SELinux is disabled")
+ return
+ end
+
+ if current_file_context && current_file_context != new_resource.secontext
+ converge_by "modifying label #{new_resource.secontext} to #{new_resource.file_spec}" do
+ shell_out!("semanage fcontext -m -f #{new_resource.file_type} -t #{new_resource.secontext} '#{new_resource.file_spec}'")
+ relabel_files
+ end
+ end
+ end
+
+ # Delete if exists
+ action :delete, description: "Removes the file context if set. " do
+ if selinux_disabled?
+ Chef::Log.warn("Unable to delete SELinux fcontext #{new_resource.name} as SELinux is disabled")
+ return
+ end
+
+ if current_file_context
+ converge_by "deleting label for #{new_resource.file_spec}" do
+ shell_out!("semanage fcontext -d -f #{new_resource.file_type} '#{new_resource.file_spec}'")
+ relabel_files
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/resource/selinux_install.rb b/lib/chef/resource/selinux_install.rb
new file mode 100644
index 0000000000..d53a74392a
--- /dev/null
+++ b/lib/chef/resource/selinux_install.rb
@@ -0,0 +1,107 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require_relative "../resource"
+
+class Chef
+ class Resource
+ class SelinuxInstall < Chef::Resource
+ unified_mode true
+
+ provides :selinux_install
+
+ description "Use **selinux_install** resource to encapsulates the set of selinux packages to install in order to manage selinux. It also ensures the directory `/etc/selinux` is created."
+ introduced "18.0"
+ examples <<~DOC
+ **Default installation**:
+
+ ```ruby
+ selinux_install 'example'
+ ```
+
+ **Install with custom packages**:
+
+ ```ruby
+ selinux_install 'example' do
+ packages %w(policycoreutils selinux-policy selinux-policy-targeted)
+ end
+ ```
+
+ **Uninstall**
+ ```ruby
+ selinux_install 'example' do
+ action :remove
+ end
+ ```
+ DOC
+
+ property :packages, [String, Array],
+ default: lazy { default_install_packages },
+ description: "SELinux packages for system."
+
+ action_class do
+ def do_package_action(action)
+ # friendly message for unsupported platforms
+ raise "The platform #{node["platform"]} is not currently supported by the `selinux_install` resource. Please file an issue at https://github.com/chef/chef/issues with details on the platform this cookbook is running on." if new_resource.packages.nil?
+
+ package "selinux" do
+ package_name new_resource.packages
+ action action
+ end
+ end
+ end
+
+ action :install, description: "Install required packages." do
+ do_package_action(action)
+
+ directory "/etc/selinux" do
+ owner "root"
+ group "root"
+ mode "0755"
+ action :create
+ end
+ end
+
+ action :upgrade, description: "Upgrade required packages." do
+ do_package_action(a)
+ end
+
+ action :remove, description: "Remove any SELinux-related packages." do
+ do_package_action(a)
+ end
+
+ private
+
+ #
+ # Get an array of packages to be installed based upon node platform_family
+ #
+ # @return [Array] Array of string of package names
+ def default_install_packages
+ case node["platform_family"]
+ when "rhel", "fedora", "amazon"
+ %w{make policycoreutils selinux-policy selinux-policy-targeted selinux-policy-devel libselinux-utils setools-console}
+ when "debian"
+ if node["platform"] == "ubuntu"
+ if node["platform_version"].to_f == 18.04
+ %w{make policycoreutils selinux selinux-basics selinux-policy-default selinux-policy-dev auditd setools}
+ else
+ %w{make policycoreutils selinux-basics selinux-policy-default selinux-policy-dev auditd setools}
+ end
+ else
+ %w{make policycoreutils selinux-basics selinux-policy-default selinux-policy-dev auditd setools}
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/selinux_module.rb b/lib/chef/resource/selinux_module.rb
new file mode 100644
index 0000000000..49810e4b23
--- /dev/null
+++ b/lib/chef/resource/selinux_module.rb
@@ -0,0 +1,143 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require_relative "../resource"
+
+class Chef
+ class Resource
+ class SelinuxModule < Chef::Resource
+ unified_mode true
+
+ provides :selinux_module
+
+ description "Use **selinux_module** module resource to create an SELinux policy module from a cookbook file or content provided as a string."
+ introduced "18.0"
+ examples <<~DOC
+ **Creating SElinux module from .te file located at `files` directory of your cookbook.**:
+
+ ```ruby
+ selinux_module 'my_policy_module' do
+ source 'my_policy_module.te'
+ action :create
+ end
+ ```
+ DOC
+
+ property :module_name, String,
+ name_property: true,
+ description: "Override the module name."
+
+ property :source, String,
+ description: "Module source file name."
+
+ property :content, String,
+ description: "Module source as String."
+
+ property :cookbook, String,
+ description: "Cookbook to source from module source file from(if it is not located in the current cookbook). The default value is the current cookbook.",
+ desired_state: false
+
+ property :base_dir, String,
+ default: "/etc/selinux/local",
+ description: "Directory to create module source file in."
+
+ action_class do
+ def selinux_module_filepath(type)
+ path = ::File.join(new_resource.base_dir, "#{new_resource.module_name}")
+ path.concat(".#{type}") if type
+ end
+
+ def list_installed_modules
+ shell_out!("semodule --list-modules").stdout.split("\n").map { |x| x.split(/\s/).first }
+ end
+ end
+
+ action :create, description: "Compile a module and install it." do
+ directory new_resource.base_dir
+
+ if property_is_set?(:content)
+ file selinux_module_filepath("te") do
+ content new_resource.content
+
+ mode "0600"
+ owner "root"
+ group "root"
+
+ action :create
+
+ notifies :run, "execute[Compiling SELinux modules at '#{new_resource.base_dir}']", :immediately
+ end
+ else
+ cookbook_file selinux_module_filepath("te") do
+ cookbook new_resource.cookbook
+ source new_resource.source
+
+ mode "0600"
+ owner "root"
+ group "root"
+
+ action :create
+
+ notifies :run, "execute[Compiling SELinux modules at '#{new_resource.base_dir}']", :immediately
+ end
+ end
+
+ execute "Compiling SELinux modules at '#{new_resource.base_dir}'" do
+ cwd new_resource.base_dir
+ command "make -C #{new_resource.base_dir} -f /usr/share/selinux/devel/Makefile"
+ timeout 120
+ user "root"
+
+ action :nothing
+
+ notifies :run, "execute[Install SELinux module '#{selinux_module_filepath("pp")}']", :immediately
+ end
+
+ raise "Compilation must have failed, no 'pp' file found at: '#{selinux_module_filepath("pp")}'" unless ::File.exist?(selinux_module_filepath("pp"))
+
+ execute "Install SELinux module '#{selinux_module_filepath("pp")}'" do
+ command "semodule --install '#{selinux_module_filepath("pp")}'"
+ action :nothing
+ end
+ end
+
+ action :delete, description: "Remove module source files from `/etc/selinux/local`." do
+ %w{fc if pp te}.each do |type|
+ next unless ::File.exist?(selinux_module_filepath(type))
+
+ file selinux_module_filepath(type) do
+ action :delete
+ end
+ end
+ end
+
+ action :install, description: "Install a compiled module into the system." do
+ raise "Module must be compiled before it can be installed, no 'pp' file found at: '#{selinux_module_filepath("pp")}'" unless ::File.exist?(selinux_module_filepath("pp"))
+
+ unless list_installed_modules.include? new_resource.module_name
+ converge_by "Install SELinux module #{selinux_module_filepath("pp")}" do
+ shell_out!("semodule", "--install", selinux_module_filepath("pp"))
+ end
+ end
+ end
+
+ action :remove, description: "Remove a module from the system." do
+ if list_installed_modules.include? new_resource.module_name
+ converge_by "Remove SELinux module #{new_resource.module_name}" do
+ shell_out!("semodule", "--remove", new_resource.module_name)
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/resource/selinux_permissive.rb b/lib/chef/resource/selinux_permissive.rb
new file mode 100644
index 0000000000..155f4ef390
--- /dev/null
+++ b/lib/chef/resource/selinux_permissive.rb
@@ -0,0 +1,64 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require_relative "../resource"
+
+class Chef
+ class Resource
+ class SelinuxPermissive < Chef::Resource
+ unified_mode true
+
+ provides :selinux_permissive
+
+ description "Use **selinux_permissive** resource to allows some types to misbehave without stopping them. Not as good as specific policies, but better than disabling SELinux entirely."
+ introduced "18.0"
+ examples <<~DOC
+ **Disable enforcement on Apache**:
+
+ ```ruby
+ selinux_permissive 'httpd_t' do
+ notifies :restart, 'service[httpd]'
+ end
+ ```
+ DOC
+
+ property :context, String,
+ name_property: true,
+ description: "The SELinux context to permit."
+
+ action_class do
+ def current_permissives
+ shell_out!("semanage permissive -ln").stdout.split("\n")
+ end
+ end
+
+ # Create if doesn't exist, do not touch if permissive is already registered (even under different type)
+ action :add, description: "Add a permissive, unless already set." do
+ unless current_permissives.include? new_resource.context
+ converge_by "adding permissive context #{new_resource.context}" do
+ shell_out!("semanage permissive -a '#{new_resource.context}'")
+ end
+ end
+ end
+
+ # Delete if exists
+ action :delete, description: "Remove a permissive, if set." do
+ if current_permissives.include? new_resource.context
+ converge_by "deleting permissive context #{new_resource.context}" do
+ shell_out!("semanage permissive -d '#{new_resource.context}'")
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/resource/selinux_port.rb b/lib/chef/resource/selinux_port.rb
new file mode 100644
index 0000000000..8690adf9b1
--- /dev/null
+++ b/lib/chef/resource/selinux_port.rb
@@ -0,0 +1,118 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require_relative "../resource"
+require_relative "selinux/common_helpers"
+
+class Chef
+ class Resource
+ class SelinuxPort < Chef::Resource
+ unified_mode true
+
+ provides :selinux_port
+
+ description "Use **selinux_port** resource to allows assigning a network port to a certain SELinux context, e.g. for running a webserver on a non-standard port."
+ introduced "18.0"
+ examples <<~DOC
+ **Allow nginx/apache to bind to port 5678 by giving it the http_port_t context**:
+
+ ```ruby
+ selinux_port '5678' do
+ protocol 'tcp'
+ secontext 'http_port_t'
+ end
+ ```
+ DOC
+
+ property :port, [Integer, String],
+ name_property: true,
+ regex: /^\d+$/,
+ description: "Port to modify."
+
+ property :protocol, String,
+ equal_to: %w{tcp udp},
+ required: %i{manage add modify},
+ description: "Protocol to modify."
+
+ property :secontext, String,
+ required: %i{manage add modify},
+ description: "SELinux context to assign to the port."
+
+ action_class do
+ include Chef::SELinux::CommonHelpers
+ def current_port_context
+ # use awk to see if the given port is within a reported port range
+ shell_out!(
+ <<~CMD
+ seinfo --portcon=#{new_resource.port} | grep 'portcon #{new_resource.protocol}' | \
+ awk -F: '$(NF-1) !~ /reserved_port_t$/ && $(NF-3) !~ /[0-9]*-[0-9]*/ {print $(NF-1)}'
+ CMD
+ ).stdout.split
+ end
+ end
+
+ action :manage, description: "Assign the port to the right context regardless of previous state." do
+ run_action(:add)
+ run_action(:modify)
+ end
+
+ action :addormodify, description: "Assigns the port context if not set. Updates the port context if previously set." do
+ Chef::Log.warn("The :addormodify action for selinux_port is deprecated and will be removed in a future release. Use the :manage action instead.")
+ run_action(:manage)
+ end
+
+ # Create if doesn't exist, do not touch if port is already registered (even under different type)
+ action :add, description: "Assign the port context if not set." do
+ if selinux_disabled?
+ Chef::Log.warn("Unable to add SELinux port #{new_resource.name} as SELinux is disabled")
+ return
+ end
+
+ if current_port_context.empty?
+ converge_by "Adding context #{new_resource.secontext} to port #{new_resource.port}/#{new_resource.protocol}" do
+ shell_out!("semanage port -a -t '#{new_resource.secontext}' -p #{new_resource.protocol} #{new_resource.port}")
+ end
+ end
+ end
+
+ # Only modify port if it exists & doesn't have the correct context already
+ action :modify, description: "Update the port context if previously set." do
+ if selinux_disabled?
+ Chef::Log.warn("Unable to modify SELinux port #{new_resource.name} as SELinux is disabled")
+ return
+ end
+
+ if !current_port_context.empty? && !current_port_context.include?(new_resource.secontext)
+ converge_by "Modifying context #{new_resource.secontext} to port #{new_resource.port}/#{new_resource.protocol}" do
+ shell_out!("semanage port -m -t '#{new_resource.secontext}' -p #{new_resource.protocol} #{new_resource.port}")
+ end
+ end
+ end
+
+ # Delete if exists
+ action :delete, description: "Removes the port context if set." do
+ if selinux_disabled?
+ Chef::Log.warn("Unable to delete SELinux port #{new_resource.name} as SELinux is disabled")
+ return
+ end
+
+ unless current_port_context.empty?
+ converge_by "Deleting context from port #{new_resource.port}/#{new_resource.protocol}" do
+ shell_out!("semanage port -d -p #{new_resource.protocol} #{new_resource.port}")
+ end
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/resource/selinux_state.rb b/lib/chef/resource/selinux_state.rb
new file mode 100644
index 0000000000..095a272ef7
--- /dev/null
+++ b/lib/chef/resource/selinux_state.rb
@@ -0,0 +1,166 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require_relative "../resource"
+require_relative "selinux/common_helpers"
+
+class Chef
+ class Resource
+ class SelinuxState < Chef::Resource
+ unified_mode true
+
+ provides :selinux_state
+
+ description "Use **selinux_state** resource to manages the SELinux state on the system. It does this by using the `setenforce` command and rendering the `/etc/selinux/config` file from a template."
+ introduced "18.0"
+ examples <<~DOC
+ **Set SELinux state to permissive**:
+
+ ```ruby
+ selinux_state 'permissive' do
+ action :permissive
+ end
+ ```
+
+ **Set SELinux state to enforcing**:
+
+ ```ruby
+ selinux_state 'enforcing' do
+ action :enforcing
+ end
+ ```
+
+ **Set SELinux state to disabled**:
+ ```ruby
+ selinux_state 'disabled' do
+ action :disabled
+ end
+ ```
+ DOC
+
+ default_action :nothing
+
+ property :config_file, String,
+ default: "/etc/selinux/config",
+ description: "Path to SELinux config file on disk."
+
+ property :persistent, [true, false],
+ default: true,
+ description: "Persist status update to the selinux configuration file."
+
+ property :policy, String,
+ default: lazy { default_policy_platform },
+ equal_to: %w{default minimum mls src strict targeted},
+ description: "SELinux policy type."
+
+ property :automatic_reboot, [true, false, Symbol],
+ default: false,
+ description: "Perform an automatic node reboot if required for state change."
+
+ deprecated_property_alias "temporary", "persistent", "The temporary property was renamed persistent in the 4.0 release of this cookbook. Please update your cookbooks to use the new property name."
+
+ action_class do
+ include Chef::SELinux::CommonHelpers
+ def render_selinux_template(action)
+ Chef::Log.warn("It is advised to set the configuration first to permissive to relabel the filesystem prior to enforcing.") if selinux_disabled? && action == :enforcing
+
+ unless new_resource.automatic_reboot
+ Chef::Log.warn("Changes from disabled require a reboot.") if selinux_disabled? && %i{enforcing permissive}.include?(action)
+ Chef::Log.warn("Disabling selinux requires a reboot.") if (selinux_enforcing? || selinux_permissive?) && action == :disabled
+ end
+
+ template "#{action} selinux config" do
+ path new_resource.config_file
+ source debian? ? ::File.expand_path("selinux/selinux_debian.erb", __dir__) : ::File.expand_path("selinux/selinux_default.erb", __dir__)
+ local true
+ variables(
+ selinux: action.to_s,
+ selinuxtype: new_resource.policy
+ )
+ end
+ end
+
+ def node_selinux_restart
+ unless new_resource.automatic_reboot
+ Chef::Log.warn("SELinux state change to #{action} requires a manual reboot as SELinux is currently #{selinux_state} and automatic reboots are disabled.")
+ return
+ end
+
+ outer_action = action
+ reboot "selinux_state_change" do
+ delay_mins 1
+ reason "SELinux state change to #{outer_action} from #{selinux_state}"
+
+ action new_resource.automatic_reboot.is_a?(Symbol) ? new_resource.automatic_reboot : :reboot_now
+ end
+ end
+ end
+
+ action :enforcing, description: "Set the SELinux state to enforcing." do
+ unless selinux_disabled? || selinux_enforcing?
+ execute "selinux-setenforce-enforcing" do
+ command "/usr/sbin/setenforce 1"
+ end
+ end
+
+ if selinux_activate_required?
+ execute "debian-selinux-activate" do
+ command "/usr/sbin/selinux-activate"
+ end
+ end
+
+ render_selinux_template(action) if new_resource.persistent
+ node_selinux_restart if state_change_reboot_required?
+ end
+
+ action :permissive, description: "Set the SELinux state to permissive." do
+ unless selinux_disabled? || selinux_permissive?
+ execute "selinux-setenforce-permissive" do
+ command "/usr/sbin/setenforce 0"
+ end
+ end
+
+ if selinux_activate_required?
+ execute "debian-selinux-activate" do
+ command "/usr/sbin/selinux-activate"
+ end
+ end
+
+ render_selinux_template(action) if new_resource.persistent
+ node_selinux_restart if state_change_reboot_required?
+ end
+
+ action :disabled, description: "Set the SELinux state to disabled. **NOTE**: Switching to or from disabled requires a reboot!" do
+ raise "A non-persistent change to the disabled SELinux status is not possible." unless new_resource.persistent
+
+ render_selinux_template(action)
+ node_selinux_restart if state_change_reboot_required?
+ end
+
+ private
+
+ #
+ # Decide default policy platform based upon platform_family
+ #
+ # @return [String] Policy platform name
+ def default_policy_platform
+ case node["platform_family"]
+ when "rhel", "fedora", "amazon"
+ "targeted"
+ when "debian"
+ "default"
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index ac5ec5d8e0..0d310f8bea 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -124,6 +124,13 @@ require_relative "resource/route"
require_relative "resource/ruby"
require_relative "resource/ruby_block"
require_relative "resource/script"
+require_relative "resource/selinux_boolean"
+require_relative "resource/selinux_fcontext"
+require_relative "resource/selinux_install"
+require_relative "resource/selinux_module"
+require_relative "resource/selinux_permissive"
+require_relative "resource/selinux_port"
+require_relative "resource/selinux_state"
require_relative "resource/service"
require_relative "resource/sudo"
require_relative "resource/sysctl"
diff --git a/spec/unit/resource/selinux_boolean_spec.rb b/spec/unit/resource/selinux_boolean_spec.rb
new file mode 100644
index 0000000000..63af7c5a3b
--- /dev/null
+++ b/spec/unit/resource/selinux_boolean_spec.rb
@@ -0,0 +1,92 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::SelinuxBoolean do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::SelinuxBoolean.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:set) }
+ let(:selinux_state) { double("shellout!", stdout: "permissive") }
+
+ it "sets boolean proprty as name_property" do
+ expect(resource.boolean).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "supports :set action" do
+ expect { resource.action :set }.not_to raise_error
+ end
+
+ context "coercing value property" do
+ it "should set value properly to 'on' when valid parameter is sent and is literal positive" do
+ resource.value = 1
+ expect(resource.value).to eql("on")
+
+ resource.value = "true"
+ expect(resource.value).to eql("on")
+
+ resource.value = true
+ expect(resource.value).to eql("on")
+ end
+
+ it "should set value properly to 'off' when valid parameter is sent and is literal negative" do
+ resource.value = 0
+ expect(resource.value).to eql("off")
+
+ resource.value = "false"
+ expect(resource.value).to eql("off")
+
+ resource.value = false
+ expect(resource.value).to eql("off")
+ end
+
+ it "should raise an exception if invalid parameter is sent" do
+ expect do
+ resource.value = "ON"
+ end.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "#Chef::SELinux::CommonHelpers" do
+ context "#selinux_permissive?" do
+ it "should return true if selinux_state is :permissive" do
+ allow(provider).to receive(:shell_out!).and_return(selinux_state)
+ expect(provider.selinux_permissive?).to eql(true)
+ end
+ end
+
+ context "#selinux_disabled?" do
+ it "should return false if selinux_state is :permissive" do
+ allow(provider).to receive(:shell_out!).and_return(selinux_state)
+ expect(provider.selinux_disabled?).to eql(false)
+ end
+ end
+
+ context "#selinux_enforcing?" do
+ it "should return false if selinux_state is :permissive" do
+ allow(provider).to receive(:shell_out!).and_return(selinux_state)
+ expect(provider.selinux_enforcing?).to eql(false)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/unit/resource/selinux_fcontext_spec.rb b/spec/unit/resource/selinux_fcontext_spec.rb
new file mode 100644
index 0000000000..7e1c31c23e
--- /dev/null
+++ b/spec/unit/resource/selinux_fcontext_spec.rb
@@ -0,0 +1,65 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::SelinuxFcontext do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::SelinuxFcontext.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:manage) }
+ let(:restoreconf) { double("shellout", stdout: "restorecon reset /var/www/html/index.html context unconfined_u:object_r:user_home_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0") }
+
+ it "sets file_spec proprty as name_property" do
+ expect(resource.file_spec).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :manage" do
+ expect(resource.action).to eql([:manage])
+ end
+
+ it "supports :manage, :addormodify, :add, :modify, :delete actions" do
+ expect { resource.action :manage }.not_to raise_error
+ expect { resource.action :addormodify }.not_to raise_error
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :modify }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+
+ it "checks 'a', 'f', 'd', 'c', 'b', 's', 'l', 'p' as valid file_type property values" do
+ expect { resource.file_type "a" }.not_to raise_error
+ expect { resource.file_type "f" }.not_to raise_error
+ expect { resource.file_type "d" }.not_to raise_error
+ expect { resource.file_type "c" }.not_to raise_error
+ expect { resource.file_type "b" }.not_to raise_error
+ expect { resource.file_type "s" }.not_to raise_error
+ expect { resource.file_type "l" }.not_to raise_error
+ expect { resource.file_type "p" }.not_to raise_error
+ end
+
+ it "sets default value for file_type property to 'a'" do
+ expect(resource.file_type).to eql("a")
+ end
+
+ describe "#relabel_files" do
+ it "returns verbose output with details of the file for which SELinux config is restored" do
+ allow(provider).to receive(:shell_out!).and_return(restoreconf)
+ expect(provider.relabel_files).to eql(restoreconf)
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/unit/resource/selinux_install_spec.rb b/spec/unit/resource/selinux_install_spec.rb
new file mode 100644
index 0000000000..5e82dfb840
--- /dev/null
+++ b/spec/unit/resource/selinux_install_spec.rb
@@ -0,0 +1,60 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::SelinuxInstall do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::SelinuxInstall.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:install) }
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install, :upgrade, :remove actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :upgrade }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ it "sets default packages on 'rhel', 'fedora', 'amazon' platforms" do
+ node.automatic_attrs[:platform_family] = "rhel"
+ expect(resource.packages).to eql(%w{make policycoreutils selinux-policy selinux-policy-targeted selinux-policy-devel libselinux-utils setools-console})
+ end
+
+ it "sets default packages on debian irrespective of platform_version" do
+ node.automatic_attrs[:platform_family] = "debian"
+ expect(resource.packages).to eql(%w{make policycoreutils selinux-basics selinux-policy-default selinux-policy-dev auditd setools})
+ end
+
+ it "sets default packages on ubuntu 18.04 platforms" do
+ node.automatic_attrs[:platform_family] = "debian"
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = 18.04
+ expect(resource.packages).to eql(%w{make policycoreutils selinux selinux-basics selinux-policy-default selinux-policy-dev auditd setools})
+ end
+
+ it "sets default packages on ubuntu platforms and versions other than 18.04" do
+ node.automatic_attrs[:platform_family] = "debian"
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = 20.04
+ expect(resource.packages).to eql(%w{make policycoreutils selinux-basics selinux-policy-default selinux-policy-dev auditd setools})
+ end
+end \ No newline at end of file
diff --git a/spec/unit/resource/selinux_module_spec.rb b/spec/unit/resource/selinux_module_spec.rb
new file mode 100644
index 0000000000..dadd9eb8fa
--- /dev/null
+++ b/spec/unit/resource/selinux_module_spec.rb
@@ -0,0 +1,55 @@
+#
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::SelinuxModule do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::SelinuxModule.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:create) }
+
+ it "sets module_name property as name_property" do
+ expect(resource.module_name).to eql("fakey_fakerton")
+ end
+
+ it "sets default value for base_dir property" do
+ expect(resource.base_dir).to eql("/etc/selinux/local")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create, :delete, :install, :remove actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ describe "#selinux_module_filepath" do
+ it "returns selinux module file path based upon base_dir property and module_name property" do
+ resource.base_dir = "/opt/selinux"
+ resource.module_name = "my_module"
+ file_type = "te"
+ expect(provider.selinux_module_filepath(file_type)).to eql("/opt/selinux/my_module.te")
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/unit/resource/selinux_permissive_spec.rb b/spec/unit/resource/selinux_permissive_spec.rb
new file mode 100644
index 0000000000..96ee3c3c81
--- /dev/null
+++ b/spec/unit/resource/selinux_permissive_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::SelinuxPermissive do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::SelinuxPermissive.new("fakey_fakerton", run_context) }
+ let(:provider) { resource.provider_for_action(:add) }
+
+ it "sets context property as name_property" do
+ expect(resource.context).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "supports :add, :delete actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+end \ No newline at end of file
diff --git a/spec/unit/resource/selinux_port_spec.rb b/spec/unit/resource/selinux_port_spec.rb
new file mode 100644
index 0000000000..2ed14c5ef6
--- /dev/null
+++ b/spec/unit/resource/selinux_port_spec.rb
@@ -0,0 +1,42 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::SelinuxPort do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::SelinuxPort.new("5678", run_context) }
+ let(:provider) { resource.provider_for_action(:manage) }
+
+ it "sets port property as name_property" do
+ expect(resource.port).to eql("5678")
+ end
+
+ it "sets the default action as :manage" do
+ expect(resource.action).to eql([:manage])
+ end
+
+ it "supports :manage, :addormodify, :add, :modify, :delete actions" do
+ expect { resource.action :manage }.not_to raise_error
+ expect { resource.action :addormodify }.not_to raise_error
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :modify }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ end
+end \ No newline at end of file
diff --git a/spec/unit/resource/selinux_state_spec.rb b/spec/unit/resource/selinux_state_spec.rb
new file mode 100644
index 0000000000..356a872627
--- /dev/null
+++ b/spec/unit/resource/selinux_state_spec.rb
@@ -0,0 +1,46 @@
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::SelinuxState do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::SelinuxState.new("5678", run_context) }
+ let(:provider) { resource.provider_for_action(:enforcing) }
+
+ it "sets the default action as :enforcing" do
+ expect(resource.action).to eql([:enforcing])
+ end
+
+ it "sets default value for policy property for 'rhel', 'fedora', 'amazon' platforms" do
+ node.automatic_attrs[:platform_family] = "rhel"
+ expect(resource.policy).to eql("targeted")
+ end
+
+ it "supports :enforcing, :permissive, :disabled actions" do
+ expect { resource.action :enforcing }.not_to raise_error
+ expect { resource.action :permissive }.not_to raise_error
+ expect { resource.action :disabled }.not_to raise_error
+ end
+
+ it "sets default value for policy property for debian platforms" do
+ node.automatic_attrs[:platform_family] = "debian"
+ expect(resource.policy).to eql("default")
+ end
+end \ No newline at end of file