fix: Enable port modification in state selinux.port_policy_present

This commit is contained in:
Jason Woods 2020-02-14 14:11:26 +00:00 committed by Daniel Wozniak
parent b990868ca7
commit 0512a8a52a
3 changed files with 391 additions and 19 deletions

View file

@ -10,7 +10,6 @@ Execute calls on selinux
proper packages are installed.
"""
import os
import re
@ -807,12 +806,47 @@ def port_add_policy(name, sel_type=None, protocol=None, port=None, sel_range=Non
.. code-block:: bash
salt '*' selinux.port_add_policy add tcp/8080 http_port_t
salt '*' selinux.port_add_policy add foobar http_port_t protocol=tcp port=8091
salt '*' selinux.port_add_policy tcp/8080 http_port_t
salt '*' selinux.port_add_policy foobar http_port_t protocol=tcp port=8091
"""
return _port_add_or_delete_policy("add", name, sel_type, protocol, port, sel_range)
def port_modify_policy(name, sel_type=None, protocol=None, port=None, sel_range=None):
"""
.. versionadded:: 2019.2.0
Modifies the SELinux policy for a given protocol and port.
Returns the result of the call to semanage.
name
The protocol and port spec. Can be formatted as ``(tcp|udp)/(port|port-range)``.
sel_type
The SELinux Type. Required.
protocol
The protocol for the port, ``tcp`` or ``udp``. Required if name is not formatted.
port
The port or port range. Required if name is not formatted.
sel_range
The SELinux MLS/MCS Security Range.
CLI Example:
.. code-block:: bash
salt '*' selinux.port_modify_policy tcp/8080 http_port_t
salt '*' selinux.port_modify_policy foobar http_port_t protocol=tcp port=8091
"""
return _port_add_or_delete_policy(
"modify", name, sel_type, protocol, port, sel_range
)
def port_delete_policy(name, protocol=None, port=None):
"""
.. versionadded:: 2019.2.0
@ -846,13 +880,13 @@ def _port_add_or_delete_policy(
"""
.. versionadded:: 2019.2.0
Performs the action as called from ``port_add_policy`` or ``port_delete_policy``.
Performs the action as called from ``port_add_policy``, ``port_modify_policy`` or ``port_delete_policy``.
Returns the result of the call to semanage.
"""
if action not in ["add", "delete"]:
if action not in ["add", "modify", "delete"]:
raise SaltInvocationError(
f'Actions supported are "add" and "delete", not "{action}".'
f'Actions supported are "add", "modify" and "delete", not "{action}".'
)
if action == "add" and not sel_type:
raise SaltInvocationError("SELinux Type is required to add a policy")

View file

@ -464,10 +464,8 @@ def fcontext_policy_applied(name, recursive=False):
ret.update(
{
"result": True,
"comment": (
'SElinux policies are already applied for filespec "{}"'.format(
name
)
"comment": 'SElinux policies are already applied for filespec "{}"'.format(
name
),
}
)
@ -517,24 +515,31 @@ def port_policy_present(name, sel_type, protocol=None, port=None, sel_range=None
{
"result": True,
"comment": f'SELinux policy for "{name}" already present '
+ 'with specified sel_type "{}", protocol "{}" and port "{}".'.format(
sel_type, protocol, port
),
+ f'with specified sel_type "{sel_type}", protocol "{protocol}" and port "{port}".',
}
)
return ret
if __opts__["test"]:
ret.update({"result": None})
else:
add_ret = __salt__["selinux.port_add_policy"](
old_state = __salt__["selinux.port_get_policy"](
name=name,
protocol=protocol,
port=port,
)
if old_state:
module_method = "selinux.port_modify_policy"
else:
module_method = "selinux.port_add_policy"
add_modify_ret = __salt__[module_method](
name=name,
sel_type=sel_type,
protocol=protocol,
port=port,
sel_range=sel_range,
)
if add_ret["retcode"] != 0:
ret.update({"comment": f"Error adding new policy: {add_ret}"})
if add_modify_ret["retcode"] != 0:
ret.update({"comment": f"Error adding new policy: {add_modify_ret}"})
else:
ret.update({"result": True})
new_state = __salt__["selinux.port_get_policy"](
@ -578,9 +583,7 @@ def port_policy_absent(name, sel_type=None, protocol=None, port=None):
{
"result": True,
"comment": f'SELinux policy for "{name}" already absent '
+ 'with specified sel_type "{}", protocol "{}" and port "{}".'.format(
sel_type, protocol, port
),
+ f'with specified sel_type "{sel_type}", protocol "{protocol}" and port "{port}".',
}
)
return ret

View file

@ -1,5 +1,6 @@
"""
:codeauthor: Jayesh Kariya <jayeshk@saltstack.com>
:codeauthor: Jason Woods <devel@jasonwoods.me.uk>
"""
import pytest
@ -118,3 +119,337 @@ def test_boolean():
ret.update({"comment": comt, "result": False})
ret.update({"changes": {}})
assert selinux.boolean(name, value) == ret
def test_port_policy_present():
"""
Test to set up an SELinux port.
"""
name = "tcp/8080"
protocol = "tcp"
port = "8080"
ret = {"name": name, "changes": {}, "result": False, "comment": ""}
# Test when already present with same sel_type
mock_add = MagicMock(return_value={"retcode": 0})
mock_modify = MagicMock(return_value={"retcode": 0})
mock_get = MagicMock(
return_value={
"sel_type": "http_cache_port_t",
"protocol": "tcp",
"port": "8080",
}
)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_add_policy": mock_add,
"selinux.port_modify_policy": mock_modify,
},
):
with patch.dict(selinux.__opts__, {"test": False}):
comt = (
f'SELinux policy for "{name}" already present '
+ f'with specified sel_type "http_cache_port_t", protocol "None" '
+ f'and port "None".'
)
ret.update({"comment": comt, "result": True})
assert selinux.port_policy_present(name, "http_cache_port_t") == ret
comt = (
f'SELinux policy for "name" already present '
+ f'with specified sel_type "http_cache_port_t", protocol "{protocol}" '
+ f'and port "{port}".'
)
ret.update({"comment": comt, "changes": {}, "result": True, "name": "name"})
assert (
selinux.port_policy_present("name", "http_cache_port_t", protocol, port)
== ret
)
ret.update({"name": name})
# Test adding new port policy
mock_add = MagicMock(return_value={"retcode": 0})
mock_modify = MagicMock(return_value={"retcode": 0})
mock_get = MagicMock(
side_effect=[
None,
None,
None,
{"sel_type": "http_cache_port_t", "protocol": "tcp", "port": "8080"},
]
)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_add_policy": mock_add,
"selinux.port_modify_policy": mock_modify,
},
):
with patch.dict(selinux.__opts__, {"test": True}):
ret.update({"comment": "", "result": None})
assert selinux.port_policy_present(name, "http_cache_port_t") == ret
with patch.dict(selinux.__opts__, {"test": False}):
ret.update(
{
"comment": "",
"changes": {
"old": None,
"new": {
"sel_type": "http_cache_port_t",
"protocol": "tcp",
"port": "8080",
},
},
"result": True,
}
)
assert selinux.port_policy_present(name, "http_cache_port_t") == ret
# Test modifying policy to a new sel_type
mock_add = MagicMock(return_value={"retcode": 0})
mock_modify = MagicMock(return_value={"retcode": 0})
mock_get = MagicMock(
side_effect=[
None,
None,
{"sel_type": "http_cache_port_t", "protocol": "tcp", "port": "8080"},
{"sel_type": "http_port_t", "protocol": "tcp", "port": "8080"},
]
)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_add_policy": mock_add,
"selinux.port_modify_policy": mock_modify,
},
):
with patch.dict(selinux.__opts__, {"test": True}):
ret.update({"comment": "", "changes": {}, "result": None})
assert selinux.port_policy_present(name, "http_port_t") == ret
with patch.dict(selinux.__opts__, {"test": False}):
ret.update(
{
"comment": "",
"changes": {
"old": {
"sel_type": "http_cache_port_t",
"protocol": "tcp",
"port": "8080",
},
"new": {
"sel_type": "http_port_t",
"protocol": "tcp",
"port": "8080",
},
},
"result": True,
}
)
assert selinux.port_policy_present(name, "http_port_t") == ret
# Test adding new port policy with custom name and using protocol and port parameters
mock_add = MagicMock(return_value={"retcode": 0})
mock_modify = MagicMock(return_value={"retcode": 0})
mock_get = MagicMock(
side_effect=[
None,
None,
{"sel_type": "http_cache_port_t", "protocol": "tcp", "port": "8081"},
]
)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_add_policy": mock_add,
"selinux.port_modify_policy": mock_modify,
},
):
with patch.dict(selinux.__opts__, {"test": False}):
ret.update(
{
"name": "required_protocol_port",
"comment": "",
"changes": {
"old": None,
"new": {
"sel_type": "http_cache_port_t",
"protocol": "tcp",
"port": "8081",
},
},
"result": True,
}
)
assert (
selinux.port_policy_present(
"required_protocol_port",
"http_cache_port_t",
protocol="tcp",
port="8081",
)
== ret
)
# Test failure of adding new policy
mock_add = MagicMock(return_value={"retcode": 1})
mock_modify = MagicMock(return_value={"retcode": 1})
mock_get = MagicMock(return_value=None)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_add_policy": mock_add,
"selinux.port_modify_policy": mock_modify,
},
):
with patch.dict(selinux.__opts__, {"test": False}):
comt = "Error adding new policy: {'retcode': 1}"
ret.update({"name": name, "comment": comt, "changes": {}, "result": False})
assert selinux.port_policy_present(name, "http_cache_port_t") == ret
def test_port_policy_absent():
"""
Test to delete an SELinux port.
"""
name = "tcp/8080"
protocol = "tcp"
port = "8080"
ret = {"name": name, "changes": {}, "result": False, "comment": ""}
# Test policy already removed
mock_delete = MagicMock(return_value={"retcode": 0})
mock_get = MagicMock(return_value=None)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_delete_policy": mock_delete,
},
):
with patch.dict(selinux.__opts__, {"test": False}):
comt = (
f'SELinux policy for "{name}" already absent '
+ f'with specified sel_type "http_cache_port_t", protocol "None" '
+ f'and port "None".'
)
ret.update({"comment": comt, "changes": {}, "result": True})
assert selinux.port_policy_absent(name, "http_cache_port_t") == ret
comt = (
f'SELinux policy for "name" already absent '
+ f'with specified sel_type "http_cache_port_t", protocol "{protocol}" '
+ f'and port "{port}".'
)
ret.update({"comment": comt, "changes": {}, "result": True, "name": "name"})
assert (
selinux.port_policy_absent("name", "http_cache_port_t", protocol, port)
== ret
)
ret.update({"name": name})
# Test removing a policy
mock_delete = MagicMock(return_value={"retcode": 0})
mock_get = MagicMock(
side_effect=[
{"sel_type": "http_cache_port_t", "protocol": "tcp", "port": "8080"},
{"sel_type": "http_cache_port_t", "protocol": "tcp", "port": "8080"},
None,
]
)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_delete_policy": mock_delete,
},
):
with patch.dict(selinux.__opts__, {"test": True}):
ret.update({"comment": "", "result": None})
assert selinux.port_policy_absent(name, "http_cache_port_t") == ret
with patch.dict(selinux.__opts__, {"test": False}):
ret.update(
{
"comment": "",
"changes": {
"old": {
"sel_type": "http_cache_port_t",
"protocol": "tcp",
"port": "8080",
},
"new": None,
},
"result": True,
}
)
assert selinux.port_policy_absent(name, "http_cache_port_t") == ret
# Test removing a policy using custom name and with protocol and port parameters
mock_delete = MagicMock(return_value={"retcode": 0})
mock_get = MagicMock(
side_effect=[
{"sel_type": "http_cache_port_t", "protocol": "tcp", "port": "8081"},
None,
]
)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_delete_policy": mock_delete,
},
):
with patch.dict(selinux.__opts__, {"test": False}):
ret.update(
{
"name": "required_protocol_port",
"comment": "",
"changes": {
"old": {
"sel_type": "http_cache_port_t",
"protocol": "tcp",
"port": "8081",
},
"new": None,
},
"result": True,
}
)
assert (
selinux.port_policy_absent(
"required_protocol_port",
"http_cache_port_t",
protocol="tcp",
port="8081",
)
== ret
)
# Test failure to delete a policy
mock_delete = MagicMock(return_value={"retcode": 2})
mock_get = MagicMock(
return_value={
"sel_type": "http_cache_port_t",
"protocol": "tcp",
"port": "8080",
}
)
with patch.dict(
selinux.__salt__,
{
"selinux.port_get_policy": mock_get,
"selinux.port_delete_policy": mock_delete,
},
):
with patch.dict(selinux.__opts__, {"test": False}):
comt = "Error deleting policy: {'retcode': 2}"
ret.update({"name": name, "comment": comt, "changes": {}, "result": False})
assert selinux.port_policy_absent(name, "http_cache_port_t") == ret