diff options
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 |