From 3f061035ba60d909279d8c5b721cd2bec0acbf8f Mon Sep 17 00:00:00 2001 From: Yansheng Wei <30584201+ywei2017@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:06:11 -0500 Subject: [PATCH] [issue #119] Update fcontext to allow override of built-in types (#120) * [issue #119] Update fcontext to allow override of built-in types --------- Signed-off-by: Yansheng Wei --- .github/workflows/ci.yml | 35 +++++++++++++------ documentation/selinux_boolean.md | 4 +-- documentation/selinux_fcontext.md | 4 +-- documentation/selinux_install.md | 4 +-- documentation/selinux_login.md | 4 +-- documentation/selinux_module.md | 4 +-- documentation/selinux_permissive.md | 4 +-- documentation/selinux_port.md | 4 +-- documentation/selinux_state.md | 6 ++-- documentation/selinux_user.md | 4 +-- kitchen.yml | 12 ++++--- resources/fcontext.rb | 9 ++++- spec/unit/resources/fcontext_spec.rb | 15 +++++--- .../selinux_test/recipes/fcontext.rb | 35 +++++++++++++++++++ .../fcontext/controls/fcontext_control.rb | 9 +++++ 15 files changed, 113 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bcc2e45..bbdaf49e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,28 +10,34 @@ name: ci jobs: lint-unit: uses: sous-chefs/.github/.github/workflows/lint-unit.yml@3.1.1 + permissions: + actions: write + checks: write + pull-requests: write + statuses: write + issues: write integration: needs: lint-unit - runs-on: macos-10.15 + runs-on: ubuntu-latest strategy: matrix: os: - almalinux-8 + - almalinux-9 - amazonlinux-2 - - centos-7 - - centos-stream-8 + - amazonlinux-2023 + - centos-stream-9 - fedora-latest - - debian-10 - debian-11 - - oracle-7 + - debian-12 - oracle-8 + - oracle-9 - rockylinux-8 - - ubuntu-1804 + - rockylinux-9 - ubuntu-2004 - # Disabled due to image problems: - # https://github.com/chef/bento/issues/1405 - # - ubuntu-2204 + - ubuntu-2204 + - ubuntu-2404 suite: - enforcing - permissive @@ -39,8 +45,17 @@ jobs: - port - fcontext fail-fast: false - steps: + - name: Install Vagrant VirtualBox + run: | + wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg + wget -O- https://www.virtualbox.org/download/oracle_vbox_2016.asc | gpg --dearmor | sudo tee /usr/share/keyrings/oracle-virtualbox-2016.gpg + echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list + echo "deb [signed-by=/usr/share/keyrings/oracle-virtualbox-2016.gpg] https://download.virtualbox.org/virtualbox/debian $(lsb_release -cs) contrib" | sudo tee /etc/apt/sources.list.d/virtualbox.list + sudo apt-get update + sudo apt-get install -y software-properties-common vagrant virtualbox + vagrant --version + vboxmanage --version - name: Check out code uses: actions/checkout@v4 - name: Install Chef diff --git a/documentation/selinux_boolean.md b/documentation/selinux_boolean.md index 24b47965..53f8da7e 100644 --- a/documentation/selinux_boolean.md +++ b/documentation/selinux_boolean.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_boolean +[Back to resource list](../README.md#resources) + Set SELinux boolean values. Introduced: v4.0.0 diff --git a/documentation/selinux_fcontext.md b/documentation/selinux_fcontext.md index 959f883d..ab554e2e 100644 --- a/documentation/selinux_fcontext.md +++ b/documentation/selinux_fcontext.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_fcontext +[Back to resource list](../README.md#resources) + Set the SELinux context of files with `semanage fcontext`. ## Actions diff --git a/documentation/selinux_install.md b/documentation/selinux_install.md index 98633af0..cb8d238a 100644 --- a/documentation/selinux_install.md +++ b/documentation/selinux_install.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_install +[Back to resource list](../README.md#resources) + The `selinux_install` resource is used to encapsulate the set of selinux packages to install in order to manage selinux. It also ensures the directory `/etc/selinux` is created. Introduced: v4.0.0 diff --git a/documentation/selinux_login.md b/documentation/selinux_login.md index a3547f48..86d4cab1 100644 --- a/documentation/selinux_login.md +++ b/documentation/selinux_login.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_login +[Back to resource list](../README.md#resources) + The `selinux_login` resource is used to manage Linux user to SELinux user mappings on the system. ## Actions diff --git a/documentation/selinux_module.md b/documentation/selinux_module.md index 38a4e47b..375c6507 100644 --- a/documentation/selinux_module.md +++ b/documentation/selinux_module.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_module +[Back to resource list](../README.md#resources) + Create an SELinux module from a cookfile file or content provided as a string. Introduced: v4.0.0 diff --git a/documentation/selinux_permissive.md b/documentation/selinux_permissive.md index 5c5ee9f9..3df55aad 100644 --- a/documentation/selinux_permissive.md +++ b/documentation/selinux_permissive.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_permissive +[Back to resource list](../README.md#resources) + Allows some types to misbehave without stopping them. Not as good as specific policies, but better than disabling SELinux entirely. > This does not set the SELinux state to permissive! Use [`selinux_state`](selinux_state.md) for that. diff --git a/documentation/selinux_port.md b/documentation/selinux_port.md index 2e173e1a..667b87f7 100644 --- a/documentation/selinux_port.md +++ b/documentation/selinux_port.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_port +[Back to resource list](../README.md#resources) + Allows assigning a network port to a certain SELinux context, e.g. for running a webserver on a non-standard port. ## Actions diff --git a/documentation/selinux_state.md b/documentation/selinux_state.md index 32710407..05be6c75 100644 --- a/documentation/selinux_state.md +++ b/documentation/selinux_state.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_state +[Back to resource list](../README.md#resources) + The `selinux_state` resource is used to manage 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: v4.0.0 @@ -13,7 +13,7 @@ Introduced: v4.0.0 | `:enforcing` | *(Default)* Set the SELinux state to enforcing | | `:permissive` | Set the state to permissive | | `:disabled` | Set the state to disabled | -` + > ⚠ Switching to or from `disabled` requires a reboot! ## Properties diff --git a/documentation/selinux_user.md b/documentation/selinux_user.md index 0310f628..48b84152 100644 --- a/documentation/selinux_user.md +++ b/documentation/selinux_user.md @@ -1,7 +1,7 @@ -[Back to resource list](../README.md#resources) - # selinux_user +[Back to resource list](../README.md#resources) + The `selinux_user` resource is used to manage SELinux users on the system. ## Actions diff --git a/kitchen.yml b/kitchen.yml index 691a1b2a..888e6649 100644 --- a/kitchen.yml +++ b/kitchen.yml @@ -21,18 +21,20 @@ verifier: platforms: - name: almalinux-8 + - name: almalinux-9 - name: amazonlinux-2 - - name: centos-7 - - name: centos-stream-8 - - name: debian-10 + - name: amazonlinux-2023 + - name: centos-stream-9 - name: debian-11 + - name: debian-12 - name: fedora-latest - - name: oracle-7 - name: oracle-8 + - name: oracle-9 - name: rockylinux-8 - - name: ubuntu-18.04 + - name: rockylinux-9 - name: ubuntu-20.04 - name: ubuntu-22.04 + - name: ubuntu-24.04 suites: - name: enforcing diff --git a/resources/fcontext.rb b/resources/fcontext.rb index 9efbbbb8..d8e2ca8a 100644 --- a/resources/fcontext.rb +++ b/resources/fcontext.rb @@ -93,7 +93,14 @@ def relabel_files return end - unless current_file_context + # "add" is performed in two scenarios. + # 1: The local file_contexts.local has an entry for new_resource.file_spec, but secontext <> new_resource.secontext + # 2. The local file_contexts.local does NOT have an entry for new_resource.file_spec, AND + # either the system default (file_contexts) does not have an entry for new_resource.file_spec, or the secontext <> new_resource.secontext + # In both scenarios, file_contexts.local is created with a new entry, or the secontext is updated. + + cfc = current_file_context + unless cfc && cfc == new_resource.secontext 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 diff --git a/spec/unit/resources/fcontext_spec.rb b/spec/unit/resources/fcontext_spec.rb index 27949c9a..0308f96b 100644 --- a/spec/unit/resources/fcontext_spec.rb +++ b/spec/unit/resources/fcontext_spec.rb @@ -17,9 +17,10 @@ context 'when not set' do stubs_for_provider('selinux_fcontext[/test]') do |provider| - allow(provider).to receive_shell_out('semanage fcontext -l', stdout: <<~EOF) + allow(provider).to receive_shell_out('semanage fcontext -l', stdout: <<~EOF /other/files all files user:role:type:level EOF + ) end # this is what actually checks that the fcontext was set correctly @@ -38,15 +39,18 @@ context 'when set to incorrect value' do stubs_for_provider('selinux_fcontext[/test]') do |provider| - allow(provider).to receive_shell_out('semanage fcontext -l', stdout: <<~EOF) - /test all files user:role:type:level + allow(provider).to receive_shell_out('semanage fcontext -l', stdout: <<~EOF + /test all files user:role:type:level' EOF + ) end # this is what actually checks that the fcontext was set correctly # incorrect commands would not be stubbed and would throw error stubs_for_provider('selinux_fcontext[/test]') do |provider| - # when set but incorrect, only modify calls (-m) and delete calls (-d) should happen + # when set but incorrect, the incorrect context is "deemed" as the built-in type, and + # attemp to perform the full cycle of activities + allow(provider).to receive_shell_out("semanage fcontext -a -f a -t foo '/test'") allow(provider).to receive_shell_out("semanage fcontext -m -f a -t foo '/test'") allow(provider).to receive_shell_out("semanage fcontext -d -f a '/test'") end @@ -60,9 +64,10 @@ context 'when set to correct value' do stubs_for_provider('selinux_fcontext[/test]') do |provider| - allow(provider).to receive_shell_out('semanage fcontext -l', stdout: <<~EOF) + allow(provider).to receive_shell_out('semanage fcontext -l', stdout: <<~EOF /test all files user:role:foo:level EOF + ) end # this is what actually checks that the fcontext was set correctly diff --git a/test/cookbooks/selinux_test/recipes/fcontext.rb b/test/cookbooks/selinux_test/recipes/fcontext.rb index e64f3394..8a50935e 100644 --- a/test/cookbooks/selinux_test/recipes/fcontext.rb +++ b/test/cookbooks/selinux_test/recipes/fcontext.rb @@ -21,3 +21,38 @@ secontext 'etc_t' file_type 'd' end + +# testing override of built-in context, using '/home/[^/]+/\.ssh(/.*)?' +# Use converge counter so we only do the fcontext manipulation in first round. Otherwise +# the "enforce_idempotency" will cause converge to fail. + +node.run_state['chef_converge_counter'] = shell_out('cat /tmp/chef_converge_counter').stdout.to_i +node.run_state['chef_converge_counter'] += 1 +file '/tmp/chef_converge_counter' do + content lazy { node.run_state['chef_converge_counter'].to_s } + mode '0644' + only_if { node.run_state['chef_converge_counter'] == 1 } +end + +execute 'Check built-in fcontext' do + command 'matchpathcon /home/user1/.ssh | grep ssh_home_t' + only_if { node.run_state['chef_converge_counter'] == 1 } +end + +# override with 'shadow_t' +selinux_fcontext '/home/[^/]+/\.ssh(/.*)?' do + secontext 'shadow_t' + action :add + only_if { node.run_state['chef_converge_counter'] == 1 } +end + +execute 'Check fcontext override' do + command 'matchpathcon /home/user1/.ssh | grep shadow_t' + only_if { node.run_state['chef_converge_counter'] == 1 } +end + +# remove the override +selinux_fcontext '/home/[^/]+/\.ssh(/.*)?' do + action :delete + only_if { node.run_state['chef_converge_counter'] == 1 } +end diff --git a/test/integration/fcontext/controls/fcontext_control.rb b/test/integration/fcontext/controls/fcontext_control.rb index 5dc50331..61f17750 100644 --- a/test/integration/fcontext/controls/fcontext_control.rb +++ b/test/integration/fcontext/controls/fcontext_control.rb @@ -19,3 +19,12 @@ its('selinux_label') { should match 'etc_t' } end end + +control 'fcontext override' do + title 'Verify that built-in SELinux file contexts override works correctly' + + describe command('matchpathcon /home/user1/.ssh') do + its('exit_status') { should eq 0 } + its('stdout') { should match /ssh_home_t/ } + end +end