mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-25 11:51:26 -07:00
Previously, when the active SELinux value was the same as the desired value, regardless of the value of `persistent`, the module would simply exit successfully, assuming no need for update. This made the assumption that the active and persistent values should be the same, but that is not always the case. This modification treats both the active and persistent values separately when checking for the need to update. Note that the persistent update mode, however, will still update the active as well as the persistent value. If this is not desired, it is possible to separately toggle the active value alone. Other changes: * Make the check mode actually perform checks for changes * Organizes semanage commands into set of logical steps
319 lines
9.9 KiB
Python
319 lines
9.9 KiB
Python
#!/usr/bin/python
|
|
|
|
# Copyright: (c) 2012, Stephen Fromm <sfromm@gmail.com>
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
'status': ['stableinterface'],
|
|
'supported_by': 'core'}
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: seboolean
|
|
short_description: Toggles SELinux booleans
|
|
description:
|
|
- Toggles SELinux booleans.
|
|
version_added: "0.7"
|
|
options:
|
|
name:
|
|
description:
|
|
- Name of the boolean to configure.
|
|
required: true
|
|
persistent:
|
|
description:
|
|
- Set to C(yes) if the boolean setting should survive a reboot.
|
|
type: bool
|
|
default: 'no'
|
|
state:
|
|
description:
|
|
- Desired boolean value
|
|
type: bool
|
|
required: true
|
|
notes:
|
|
- Not tested on any Debian based system.
|
|
requirements:
|
|
- libselinux-python
|
|
- libsemanage-python
|
|
author:
|
|
- Stephen Fromm (@sfromm)
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
- name: Set httpd_can_network_connect flag on and keep it persistent across reboots
|
|
seboolean:
|
|
name: httpd_can_network_connect
|
|
state: yes
|
|
persistent: yes
|
|
'''
|
|
|
|
import os
|
|
|
|
try:
|
|
import selinux
|
|
HAVE_SELINUX = True
|
|
except ImportError:
|
|
HAVE_SELINUX = False
|
|
|
|
try:
|
|
import semanage
|
|
HAVE_SEMANAGE = True
|
|
except ImportError:
|
|
HAVE_SEMANAGE = False
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils.six import binary_type
|
|
from ansible.module_utils._text import to_bytes
|
|
|
|
|
|
def has_boolean_value(module, name):
|
|
bools = []
|
|
try:
|
|
rc, bools = selinux.security_get_boolean_names()
|
|
except OSError:
|
|
module.fail_json(msg="Failed to get list of boolean names")
|
|
# work around for selinux who changed its API, see
|
|
# https://github.com/ansible/ansible/issues/25651
|
|
if len(bools) > 0:
|
|
if isinstance(bools[0], binary_type):
|
|
name = to_bytes(name)
|
|
if name in bools:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def get_boolean_value(module, name):
|
|
state = 0
|
|
try:
|
|
state = selinux.security_get_boolean_active(name)
|
|
except OSError:
|
|
module.fail_json(msg="Failed to determine current state for boolean %s" % name)
|
|
if state == 1:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def semanage_get_handle(module):
|
|
handle = semanage.semanage_handle_create()
|
|
if not handle:
|
|
module.fail_json(msg="Failed to create semanage library handle")
|
|
|
|
managed = semanage.semanage_is_managed(handle)
|
|
if managed <= 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
if managed < 0:
|
|
module.fail_json(msg="Failed to determine whether policy is manage")
|
|
if managed == 0:
|
|
if os.getuid() == 0:
|
|
module.fail_json(msg="Cannot set persistent booleans without managed policy")
|
|
else:
|
|
module.fail_json(msg="Cannot set persistent booleans; please try as root")
|
|
|
|
if semanage.semanage_connect(handle) < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to connect to semanage")
|
|
|
|
return handle
|
|
|
|
|
|
def semanage_begin_transaction(module, handle):
|
|
if semanage.semanage_begin_transaction(handle) < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to begin semanage transaction")
|
|
|
|
|
|
def semanage_set_boolean_value(module, handle, name, value):
|
|
rc, t_b = semanage.semanage_bool_create(handle)
|
|
if rc < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to create seboolean with semanage")
|
|
|
|
if semanage.semanage_bool_set_name(handle, t_b, name) < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to set seboolean name with semanage")
|
|
|
|
rc, boolkey = semanage.semanage_bool_key_extract(handle, t_b)
|
|
if rc < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to extract boolean key with semanage")
|
|
|
|
rc, exists = semanage.semanage_bool_exists(handle, boolkey)
|
|
if rc < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to check if boolean is defined")
|
|
if not exists:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="SELinux boolean %s is not defined in persistent policy" % name)
|
|
|
|
rc, sebool = semanage.semanage_bool_query(handle, boolkey)
|
|
if rc < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to query boolean in persistent policy")
|
|
|
|
semanage.semanage_bool_set_value(sebool, value)
|
|
|
|
if semanage.semanage_bool_modify_local(handle, boolkey, sebool) < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to modify boolean key with semanage")
|
|
|
|
if semanage.semanage_bool_set_active(handle, boolkey, sebool) < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to set boolean key active with semanage")
|
|
|
|
semanage.semanage_bool_key_free(boolkey)
|
|
semanage.semanage_bool_free(t_b)
|
|
semanage.semanage_bool_free(sebool)
|
|
|
|
|
|
def semanage_get_boolean_value(module, handle, name):
|
|
rc, t_b = semanage.semanage_bool_create(handle)
|
|
if rc < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to create seboolean with semanage")
|
|
|
|
if semanage.semanage_bool_set_name(handle, t_b, name) < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to set seboolean name with semanage")
|
|
|
|
rc, boolkey = semanage.semanage_bool_key_extract(handle, t_b)
|
|
if rc < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to extract boolean key with semanage")
|
|
|
|
rc, exists = semanage.semanage_bool_exists(handle, boolkey)
|
|
if rc < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to check if boolean is defined")
|
|
if not exists:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="SELinux boolean %s is not defined in persistent policy" % name)
|
|
|
|
rc, sebool = semanage.semanage_bool_query(handle, boolkey)
|
|
if rc < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to query boolean in persistent policy")
|
|
|
|
value = semanage.semanage_bool_get_value(sebool)
|
|
|
|
semanage.semanage_bool_key_free(boolkey)
|
|
semanage.semanage_bool_free(t_b)
|
|
semanage.semanage_bool_free(sebool)
|
|
|
|
return value
|
|
|
|
|
|
def semanage_commit(module, handle, load=0):
|
|
semanage.semanage_set_reload(handle, load)
|
|
if semanage.semanage_commit(handle) < 0:
|
|
semanage.semanage_handle_destroy(handle)
|
|
module.fail_json(msg="Failed to commit changes to semanage")
|
|
|
|
|
|
def semanage_destroy_handle(module, handle):
|
|
rc = semanage.semanage_disconnect(handle)
|
|
semanage.semanage_handle_destroy(handle)
|
|
if rc < 0:
|
|
module.fail_json(msg="Failed to disconnect from semanage")
|
|
|
|
|
|
# The following method implements what setsebool.c does to change
|
|
# a boolean and make it persist after reboot..
|
|
def semanage_boolean_value(module, name, state):
|
|
value = 0
|
|
changed = False
|
|
if state:
|
|
value = 1
|
|
try:
|
|
handle = semanage_get_handle(module)
|
|
semanage_begin_transaction(module, handle)
|
|
cur_value = semanage_get_boolean_value(module, handle, name)
|
|
if cur_value != value:
|
|
changed = True
|
|
if not module.check_mode:
|
|
semanage_set_boolean_value(module, handle, name, value)
|
|
semanage_commit(module, handle)
|
|
semanage_destroy_handle(module, handle)
|
|
except Exception as e:
|
|
module.fail_json(msg="Failed to manage policy for boolean %s: %s" % (name, str(e)))
|
|
return changed
|
|
|
|
|
|
def set_boolean_value(module, name, state):
|
|
rc = 0
|
|
value = 0
|
|
if state:
|
|
value = 1
|
|
try:
|
|
rc = selinux.security_set_boolean(name, value)
|
|
except OSError:
|
|
module.fail_json(msg="Failed to set boolean %s to %s" % (name, value))
|
|
if rc == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
name=dict(type='str', required=True),
|
|
persistent=dict(type='bool', default=False),
|
|
state=dict(type='bool', required=True),
|
|
),
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
if not HAVE_SELINUX:
|
|
module.fail_json(msg="This module requires libselinux-python support")
|
|
|
|
if not HAVE_SEMANAGE:
|
|
module.fail_json(msg="This module requires libsemanage-python support")
|
|
|
|
if not selinux.is_selinux_enabled():
|
|
module.fail_json(msg="SELinux is disabled on this host.")
|
|
|
|
name = module.params['name']
|
|
persistent = module.params['persistent']
|
|
state = module.params['state']
|
|
|
|
result = dict(
|
|
name=name,
|
|
persistent=persistent,
|
|
state=state
|
|
)
|
|
changed = False
|
|
|
|
if hasattr(selinux, 'selinux_boolean_sub'):
|
|
# selinux_boolean_sub allows sites to rename a boolean and alias the old name
|
|
# Feature only available in selinux library since 2012.
|
|
name = selinux.selinux_boolean_sub(name)
|
|
|
|
if not has_boolean_value(module, name):
|
|
module.fail_json(msg="SELinux boolean %s does not exist." % name)
|
|
|
|
if persistent:
|
|
changed = semanage_boolean_value(module, name, state)
|
|
else:
|
|
cur_value = get_boolean_value(module, name)
|
|
if cur_value != state:
|
|
changed = True
|
|
if not module.check_mode:
|
|
changed = set_boolean_value(module, name, state)
|
|
if not changed:
|
|
module.fail_json(msg="Failed to set boolean %s to %s" % (name, state))
|
|
try:
|
|
selinux.security_commit_booleans()
|
|
except:
|
|
module.fail_json(msg="Failed to commit pending boolean %s value" % name)
|
|
|
|
result['changed'] = changed
|
|
|
|
module.exit_json(**result)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|