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
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 |
|
/opt/xcat/bin/xcatclient |
|
/opt/xcat/bin/xcatclientnnr |
|
/opt/xcat/bin/xcatDBcmds |
|
/opt/xcat/bin/genimage |
|
/opt/xcat/share/xcat/netboot/<os>/genimage |
|
/opt/xcat/share/xcat/netboot/<os>/geninitrd |
|
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 '<<none>>' "$installdir(/.*)?/rootimg(/.*)?"
semanage fcontext -a -t '<<none>>' "$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 <<none>>. 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:
/installispublic_content_t, sohttpdserves it. Eachrootimgtree is left out of the relabel, because it keeps the labels of its own operating system.httpdreads and maps the/tftpbootfiles thatxcat.confserves.xcatws.cgiconnects to thexcatdport.rsyncreads/etc/shadowand writes a copy it created at mode 0000.useraddandgroupaddread therootimg/var/runsymlink, which the rpm scriptlets of agenimagebuild 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.