mirror of
https://github.com/containers/udica
synced 2024-05-20 01:06:12 +02:00
Compare commits
4 Commits
391f8655f0
...
5638f0b938
Author | SHA1 | Date | |
---|---|---|---|
vmojzis | 5638f0b938 | ||
Vit Mojzis | f411c14698 | ||
Vit Mojzis | d444e67ead | ||
Vit Mojzis | 3cda61f9a5 |
50
README.md
50
README.md
|
@ -170,6 +170,56 @@ SELinux now allows binding to tcp/udp port *21*, but not to *80*:
|
|||
Ncat: SHA-1 fingerprint: 6EEC 102E 6666 5F96 CC4F E5FA A1BE 4A5E 6C76 B6DC
|
||||
Ncat: bind to :::80: Permission denied. QUITTING.
|
||||
|
||||
## Creating SELinux policy for confined user
|
||||
|
||||
Each Linux user on an SELinux-enabled system is mapped to an SELinux user. By default administrators can choose between the following SELinux users when confining a user account: root, staff_u, sysadm_u, user_u, xguest_u, guest_u (and unconfined_u which does not limit the user's actions).
|
||||
|
||||
To give administrators more options in confining users, *udica* now provides a way to generate a custom SELinux user (and corresponding roles and types) based on the specified parameters. The new user policy is assembled using a set of predefined policy macros based on use-cases (managing network, administrative tasks, etc.).
|
||||
|
||||
To generate a confined user, use the "confined_user" keyword followed by a list of options:
|
||||
|
||||
| Option | Use case |
|
||||
| ------------- | ------------- |
|
||||
| -a, --admin_commands | Use administrative commands (vipw, passwd, ...) |
|
||||
| -g, --graphical_login | Use graphical login environment |
|
||||
| -m, --mozilla_usage | Use mozilla firefox |
|
||||
| -n, --networking | Manage basic networking (ip, ifconfig, traceroute, tcpdump, ...) |
|
||||
| -d, --security_advanced | Manage SELinux settings (semanage, semodule, sepolicy, ...) |
|
||||
| -i, --security_basic | Use read-only security-related tools (seinfo, getsebool, sesearch, ...) |
|
||||
| -s, --sudo | Run commands as root using sudo |
|
||||
| -l, --user_login | Basic rules common to all users (tty, pty, ...) |
|
||||
| -c, --ssh_connect | Connect over SSH |
|
||||
| -b, --basic_commands | Use basic commands (date, ls, ps, man, systemctl -user, journalctl -user, passwd, ...) |
|
||||
|
||||
The new user also needs to be assigned an MLS/MCS level and range. These are set to `s0` and `s0:c0.c1023` respectively by default to work well in *targeted* policy mode.
|
||||
For more details see [Red Hat Multi-Level Security documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/using_selinux/index#using-multi-level-security-mls_using-selinux).
|
||||
|
||||
```
|
||||
$ udica confined_user -abcdgilmns --level s0 --range "s0:c0" custom_user
|
||||
|
||||
Created custom_user.cil
|
||||
Run the following commands to apply the new policy:
|
||||
Install the new policy module
|
||||
# semodule -i custom_user.cil /usr/share/udica/macros/confined_user_macros.cil
|
||||
Create a default context file for the new user
|
||||
# sed -e ’s|user|custom_user|g’ /etc/selinux/targeted/contexts/users/user_u > /etc/selinux/targeted/contexts/users/custom_user_u
|
||||
Map the new selinux user to an existing user account
|
||||
# semanage login -a -s custom_user_u custom_user
|
||||
Fix labels in the user's home directory
|
||||
# restorecon -RvF /home/custom_user
|
||||
```
|
||||
|
||||
As prompted by *udica*, the new user policy needs to be installed into the system along with the *confined_user_macros* file and a *default context* file needs to be created before the policy is ready to be used.
|
||||
|
||||
Last step is either assignment to an existing linux user (using `semanage login`), or specifying the new SELinux user when creating a new linux user account (no need to run `restorecon` for a new user home directory).
|
||||
```
|
||||
useradd -Z custom_user_u
|
||||
```
|
||||
|
||||
The created policy defines a new SELinux user `<user_name>_u`, a corresponding role `<user_name>_r` and a list of types (varies based on selected options) `<user_name>_t, <user_name>_sudo_t, <user_name>_ssh_agent_t, ...`
|
||||
|
||||
See [Red Hat Confined User documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/using_selinux/managing-confined-and-unconfined-users_using-selinux#doc-wrapper) for more details about confined users, their assignment, available roles and access they allow.
|
||||
|
||||
## SELinux labels vs. objects they represent
|
||||
|
||||
Policies generated by *udica* work with **SELinux labels** as opposed to filesystem paths, port numbers etc. This means that allowing access to given path (e.g. path to a directory mounted to your container), port number, or any other resource may also allow access to other resources you didn't specify, since the same SELinux label can be assigned to multiple resources.
|
||||
|
|
1
setup.py
1
setup.py
|
@ -37,6 +37,7 @@ setuptools.setup(
|
|||
data_files=[
|
||||
("/usr/share/licenses/udica", ["LICENSE"]),
|
||||
("/usr/share/udica/ansible", ["udica/ansible/deploy-module.yml"]),
|
||||
("/usr/share/udica/macros", ["udica/macros/confined_user_macros.cil"]),
|
||||
],
|
||||
# scripts=["bin/udica"],
|
||||
entry_points={"console_scripts": ["udica=udica.__main__:main"]},
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
(boolean my_container_exec_content true)
|
||||
(role my_container_r)
|
||||
(type my_container_dbus_t)
|
||||
(type my_container_gkeyringd_t)
|
||||
(type my_container_ssh_agent_t)
|
||||
(type my_container_sudo_t)
|
||||
(type my_container_sudo_tmp_t)
|
||||
(type my_container_t)
|
||||
(type my_container_userhelper_t)
|
||||
(user my_container_u)
|
||||
(userrole my_container_u my_container_r)
|
||||
(userlevel my_container_u (s0))
|
||||
(userrange my_container_u ((s0 ) (s0 (c0))))
|
||||
|
||||
(call confinedom_admin_commands_macro (my_container_t my_container_r my_container_sudo_t))
|
||||
(call confinedom_graphical_login_macro (my_container_t my_container_r my_container_dbus_t))
|
||||
(call confinedom_mozilla_usage_macro (my_container_t my_container_r))
|
||||
(call confinedom_networking_macro (my_container_t my_container_r))
|
||||
(call confinedom_security_advanced_macro (my_container_t my_container_r my_container_sudo_t my_container_userhelper_t))
|
||||
(call confinedom_security_basic_macro (my_container_t my_container_r))
|
||||
(call confinedom_sudo_macro (my_container_t my_container_r my_container_sudo_t my_container_sudo_tmp_t))
|
||||
(call confinedom_user_login_macro (my_container_t my_container_r my_container_gkeyringd_t my_container_dbus_t my_container_exec_content))
|
||||
(call confined_ssh_connect_macro (my_container_t my_container_r my_container_ssh_agent_t))
|
||||
(call confined_use_basic_commands_macro (my_container_t my_container_r))
|
|
@ -0,0 +1,15 @@
|
|||
(boolean my_container_exec_content true)
|
||||
(role my_container_r)
|
||||
(type my_container_dbus_t)
|
||||
(type my_container_gkeyringd_t)
|
||||
(type my_container_ssh_agent_t)
|
||||
(type my_container_sudo_t)
|
||||
(type my_container_t)
|
||||
(user my_container_u)
|
||||
(userrole my_container_u my_container_r)
|
||||
(userlevel my_container_u (s0))
|
||||
(userrange my_container_u ((s0 ) (s0 (c0))))
|
||||
|
||||
(call confinedom_admin_commands_macro (my_container_t my_container_r my_container_sudo_t))
|
||||
(call confinedom_user_login_macro (my_container_t my_container_r my_container_gkeyringd_t my_container_dbus_t my_container_exec_content))
|
||||
(call confined_ssh_connect_macro (my_container_t my_container_r my_container_ssh_agent_t))
|
|
@ -0,0 +1,12 @@
|
|||
(boolean my_container_exec_content true)
|
||||
(role my_container_r)
|
||||
(type my_container_dbus_t)
|
||||
(type my_container_gkeyringd_t)
|
||||
(type my_container_t)
|
||||
(user my_container_u)
|
||||
(userrole my_container_u my_container_r)
|
||||
(userlevel my_container_u (s0))
|
||||
(userrange my_container_u ((s0 ) (s0 (c0))))
|
||||
|
||||
(call confinedom_user_login_macro (my_container_t my_container_r my_container_gkeyringd_t my_container_dbus_t my_container_exec_content))
|
||||
(call confined_use_basic_commands_macro (my_container_t my_container_r))
|
|
@ -0,0 +1,17 @@
|
|||
(boolean my_container_exec_content true)
|
||||
(role my_container_r)
|
||||
(type my_container_dbus_t)
|
||||
(type my_container_gkeyringd_t)
|
||||
(type my_container_sudo_t)
|
||||
(type my_container_sudo_tmp_t)
|
||||
(type my_container_t)
|
||||
(type my_container_userhelper_t)
|
||||
(user my_container_u)
|
||||
(userrole my_container_u my_container_r)
|
||||
(userlevel my_container_u (s0))
|
||||
(userrange my_container_u ((s0 ) (s0 (c0))))
|
||||
|
||||
(call confinedom_security_advanced_macro (my_container_t my_container_r my_container_sudo_t my_container_userhelper_t))
|
||||
(call confinedom_security_basic_macro (my_container_t my_container_r))
|
||||
(call confinedom_sudo_macro (my_container_t my_container_r my_container_sudo_t my_container_sudo_tmp_t))
|
||||
(call confinedom_user_login_macro (my_container_t my_container_r my_container_gkeyringd_t my_container_dbus_t my_container_exec_content))
|
|
@ -369,7 +369,26 @@ class TestBase(unittest.TestCase):
|
|||
self.assert_templates(output, ["base_container"])
|
||||
self.assert_policy(test_file("test_devices.podman.cil"))
|
||||
|
||||
def run_udica(self, args):
|
||||
# Confined user tests
|
||||
def test_confined_user(self):
|
||||
"""udica confined_user <args> --level s0 --range s0:c0 my_container"""
|
||||
for arg in ["cla", "lb", "lsid", "abcdgilmns"]:
|
||||
output = self.run_udica(
|
||||
[
|
||||
"udica",
|
||||
"confined_user",
|
||||
"-{}".format(arg),
|
||||
"--level",
|
||||
"s0",
|
||||
"--range",
|
||||
"s0:c0",
|
||||
"my_container",
|
||||
],
|
||||
True,
|
||||
)
|
||||
self.assert_policy(test_file("test_confined_{}.cil".format(arg)))
|
||||
|
||||
def run_udica(self, args, confined=False):
|
||||
with patch("sys.argv", args):
|
||||
with patch("sys.stderr.write") as mock_err, patch(
|
||||
"sys.stdout.write"
|
||||
|
@ -383,10 +402,16 @@ class TestBase(unittest.TestCase):
|
|||
udica.__main__.main()
|
||||
mock_err.assert_not_called()
|
||||
|
||||
self.assertRegex(mock_out.output, "Policy my_container created")
|
||||
self.assertRegex(
|
||||
mock_out.output, "--security-opt label=type:my_container.process"
|
||||
)
|
||||
if confined:
|
||||
self.assertRegex(mock_out.output, "semodule -i my_container.cil")
|
||||
self.assertRegex(
|
||||
mock_out.output, "semanage login -a -s my_container_u my_container"
|
||||
)
|
||||
else:
|
||||
self.assertRegex(mock_out.output, "Policy my_container created")
|
||||
self.assertRegex(
|
||||
mock_out.output, "--security-opt label=type:my_container.process"
|
||||
)
|
||||
|
||||
return mock_out.output
|
||||
|
||||
|
|
|
@ -13,8 +13,9 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import subprocess
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# import udica
|
||||
from udica.parse import parse_avc_file
|
||||
|
@ -25,116 +26,224 @@ from udica.policy import create_policy, load_policy, generate_playbook
|
|||
|
||||
|
||||
def get_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Script generates SELinux policy for running container."
|
||||
)
|
||||
parser.add_argument("-V", "--version", action="version", version=version)
|
||||
parser.add_argument(
|
||||
type=str, help="Name for SELinux policy module", dest="ContainerName"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--container-id",
|
||||
type=str,
|
||||
help="Running container ID",
|
||||
dest="ContainerID",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-j",
|
||||
"--json",
|
||||
help='Load json from this file, use "-j -" for stdin',
|
||||
required=False,
|
||||
dest="JsonFile",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--full-network-access",
|
||||
help="Allow container full Network access ",
|
||||
required=False,
|
||||
dest="FullNetworkAccess",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tty-access",
|
||||
help="Allow container to read and write the controlling terminal ",
|
||||
required=False,
|
||||
dest="TtyAccess",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--X-access",
|
||||
help="Allow container to communicate with Xserver ",
|
||||
required=False,
|
||||
dest="XAccess",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--virt-access",
|
||||
help="Allow container to communicate with libvirt ",
|
||||
required=False,
|
||||
dest="VirtAccess",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--stream-connect",
|
||||
help="Allow container to stream connect with given SELinux domain ",
|
||||
required=False,
|
||||
dest="StreamConnect",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--load-modules",
|
||||
help="Load templates and module created by this tool ",
|
||||
required=False,
|
||||
dest="LoadModules",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--caps",
|
||||
help='List of capabilities, e.g "-c AUDIT_WRITE,CHOWN,DAC_OVERRIDE,FOWNER,FSETID,KILL,MKNOD,NET_BIND_SERVICE,NET_RAW,SETFCAP,SETGID,SETPCAP,SETUID,SYS_CHROOT"',
|
||||
required=False,
|
||||
dest="Caps",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--devices",
|
||||
type=str,
|
||||
help='List of devices the container should have access to, e.g "--devices /dev/dri/card0,/dev/dri/renderD128"',
|
||||
dest="Devices",
|
||||
required=False,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--ansible",
|
||||
help="Generate ansible playbook to deploy SELinux policy for containers ",
|
||||
required=False,
|
||||
dest="Ansible",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--append-rules",
|
||||
type=str,
|
||||
help="Append more SELinux allow rules from file",
|
||||
dest="FileAVCS",
|
||||
required=False,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
"--container-engine",
|
||||
type=str,
|
||||
help="Specify which container engine is used for the inspected container (supports: {})".format(
|
||||
", ".join(ENGINE_ALL)
|
||||
),
|
||||
dest="ContainerEngine",
|
||||
required=False,
|
||||
default="-",
|
||||
)
|
||||
if "confined_user" in sys.argv:
|
||||
# set up confined_user parser (do not show normal "udica" options)
|
||||
parser = argparse.ArgumentParser(
|
||||
description="SELinux confined user policy generator"
|
||||
)
|
||||
parser.add_argument("confined_user")
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--admin_commands",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="admin_commands",
|
||||
help="Use administrative commands (vipw, passwd, ...)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-g",
|
||||
"--graphical_login",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="graphical_login",
|
||||
help="Use graphical login environment",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--mozilla_usage",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="mozilla_usage",
|
||||
help="Use mozilla firefox",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--networking",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="networking",
|
||||
help="Manage basic networking (ip, ifconfig, traceroute, tcpdump, ...)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--security_advanced",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="security_advanced",
|
||||
help="Manage SELinux settings (semanage, semodule, sepolicy, ...)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--security_basic",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="security_basic",
|
||||
help="Use read-only security-related tools (seinfo, getsebool, sesearch, ...)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--sudo",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="sudo",
|
||||
help="Run commands as root using sudo",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--user_login",
|
||||
action="store_true",
|
||||
default=True,
|
||||
dest="user_login",
|
||||
help="Basic rules common to all users (tty, pty, ...)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--ssh_connect",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="ssh_connect",
|
||||
help="Connect over SSH",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--basic_commands",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="basic_commands",
|
||||
help="Use basic commands (date, ls, ps, man, systemctl -user, journalctl -user, passwd, ...)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--level",
|
||||
nargs="?",
|
||||
default="s0",
|
||||
dest="level",
|
||||
help='MLS/MCS level, defaults to "s0"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--range",
|
||||
nargs="?",
|
||||
default="s0-s0:c0.c1023",
|
||||
dest="range",
|
||||
help='MLS/MCS range, defaults to "s0-s0:c0.c1023"',
|
||||
)
|
||||
parser.add_argument("uname")
|
||||
else:
|
||||
# set up normal udica parser
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Script generates SELinux policy for running container.",
|
||||
prog="udica [confined_user]",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""Additional options:
|
||||
confined_user Generate policy for a new confined user instead of a container policy""",
|
||||
)
|
||||
parser.add_argument("-V", "--version", action="version", version=version)
|
||||
parser.add_argument(
|
||||
type=str, help="Name for SELinux policy module", dest="ContainerName"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--container-id",
|
||||
type=str,
|
||||
help="Running container ID",
|
||||
dest="ContainerID",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-j",
|
||||
"--json",
|
||||
help='Load json from this file, use "-j -" for stdin',
|
||||
required=False,
|
||||
dest="JsonFile",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--full-network-access",
|
||||
help="Allow container full Network access ",
|
||||
required=False,
|
||||
dest="FullNetworkAccess",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tty-access",
|
||||
help="Allow container to read and write the controlling terminal ",
|
||||
required=False,
|
||||
dest="TtyAccess",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--X-access",
|
||||
help="Allow container to communicate with Xserver ",
|
||||
required=False,
|
||||
dest="XAccess",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--virt-access",
|
||||
help="Allow container to communicate with libvirt ",
|
||||
required=False,
|
||||
dest="VirtAccess",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--stream-connect",
|
||||
help="Allow container to stream connect with given SELinux domain ",
|
||||
required=False,
|
||||
dest="StreamConnect",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--load-modules",
|
||||
help="Load templates and module created by this tool ",
|
||||
required=False,
|
||||
dest="LoadModules",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--caps",
|
||||
help='List of capabilities, e.g "-c AUDIT_WRITE,CHOWN,DAC_OVERRIDE,FOWNER,FSETID,KILL,MKNOD,NET_BIND_SERVICE,NET_RAW,SETFCAP,SETGID,SETPCAP,SETUID,SYS_CHROOT"',
|
||||
required=False,
|
||||
dest="Caps",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--devices",
|
||||
type=str,
|
||||
help='List of devices the container should have access to, e.g "--devices /dev/dri/card0,/dev/dri/renderD128"',
|
||||
dest="Devices",
|
||||
required=False,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--ansible",
|
||||
help="Generate ansible playbook to deploy SELinux policy for containers ",
|
||||
required=False,
|
||||
dest="Ansible",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--append-rules",
|
||||
type=str,
|
||||
help="Append more SELinux allow rules from file",
|
||||
dest="FileAVCS",
|
||||
required=False,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
"--container-engine",
|
||||
type=str,
|
||||
help="Specify which container engine is used for the inspected container (supports: {})".format(
|
||||
", ".join(ENGINE_ALL)
|
||||
),
|
||||
dest="ContainerEngine",
|
||||
required=False,
|
||||
default="-",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
return vars(args)
|
||||
|
||||
|
@ -142,6 +251,13 @@ def get_args():
|
|||
def main():
|
||||
opts = get_args()
|
||||
|
||||
# generate confined user policy
|
||||
if "confined_user" in opts.keys():
|
||||
from udica.confined_user import create_confined_user_policy
|
||||
|
||||
create_confined_user_policy(opts)
|
||||
return
|
||||
|
||||
if opts["ContainerID"]:
|
||||
container_inspect_raw = None
|
||||
for backend in [ENGINE_PODMAN, ENGINE_DOCKER]:
|
||||
|
@ -167,8 +283,6 @@ def main():
|
|||
|
||||
if opts["JsonFile"]:
|
||||
if opts["JsonFile"] == "-":
|
||||
import sys
|
||||
|
||||
container_inspect_raw = sys.stdin.read()
|
||||
else:
|
||||
import os.path
|
||||
|
@ -182,8 +296,6 @@ def main():
|
|||
|
||||
if (not opts["JsonFile"]) and (not opts["ContainerID"]):
|
||||
try:
|
||||
import sys
|
||||
|
||||
container_inspect_raw = sys.stdin.read()
|
||||
except Exception as e:
|
||||
print("Couldn't parse inspect data from stdin:", e)
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
# Copyright (C) 2023 Vit Mojzis, <vmojzis@redhat.com>
|
||||
#
|
||||
# This program 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 2 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
MACRO_CALLS = {
|
||||
"admin_commands": (
|
||||
"(call confinedom_admin_commands_macro ({}))",
|
||||
("_t", "_r", "_sudo_t"),
|
||||
),
|
||||
"graphical_login": (
|
||||
"(call confinedom_graphical_login_macro ({}))",
|
||||
("_t", "_r", "_dbus_t"),
|
||||
),
|
||||
"mozilla_usage": ("(call confinedom_mozilla_usage_macro ({}))", ("_t", "_r")),
|
||||
"networking": ("(call confinedom_networking_macro ({}))", ("_t", "_r")),
|
||||
"security_advanced": (
|
||||
"(call confinedom_security_advanced_macro ({}))",
|
||||
("_t", "_r", "_sudo_t", "_userhelper_t"),
|
||||
),
|
||||
"security_basic": ("(call confinedom_security_basic_macro ({}))", ("_t", "_r")),
|
||||
"sudo": (
|
||||
"(call confinedom_sudo_macro ({}))",
|
||||
("_t", "_r", "_sudo_t", "_sudo_tmp_t"),
|
||||
),
|
||||
"user_login": (
|
||||
"(call confinedom_user_login_macro ({}))",
|
||||
("_t", "_r", "_gkeyringd_t", "_dbus_t", "_exec_content"),
|
||||
),
|
||||
"ssh_connect": (
|
||||
"(call confined_ssh_connect_macro ({}))",
|
||||
("_t", "_r", "_ssh_agent_t"),
|
||||
),
|
||||
"basic_commands": ("(call confined_use_basic_commands_macro ({}))", ("_t", "_r")),
|
||||
}
|
||||
|
||||
TYPE_DEFS = {
|
||||
"_t": "(type {}_t)",
|
||||
"_r": "(role {}_r)",
|
||||
"_dbus_t": "(type {}_dbus_t)",
|
||||
"_gkeyringd_t": "(type {}_gkeyringd_t)",
|
||||
"_ssh_agent_t": "(type {}_ssh_agent_t)",
|
||||
"_sudo_t": "(type {}_sudo_t)",
|
||||
"_sudo_tmp_t": "(type {}_sudo_tmp_t)",
|
||||
"_userhelper_t": "(type {}_userhelper_t)",
|
||||
"_exec_content": "(boolean {}_exec_content true)",
|
||||
}
|
||||
|
||||
|
||||
def create_confined_user_policy(opts):
|
||||
# MCS/MLS range handling - needs to be separated into up-to 4 parts
|
||||
# s0-s15:c0.c1023 -> (userrange {uname}_u ((s0 ) (s15 (range c0 c1023))))
|
||||
# s0:c0 -> (userrange {uname}_u ((s0 ) (s0 (c0))))
|
||||
mls_range = opts["range"]
|
||||
mcs_range = ""
|
||||
# separate MCS portion
|
||||
if ":" in opts["range"]:
|
||||
# s0:c0.c1023
|
||||
(mls_range, mcs_range) = opts["range"].split(":")
|
||||
if "-" in mls_range:
|
||||
# s0-s15
|
||||
(range_l, range_h) = mls_range.split("-")
|
||||
else:
|
||||
# s0
|
||||
range_l = mls_range
|
||||
range_h = range_l
|
||||
if mcs_range != "":
|
||||
if "." in mcs_range:
|
||||
# s0:c0.c1023 -> (userrange {uname}_u ((s0 ) (s0 (range c0 c1023))))
|
||||
(mcs_range_l, mcs_range_h) = mcs_range.split(".")
|
||||
mcs_range = "(range {} {})".format(mcs_range_l, mcs_range_h)
|
||||
else:
|
||||
# s0:c0 -> (userrange {uname}_u ((s0 ) (s0 (c0))))
|
||||
mcs_range = "({})".format(mcs_range)
|
||||
|
||||
range = "({} ) ({} {})".format(range_l, range_h, mcs_range)
|
||||
|
||||
defs = set()
|
||||
|
||||
policy = """
|
||||
(user {uname}_u)
|
||||
(userrole {uname}_u {uname}_r)
|
||||
(userlevel {uname}_u ({level}))
|
||||
(userrange {uname}_u ({range}))
|
||||
""".format(
|
||||
uname=opts["uname"], level=opts["level"], range=range
|
||||
)
|
||||
|
||||
# process arguments determining which macros are to be used
|
||||
for arg, value in opts.items():
|
||||
if not value or arg not in MACRO_CALLS.keys():
|
||||
continue
|
||||
for param in MACRO_CALLS[arg][1]:
|
||||
defs.add(TYPE_DEFS[param].format(opts["uname"]))
|
||||
policy += "\n" + (
|
||||
MACRO_CALLS[arg][0].format(
|
||||
" ".join([opts["uname"] + s for s in MACRO_CALLS[arg][1]])
|
||||
)
|
||||
)
|
||||
# print("{}: {}".format(arg, value))
|
||||
|
||||
policy = "\n".join(sorted(defs)) + policy
|
||||
|
||||
with open("{}.cil".format(opts["uname"]), "w") as f:
|
||||
f.write(policy)
|
||||
|
||||
print("Created {}.cil".format(opts["uname"]))
|
||||
print("Run the following commands to apply the new policy:")
|
||||
print("Install the new policy module")
|
||||
print(
|
||||
"# semodule -i {}.cil /usr/share/udica/macros/confined_user_macros.cil".format(
|
||||
opts["uname"]
|
||||
)
|
||||
)
|
||||
print("Create a default context file for the new user")
|
||||
print(
|
||||
"# sed -e ’s|user|{}|g’ /etc/selinux/targeted/contexts/users/user_u > /etc/selinux/targeted/contexts/users/{}_u".format(
|
||||
opts["uname"], opts["uname"]
|
||||
)
|
||||
)
|
||||
print("Map the new selinux user to an existing user account")
|
||||
print("# semanage login -a -s {}_u {}".format(opts["uname"], opts["uname"]))
|
||||
print("Fix labels in the user's home directory")
|
||||
print("# restorecon -RvF /home/{}".format(opts["uname"]))
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue