summaryrefslogtreecommitdiff
path: root/lib/ansible/modules/extras/system/seport.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/modules/extras/system/seport.py')
-rw-r--r--lib/ansible/modules/extras/system/seport.py305
1 files changed, 305 insertions, 0 deletions
diff --git a/lib/ansible/modules/extras/system/seport.py b/lib/ansible/modules/extras/system/seport.py
new file mode 100644
index 0000000000..242661a143
--- /dev/null
+++ b/lib/ansible/modules/extras/system/seport.py
@@ -0,0 +1,305 @@
+#!/usr/bin/python
+
+# (c) 2014, Dan Keder <dan.keder@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+DOCUMENTATION = '''
+---
+module: seport
+short_description: Manages SELinux network port type definitions
+description:
+ - Manages SELinux network port type definitions.
+version_added: "2.0"
+options:
+ ports:
+ description:
+ - Ports or port ranges, separated by a comma
+ required: true
+ default: null
+ proto:
+ description:
+ - Protocol for the specified port.
+ required: true
+ default: null
+ choices: [ 'tcp', 'udp' ]
+ setype:
+ description:
+ - SELinux type for the specified port.
+ required: true
+ default: null
+ state:
+ description:
+ - Desired boolean value.
+ required: true
+ default: present
+ choices: [ 'present', 'absent' ]
+ reload:
+ description:
+ - Reload SELinux policy after commit.
+ required: false
+ default: yes
+notes:
+ - The changes are persistent across reboots
+ - Not tested on any debian based system
+requirements: [ 'libselinux-python', 'policycoreutils-python' ]
+author: Dan Keder
+'''
+
+EXAMPLES = '''
+# Allow Apache to listen on tcp port 8888
+- seport: ports=8888 proto=tcp setype=http_port_t state=present
+# Allow sshd to listen on tcp port 8991
+- seport: ports=8991 proto=tcp setype=ssh_port_t state=present
+# Allow memcached to listen on tcp ports 10000-10100 and 10112
+- seport: ports=10000-10100,10112 proto=tcp setype=memcache_port_t state=present
+'''
+
+try:
+ import selinux
+ HAVE_SELINUX=True
+except ImportError:
+ HAVE_SELINUX=False
+
+try:
+ import seobject
+ HAVE_SEOBJECT=True
+except ImportError:
+ HAVE_SEOBJECT=False
+
+from ansible.module_utils.basic import *
+from ansible.module_utils.pycompat24 import get_exception
+
+
+def semanage_port_get_ports(seport, setype, proto):
+ """ Get the list of ports that have the specified type definition.
+
+ :param seport: Instance of seobject.portRecords
+
+ :type setype: str
+ :param setype: SELinux type.
+
+ :type proto: str
+ :param proto: Protocol ('tcp' or 'udp')
+
+ :rtype: list
+ :return: List of ports that have the specified SELinux type.
+ """
+ records = seport.get_all_by_type()
+ if (setype, proto) in records:
+ return records[(setype, proto)]
+ else:
+ return []
+
+
+def semanage_port_get_type(seport, port, proto):
+ """ Get the SELinux type of the specified port.
+
+ :param seport: Instance of seobject.portRecords
+
+ :type port: str
+ :param port: Port or port range (example: "8080", "8080-9090")
+
+ :type proto: str
+ :param proto: Protocol ('tcp' or 'udp')
+
+ :rtype: tuple
+ :return: Tuple containing the SELinux type and MLS/MCS level, or None if not found.
+ """
+ ports = port.split('-', 1)
+ if len(ports) == 1:
+ ports.extend(ports)
+ key = (int(ports[0]), int(ports[1]), proto)
+
+ records = seport.get_all()
+ if key in records:
+ return records[key]
+ else:
+ return None
+
+
+def semanage_port_add(module, ports, proto, setype, do_reload, serange='s0', sestore=''):
+ """ Add SELinux port type definition to the policy.
+
+ :type module: AnsibleModule
+ :param module: Ansible module
+
+ :type ports: list
+ :param ports: List of ports and port ranges to add (e.g. ["8080", "8080-9090"])
+
+ :type proto: str
+ :param proto: Protocol ('tcp' or 'udp')
+
+ :type setype: str
+ :param setype: SELinux type
+
+ :type do_reload: bool
+ :param do_reload: Whether to reload SELinux policy after commit
+
+ :type serange: str
+ :param serange: SELinux MLS/MCS range (defaults to 's0')
+
+ :type sestore: str
+ :param sestore: SELinux store
+
+ :rtype: bool
+ :return: True if the policy was changed, otherwise False
+ """
+ try:
+ seport = seobject.portRecords(sestore)
+ seport.set_reload(do_reload)
+ change = False
+ ports_by_type = semanage_port_get_ports(seport, setype, proto)
+ for port in ports:
+ if port not in ports_by_type:
+ change = True
+ port_type = semanage_port_get_type(seport, port, proto)
+ if port_type is None and not module.check_mode:
+ seport.add(port, proto, serange, setype)
+ elif port_type is not None and not module.check_mode:
+ seport.modify(port, proto, serange, setype)
+
+ except ValueError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+ except IOError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+ except KeyError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+ except OSError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+ except RuntimeError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+
+ return change
+
+
+def semanage_port_del(module, ports, proto, setype, do_reload, sestore=''):
+ """ Delete SELinux port type definition from the policy.
+
+ :type module: AnsibleModule
+ :param module: Ansible module
+
+ :type ports: list
+ :param ports: List of ports and port ranges to delete (e.g. ["8080", "8080-9090"])
+
+ :type proto: str
+ :param proto: Protocol ('tcp' or 'udp')
+
+ :type setype: str
+ :param setype: SELinux type.
+
+ :type do_reload: bool
+ :param do_reload: Whether to reload SELinux policy after commit
+
+ :type sestore: str
+ :param sestore: SELinux store
+
+ :rtype: bool
+ :return: True if the policy was changed, otherwise False
+ """
+ try:
+ seport = seobject.portRecords(sestore)
+ seport.set_reload(do_reload)
+ change = False
+ ports_by_type = semanage_port_get_ports(seport, setype, proto)
+ for port in ports:
+ if port in ports_by_type:
+ change = True
+ if not module.check_mode:
+ seport.delete(port, proto)
+
+ except ValueError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+ except IOError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+ except KeyError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+ except OSError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+ except RuntimeError:
+ e = get_exception()
+ module.fail_json(msg="%s: %s\n" % (e.__class__.__name__, str(e)))
+
+ return change
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec={
+ 'ports': {
+ 'required': True,
+ },
+ 'proto': {
+ 'required': True,
+ 'choices': ['tcp', 'udp'],
+ },
+ 'setype': {
+ 'required': True,
+ },
+ 'state': {
+ 'required': True,
+ 'choices': ['present', 'absent'],
+ },
+ 'reload': {
+ 'required': False,
+ 'type': 'bool',
+ 'default': 'yes',
+ },
+ },
+ 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.")
+
+ ports = [x.strip() for x in str(module.params['ports']).split(',')]
+ proto = module.params['proto']
+ setype = module.params['setype']
+ state = module.params['state']
+ do_reload = module.params['reload']
+
+ result = {
+ 'ports': ports,
+ 'proto': proto,
+ 'setype': setype,
+ 'state': state,
+ }
+
+ if state == 'present':
+ result['changed'] = semanage_port_add(module, ports, proto, setype, do_reload)
+ elif state == 'absent':
+ result['changed'] = semanage_port_del(module, ports, proto, setype, do_reload)
+ else:
+ module.fail_json(msg='Invalid value of argument "state": {0}'.format(state))
+
+ module.exit_json(**result)
+
+
+main()