mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-02 00:33:09 +00:00
* Add note about sefcontext doing no restorecon To someone like me who is relatively new to SELinux, setting the "reload" option to yes might suggest that a restorecon is automatically executed after the semanage call, making the new file context effective immediately. I have found out that this is not the case and would like to clarify this to others. +label: docsite_pr * Replace note by one suggested by reviewer Reviewer dagwieers suggested a better notice text during review of my original one, giving recommendations about what to do to actually get the newly chosen SELinux context applied to the file.
260 lines
8.0 KiB
Python
260 lines
8.0 KiB
Python
#!/usr/bin/python
|
|
|
|
# (c) 2016, Dag Wieers <dag@wieers.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': ['preview'],
|
|
'supported_by': 'community'}
|
|
|
|
|
|
DOCUMENTATION = r'''
|
|
---
|
|
module: sefcontext
|
|
short_description: Manages SELinux file context mapping definitions
|
|
description:
|
|
- Manages SELinux file context mapping definitions.
|
|
- Similar to the C(semanage fcontext) command.
|
|
version_added: '2.2'
|
|
options:
|
|
target:
|
|
description:
|
|
- Target path (expression).
|
|
required: yes
|
|
aliases: [ path ]
|
|
ftype:
|
|
description:
|
|
- File type.
|
|
default: a
|
|
setype:
|
|
description:
|
|
- SELinux type for the specified target.
|
|
required: yes
|
|
seuser:
|
|
description:
|
|
- SELinux user for the specified target.
|
|
selevel:
|
|
description:
|
|
- SELinux range for the specified target.
|
|
aliases: [ serange ]
|
|
state:
|
|
description:
|
|
- Desired boolean value.
|
|
choices: [ absent, present ]
|
|
default: present
|
|
reload:
|
|
description:
|
|
- Reload SELinux policy after commit.
|
|
type: bool
|
|
default: 'yes'
|
|
notes:
|
|
- The changes are persistent across reboots
|
|
- The M(sefcontext) module does not modify existing files to the new
|
|
SELinux context(s), so it is advisable to first create the SELinux
|
|
file contexts before creating files, or run C(restorecon) manually
|
|
for the existing files that require the new SELinux file contexts.
|
|
requirements:
|
|
- libselinux-python
|
|
- policycoreutils-python
|
|
author:
|
|
- Dag Wieers (@dagwieers)
|
|
'''
|
|
|
|
EXAMPLES = r'''
|
|
# Allow apache to modify files in /srv/git_repos
|
|
- sefcontext:
|
|
target: '/srv/git_repos(/.*)?'
|
|
setype: httpd_git_rw_content_t
|
|
state: present
|
|
'''
|
|
|
|
RETURN = r'''
|
|
# Default return values
|
|
'''
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils.pycompat24 import get_exception
|
|
from ansible.module_utils._text import to_native
|
|
|
|
try:
|
|
import selinux
|
|
HAVE_SELINUX = True
|
|
except ImportError:
|
|
HAVE_SELINUX = False
|
|
|
|
try:
|
|
import seobject
|
|
HAVE_SEOBJECT = True
|
|
except ImportError:
|
|
HAVE_SEOBJECT = False
|
|
|
|
# Add missing entries (backward compatible)
|
|
if HAVE_SEOBJECT:
|
|
seobject.file_types.update(dict(
|
|
a=seobject.SEMANAGE_FCONTEXT_ALL,
|
|
b=seobject.SEMANAGE_FCONTEXT_BLOCK,
|
|
c=seobject.SEMANAGE_FCONTEXT_CHAR,
|
|
d=seobject.SEMANAGE_FCONTEXT_DIR,
|
|
f=seobject.SEMANAGE_FCONTEXT_REG,
|
|
l=seobject.SEMANAGE_FCONTEXT_LINK,
|
|
p=seobject.SEMANAGE_FCONTEXT_PIPE,
|
|
s=seobject.SEMANAGE_FCONTEXT_SOCK,
|
|
))
|
|
|
|
# Make backward compatible
|
|
option_to_file_type_str = dict(
|
|
a='all files',
|
|
b='block device',
|
|
c='character device',
|
|
d='directory',
|
|
f='regular file',
|
|
l='symbolic link',
|
|
p='named pipe',
|
|
s='socket file',
|
|
)
|
|
|
|
|
|
def semanage_fcontext_exists(sefcontext, target, ftype):
|
|
''' Get the SELinux file context mapping definition from policy. Return None if it does not exist. '''
|
|
|
|
# Beware that records comprise of a string representation of the file_type
|
|
record = (target, option_to_file_type_str[ftype])
|
|
records = sefcontext.get_all()
|
|
try:
|
|
return records[record]
|
|
except KeyError:
|
|
return None
|
|
|
|
|
|
def semanage_fcontext_modify(module, result, target, ftype, setype, do_reload, serange, seuser, sestore=''):
|
|
''' Add or modify SELinux file context mapping definition to the policy. '''
|
|
|
|
changed = False
|
|
prepared_diff = ''
|
|
|
|
try:
|
|
sefcontext = seobject.fcontextRecords(sestore)
|
|
sefcontext.set_reload(do_reload)
|
|
exists = semanage_fcontext_exists(sefcontext, target, ftype)
|
|
if exists:
|
|
# Modify existing entry
|
|
orig_seuser, orig_serole, orig_setype, orig_serange = exists
|
|
|
|
if seuser is None:
|
|
seuser = orig_seuser
|
|
if serange is None:
|
|
serange = orig_serange
|
|
|
|
if setype != orig_setype or seuser != orig_seuser or serange != orig_serange:
|
|
if not module.check_mode:
|
|
sefcontext.modify(target, setype, ftype, serange, seuser)
|
|
changed = True
|
|
|
|
if module._diff:
|
|
prepared_diff += '# Change to semanage file context mappings\n'
|
|
prepared_diff += '-%s %s %s:%s:%s:%s\n' % (target, ftype, orig_seuser, orig_serole, orig_setype, orig_serange)
|
|
prepared_diff += '+%s %s %s:%s:%s:%s\n' % (target, ftype, seuser, orig_serole, setype, serange)
|
|
else:
|
|
# Add missing entry
|
|
if seuser is None:
|
|
seuser = 'system_u'
|
|
if serange is None:
|
|
serange = 's0'
|
|
|
|
if not module.check_mode:
|
|
sefcontext.add(target, setype, ftype, serange, seuser)
|
|
changed = True
|
|
|
|
if module._diff:
|
|
prepared_diff += '# Addition to semanage file context mappings\n'
|
|
prepared_diff += '+%s %s %s:%s:%s:%s\n' % (target, ftype, seuser, 'object_r', setype, serange)
|
|
|
|
except Exception:
|
|
e = get_exception()
|
|
module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, to_native(e)))
|
|
|
|
if module._diff and prepared_diff:
|
|
result['diff'] = dict(prepared=prepared_diff)
|
|
|
|
module.exit_json(changed=changed, seuser=seuser, serange=serange, **result)
|
|
|
|
|
|
def semanage_fcontext_delete(module, result, target, ftype, do_reload, sestore=''):
|
|
''' Delete SELinux file context mapping definition from the policy. '''
|
|
|
|
changed = False
|
|
prepared_diff = ''
|
|
|
|
try:
|
|
sefcontext = seobject.fcontextRecords(sestore)
|
|
sefcontext.set_reload(do_reload)
|
|
exists = semanage_fcontext_exists(sefcontext, target, ftype)
|
|
if exists:
|
|
# Remove existing entry
|
|
orig_seuser, orig_serole, orig_setype, orig_serange = exists
|
|
|
|
if not module.check_mode:
|
|
sefcontext.delete(target, ftype)
|
|
changed = True
|
|
|
|
if module._diff:
|
|
prepared_diff += '# Deletion to semanage file context mappings\n'
|
|
prepared_diff += '-%s %s %s:%s:%s:%s\n' % (target, ftype, exists[0], exists[1], exists[2], exists[3])
|
|
|
|
except Exception:
|
|
e = get_exception()
|
|
module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, to_native(e)))
|
|
|
|
if module._diff and prepared_diff:
|
|
result['diff'] = dict(prepared=prepared_diff)
|
|
|
|
module.exit_json(changed=changed, **result)
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
target=dict(required=True, aliases=['path']),
|
|
ftype=dict(type='str', default='a', choices=option_to_file_type_str.keys()),
|
|
setype=dict(type='str', required=True),
|
|
seuser=dict(type='str'),
|
|
selevel=dict(type='str', aliases=['serange']),
|
|
state=dict(type='str', default='present', choices=['absent', 'present']),
|
|
reload=dict(type='bool', default=True),
|
|
),
|
|
supports_check_mode=True,
|
|
)
|
|
if not HAVE_SELINUX:
|
|
module.fail_json(msg="This module requires libselinux-python")
|
|
|
|
if not HAVE_SEOBJECT:
|
|
module.fail_json(msg="This module requires policycoreutils-python")
|
|
|
|
if not selinux.is_selinux_enabled():
|
|
module.fail_json(msg="SELinux is disabled on this host.")
|
|
|
|
target = module.params['target']
|
|
ftype = module.params['ftype']
|
|
setype = module.params['setype']
|
|
seuser = module.params['seuser']
|
|
serange = module.params['selevel']
|
|
state = module.params['state']
|
|
do_reload = module.params['reload']
|
|
|
|
result = dict(target=target, ftype=ftype, setype=setype, state=state)
|
|
|
|
if state == 'present':
|
|
semanage_fcontext_modify(module, result, target, ftype, setype, do_reload, serange, seuser)
|
|
elif state == 'absent':
|
|
semanage_fcontext_delete(module, result, target, ftype, do_reload)
|
|
else:
|
|
module.fail_json(msg='Invalid value of argument "state": {0}'.format(state))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|