SELinux ======= xCAT runs on a management node that has SELinux in enforcing mode. An xCAT installation does not change the SELinux mode of the machine. Scope ----- The policy module covers the management node. It defines the domain of ``xcatd``, of the client commands and of the image build, and it labels the files xCAT owns. A service node and a compute node get no xCAT domain. They get a mode. ``noderes.selinux`` on the node, then the ``noderes.selinux`` of its groups, then ``site.selinux`` decides the mode that the installer of a diskful compute or service node configures. A stateless or statelite node gets ``selinux=0``, because its root carries no label. See :doc:`SELinux ` for the table and for the switches. The two domains --------------- ``xcatd`` and the xCAT client commands run in ``xcat_t``. The image build runs in ``xcat_image_t``, because it creates a complete operating system tree and therefore writes and relabels files of every type. The ``xCAT-selinux`` package labels the files each domain is entered through: =========================================== ======================= File Type =========================================== ======================= /opt/xcat/sbin/xcatd ``xcatd_exec_t`` /opt/xcat/bin/xcatclient ``xcat_client_exec_t`` /opt/xcat/bin/xcatclientnnr ``xcat_client_exec_t`` /opt/xcat/bin/xcatDBcmds ``xcat_client_exec_t`` /opt/xcat/bin/genimage ``xcat_client_exec_t`` /opt/xcat/share/xcat/netboot//genimage ``xcat_image_exec_t`` /opt/xcat/share/xcat/netboot//geninitrd ``xcat_image_exec_t`` =========================================== ======================= ``xcatd`` runs the per-OS ``genimage`` through a shell, so the build enters ``xcat_image_t`` from ``xcat_t``. An administrator who runs ``genimage`` directly enters it the same way. What each domain holds ---------------------- ``xcat_image_t`` may create, write and relabel a file of any type, including the security types a package installs inside the image: ``/etc/selinux``, the audit configuration and the keytabs of the image. The kernel cannot tell a path inside the image from the same type on the management node, so this is the broadest grant in the module and the reason the build has a domain of its own. ``xcat_t`` keeps what the daemon needs: its sockets and the ``xcatd`` port, ``/install``, ``/tftpboot``, the configuration of the services it drives, the credentials under ``/etc/xcat``, and the ``service`` class for the units it restarts. It also manages and relabels non-security files, because ``packimage`` and ``liteimg`` are symlinks to ``xcatclientnnr`` and their work runs inside ``xcatd``. What it never gets is the security types. Services xCAT starts itself --------------------------- xCAT starts ``in.tftpd`` without a systemd unit. The module transitions it to the stock ``tftpd_t``, so the TFTP server does not serve UDP 69 with the authority of the management domain. ``chronyd``, which ``setupntp`` runs, reaches ``chronyd_t`` the same way. systemd enters ``xcat_t`` on the ``xcatd`` script, so the ``ExecStart`` of the unit must name ``xcatd`` and not a shell. 115 command names are symlinks to ``xcatclient`` or ``xcatclientnnr``, and each one enters the domain through the file it points to. ``/usr/sbin/xcatd`` is a symlink, and ``execve`` resolves it, so the context belongs to the file in ``/opt/xcat``. A rootimg component in the middle of an image root is ambiguous, because ``rootimgdir`` is any path under ``/install``. The file contexts leave the published kernel and initrd of such an image root out of the relabel, so that they cannot take the guest labels with them. A published ``common`` or ``overlay`` directory of an image root called ``rootimg`` is left out for the same reason: its path is also the path of a ``common`` or ``overlay`` directory at the root of a guest tree. A path the file contexts leave out keeps the label it has. ``restorecon`` does not change it and does not clear it. ``genimage`` writes into an image root that ``restorecon`` already made ``public_content_t``, and a new file inherits the type of its directory, so the kernel is ``public_content_t`` and ``httpd`` serves it. Netboot works. The cost is a published artifact of such an image root that already carries another type, from before the package was installed. ``restorecon`` over the publication directory cannot repair it, with or without ``-F``, because the path has no context to apply. Give it one. ``site.installdir`` selects the publication directory. Add the mappings in this order, and relabel once at the end:: installdir=$(lsdef -t site -i installdir -c | sed 's/^.*=//; s:/*$::') imgroot=$installdir/netboot/rootimg/rhels9/x86_64/compute # 1. The publication directory, when site.installdir is not /install. The # packaged mappings name /install and cover no other directory. semanage fcontext -a -t public_content_t "$installdir(/.*)?" # 2. Both guest trees, before anything relabels them. semanage fcontext -a -t '<>' "$installdir(/.*)?/rootimg(/.*)?" semanage fcontext -a -t '<>' "$installdir(/.*)?/rootimg-statelite(/.*)?" # 3. The published artifacts of the one image root, after the exclusions. for name in kernel hypervisor 'initrd[^/]*' 'rootimg\.[^/]*' \ 'rootimg-statelite\.[^/]*' 'rootimgtree\.[^/]*' \ 'litefile\.csv' 'common\.filelist'; do semanage fcontext -a -t public_content_t -f f "$imgroot/$name" done # 4. One relabel, with every mapping in place. restorecon -R -v "$installdir" Steps 1 and 2 come first because a relabel before them writes a generic type over the guest labels. With ``site.installdir=/srv/xcat-install`` and no mapping for that tree, ``rootimg/etc/shadow`` resolves to ``var_t``. A mapping added after such a relabel only stops the next one from changing what is already wrong. Step 3 comes after step 2 because ``file_contexts`` keeps the expressions in the order they are read, and the last one that matches a path wins. ``$installdir(/.*)?/rootimg(/.*)?`` matches ``$imgroot`` itself when the image root carries a ``rootimg`` component in the middle, so it matches the published artifacts too. Read after the exclusions, each artifact mapping wins against them. Read before, the artifacts resolve to ``<>``. An expression with no wildcard, such as ``kernel``, wins either way, so the defect shows on the initrds and the packed images alone. Step 4 runs once. Each expression in step 3 names one regular file directly below the image root, so no path in ``$imgroot/rootimg`` or ``$imgroot/rootimg-statelite`` matches one, and ``restorecon`` leaves every file in both guest trees alone. Under the standard ``/install`` the packaged mappings already carry steps 1 and 2, and ``file_contexts.local`` is read after the packaged file, so step 3 still wins. Do not repair a label with ``chcon``, and do not map the image root as a tree. ``chcon`` writes a type that no mapping selects, so the next relabel of a path that has a mapping reverts it, and a recursive ``chcon`` relabels both guest trees as well. A tree mapping over the image root relabels them through ``restorecon`` instead. A mapping is persistent, so a later recursive relabel and a policy upgrade both keep the result. ``xCAT-test/native/selinux_image_repair.t`` starts with the wrong type on every published artifact and valid types in both guest trees, runs the four steps in order, and reads the labels back after a recursive relabel and after a reinstall of the module. It does that for a moved ``installdir`` whose image root also carries a ``rootimg`` component in the middle, which is the layout that needs every step. The unit holds ``/opt/xcat`` as the default prefix, and ``%post`` writes ``/etc/xcat/xcatroot.env`` with the prefix of the installation, which the unit reads after that default. The file contexts name ``/opt/xcat``, so a package relocated with ``rpm --prefix`` starts in its own tree but needs its own file context entries before the entrypoints reach ``xcat_t``. Read the domain of the running daemon with:: ps -o label= -p "$(cat /run/xcatd.pid)" ps -eo label,args | grep '[x]catd:' The domains are permissive -------------------------- The policy source declares ``permissive xcat_t;`` and ``permissive xcat_image_t;``. The host policy stays enforcing, and the kernel records each access that ``xcat_t`` makes outside the rules rather than refusing it. The CI collects those records, and each rule the module gains comes from one of them. A later release removes the declaration and the domain becomes enforcing. List the permissive domains of the loaded policy with:: semodule -l | grep -x xcat seinfo --permissive A transition to another domain is not covered by a permissive declaration. ``tftpd_t``, ``chronyd_t`` and the stock domains enforce their own rules. A denial that the kernel enforced carries ``permissive=0``. On a management node of this version, only such a record reports a refused access:: ausearch --input-logs -m AVC,USER_AVC -ts today | grep permissive=0 Commands run by a user ---------------------- Targeted policy gives root the ``unconfined_t`` domain, and the module transitions from there into ``xcat_t``. A confined user domain, such as ``staff_t``, gets no transition: it would leave its own confinement for a permissive domain. A command run from such a domain stays in it, and SELinux still applies the rules of that domain. SELinux does not replace the file permissions. A user who cannot read ``/etc/xcat`` cannot read it in ``xcat_t`` either. The access the module grants other services ------------------------------------------- ``xcat_t`` is one part of the module. The rest grants the access that the services around xCAT need: * ``/install`` is ``public_content_t``, so ``httpd`` serves it. Each ``rootimg`` tree is left out of the relabel, because it keeps the labels of its own operating system. * ``httpd`` reads and maps the ``/tftpboot`` files that ``xcat.conf`` serves. * ``xcatws.cgi`` connects to the ``xcatd`` port. * ``rsync`` reads ``/etc/shadow`` and writes a copy it created at mode 0000. * ``useradd`` and ``groupadd`` read the ``rootimg`` ``/var/run`` symlink, which the rpm scriptlets of a ``genimage`` build need. Reading denials --------------- ``ausearch`` without ``--input-logs`` reported nothing on an AlmaLinux 10 node whose log held 1032 denials, because audit 4.0.3 reads the journal rather than the file ``auditd.conf`` names. Pass ``--input-logs``, or read the log file, before you conclude that a machine is clean. The kernel also drops audit records printed before ``auditd`` starts, at ``printk_ratelimit_burst`` and with no notice. Boot with ``sysctl.kernel.printk_ratelimit=0`` to see that window whole. A count taken without it is a lower bound.