Skip to content

Commit

Permalink
selinux: update kernel boot params when disabling/re-enabling SELinux
Browse files Browse the repository at this point in the history
The ability to disable SELinux from userspace based on the configuration
file is being deprecated in favor of the selinux=0 kernel boot
parameter. (Note that this affects only the "full" disable; switching
to/from permissive mode will work the same as before.)

Therefore, enhance the selinux module to try to set/unset the kernel
command-line parameter using grubby when enabling/disabling SELinux.

If the grubby package is not present on the system, the module will only
update the config file and report a warning. Note that even with the
runtime disable functionality removed, setting SELINUX=disabled in the
config file will lead to a system with no SELinux policy loaded, which
will behave in a very similar way as if SELinux was fully disabled, only
there could still be some minor performance impact, since the kernel
hooks will still be active.

More information:
https://lore.kernel.org/selinux/157836784986.560897.13893922675143903084.stgit@chester/
https://fedoraproject.org/wiki/Changes/Remove_Support_For_SELinux_Runtime_Disable

Signed-off-by: Ondrej Mosnacek <[email protected]>
  • Loading branch information
WOnder93 committed May 13, 2021
1 parent 1f15216 commit dab5587
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
50 changes: 50 additions & 0 deletions plugins/modules/selinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,30 @@ def get_config_policy(configfile):
return line.split('=')[1].strip()


def get_kernel_enabled(module):
rc, stdout, stderr = module.run_command(['grubby', '--info=ALL'])
if rc != 0:
module.warn("'grubby' command failed - kernel boot configuration won't be updated")
return None

all_enabled = True
all_disabled = True
for line in stdout.split('\n'):
match = re.fullmatch('args="(.*)"', line)
if match is None:
continue
args = match.group(1).split(' ')
if 'selinux=0' in args:
all_enabled = False
else:
all_disabled = False
if all_disabled == all_enabled:
module.warn("Kernel SELinux configuration inconsistent - "
"kernel boot configuration won't be updated")
return None
return all_enabled


# setter subroutines
def set_config_state(module, state, configfile):
# SELINUX=permissive
Expand Down Expand Up @@ -153,6 +177,17 @@ def set_state(module, state):
module.fail_json(msg=msg)


def set_kernel_enabled(module, value):
rc, stdout, stderr = module.run_command(['grubby', '--update-kernel=ALL',
'--remove-args' if value else '--args',
'selinux=0'])
if rc != 0:
if value:
module.fail_json(msg='unable to remove selinux=0 from kernel config')
else:
module.fail_json(msg='unable to add selinux=0 to kernel config')


def set_config_policy(module, policy, configfile):
if not os.path.exists('/etc/selinux/%s/policy' % policy):
module.fail_json(msg='Policy %s does not exist in /etc/selinux/' % policy)
Expand Down Expand Up @@ -199,6 +234,7 @@ def main():
runtime_enabled = selinux.is_selinux_enabled()
runtime_policy = selinux.selinux_getpolicytype()[1]
runtime_state = 'disabled'
kernel_enabled = None
reboot_required = False

if runtime_enabled:
Expand All @@ -215,6 +251,7 @@ def main():

config_policy = get_config_policy(configfile)
config_state = get_config_state(configfile)
kernel_enabled = get_kernel_enabled(module)

# check to see if policy is set if state is not 'disabled'
if state != 'disabled':
Expand Down Expand Up @@ -269,6 +306,19 @@ def main():
msgs.append("Config SELinux state changed from '%s' to '%s'" % (config_state, state))
changed = True

requested_kernel_enabled = state in ('enforcing', 'permissive')
# Update kernel enabled/disabled config only when setting is consistent
# across all kernels AND the requested state differs from the current state
if kernel_enabled is not None and kernel_enabled != requested_kernel_enabled:
if not module.check_mode:
set_kernel_enabled(module, requested_kernel_enabled)
if requested_kernel_enabled:
states = ('disabled', 'enabled')
else:
states = ('enabled', 'disabled')
msgs.append("Kernel SELinux state changed from '%s' to '%s'" % states)
changed = True

module.exit_json(changed=changed, msg=', '.join(msgs), configfile=configfile, policy=policy, state=state, reboot_required=reboot_required)


Expand Down
23 changes: 23 additions & 0 deletions tests/integration/targets/selinux/tasks/selinux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
# ##############################################################################
# Test changing the state, which requires a reboot

- name: TEST 1 | Make sure grubby is present
package:
name: grubby
state: present

- name: TEST 1 | Get current SELinux config file contents
set_fact:
selinux_config_original: "{{ lookup('file', '/etc/sysconfig/selinux').split('\n') }}"
Expand Down Expand Up @@ -72,6 +77,15 @@
var: ansible_selinux
verbosity: 1

- name: Check kernel command-line arguments
ansible.builtin.command: grubby --info=DEFAULT
register: _grubby_test1

- name: TEST 1 | Assert that kernel cmdline contains selinux=0
assert:
that:
- "' selinux=0' in _grubby_test1.stdout"

- name: TEST 1 | Disable SELinux again
selinux:
state: disabled
Expand Down Expand Up @@ -109,6 +123,15 @@
state: enforcing
policy: targeted

- name: Check kernel command-line arguments
ansible.builtin.command: grubby --info=DEFAULT
register: _grubby_test2

- name: TEST 1 | Assert that kernel cmdline doesn't contain selinux=0
assert:
that:
- "' selinux=0' not in _grubby_test2.stdout"


# Second Test
# ##############################################################################
Expand Down

0 comments on commit dab5587

Please sign in to comment.