Skip to content

14. SELinux (optional)

Info

This chapter basically follows SELinux/Installation. Currently, I only use SELinux on servers, and only "mcs" policy type to be able to better isolate virtual machines from each other.

14.1. Enable SELinux

Reduce the number of services by disabling some unneeded ones in order to avoid a few SELinux denials. This may not be desired on "desktop" systems.

systemctl mask user@.service && \
systemctl disable systemd-userdbd.socket && \
rsync -a /etc/nsswitch.conf /etc/._cfg0000_nsswitch.conf && \
sed -i 's/^hosts:\([[:space:]]*\)mymachines \(.*\)$/hosts:\1\2/' /etc/._cfg0000_nsswitch.conf && \
echo -e "\e[1;32mSUCCESS\e[0m"

Setup "make.conf":

rsync -a /etc/portage/make.conf /etc/portage/._cfg0000_make.conf && \
echo -e '\nPOLICY_TYPES="mcs"' >> /etc/portage/._cfg0000_make.conf && \
sed -i 's/^USE_HARDENED="\(.*\)"/USE_HARDENED="\1 -ubac"/' /etc/portage/._cfg0000_make.conf && \
echo -e "\e[1;32mSUCCESS\e[0m"

Info

If you go with the default of unconfined useflag being enabled, you need to patch the official sec-policy/selinux-base ebuild to mitigate the problem described in bug 905371.

Thus, in regards to unconfined useflag you have two options:

Download and verify repo-patches script and sec-policy/selinux-base patch (copy&paste one command after the other):

# Switch to non-root user
su -l david

# Switch to bash shell
bash --norc

# Set the correct e-mail address
gpg_mail="d at myCodebergUsername dot de"

(
    cd /tmp/ && \
    curl -fsS --proto '=https' --tlsv1.3 --remote-name-all \
        https://codeberg.org/duxsco/gentoo-installation/raw/branch/main/bin/{repo-patches,sec-policy_selinux-base.patch,sha512.txt{,.asc}} && \
    export GNUPGHOME="$(mktemp -d)" && \
    gpg --locate-external-keys "${gpg_mail}" && \
    echo "3AAE5FC903BB199165D4C02711BE5F68440E0758:6:" | gpg --batch --import-ownertrust --quiet && \
    gpg_status="$(gpg --batch --status-fd 1 --verify sha512.txt.asc sha512.txt 2>/dev/null)" && \
    gpgconf --kill all && \
    grep -E -q "^\[GNUPG:\][[:space:]]+GOODSIG[[:space:]]+" <<< "${gpg_status}" && \
    grep -E -q "^\[GNUPG:\][[:space:]]+VALIDSIG[[:space:]]+" <<< "${gpg_status}" && \
    grep -E -q "^\[GNUPG:\][[:space:]]+TRUST_ULTIMATE[[:space:]]+" <<< "${gpg_status}" && \
    sha512sum --quiet -c sha512.txt && \
    echo -e "\e[1;32mSUCCESS\e[0m"
)

Apply the patch to sec-policy/selinux-base as root user:

mkdir -p /etc/portage/repo.postsync.d /etc/portage/repo-patches/gentoo && \
rsync -av --chown=0:0 --chmod=u=rwx,og=r /tmp/repo-patches /etc/portage/repo.postsync.d/ && \
rsync -av --chown=0:0 --chmod=a=r /tmp/sec-policy_selinux-base.patch /etc/portage/repo-patches/gentoo/ && \
ln -s /usr/share/portage/config/repos.conf /etc/portage/repos.conf/ && \
echo "dev-vcs/git -webdav" >> /etc/portage/package.use/main && \
emerge --noreplace dev-vcs/git sys-apps/pkgcore && \
eix-sync && \
echo -e "\e[1;32mSUCCESS\e[0m"
rsync -a /etc/portage/make.conf /etc/portage/._cfg0000_make.conf && \
sed -i 's/^USE_HARDENED="\(.*\)"/USE_HARDENED="\1 -unconfined"/' /etc/portage/._cfg0000_make.conf && \
echo -e "\e[1;32mSUCCESS\e[0m"

Switch to the SELinux profile:

eselect profile set "default/linux/amd64/23.0/hardened/selinux/systemd"

These commands are more or less required irrespective of the SELinux profile in use:

# (Recommended) Use the most recent SELinux policies
echo 'sec-policy/* ~amd64' >> /etc/portage/package.accept_keywords/main && \

# (Optional) To get a nice looking html site in /usr/share/doc/selinux-base-<VERSION>/mcs/html:
echo 'sec-policy/selinux-base doc' >> /etc/portage/package.use/main && \

# Definitely required:
FEATURES="-selinux" emerge --oneshot selinux-base && \

echo -e "\e[1;32mSUCCESS\e[0m"

Configure SELinux:

rsync -a /etc/selinux/config /etc/selinux/._cfg0000_config && \
sed -i 's/^SELINUXTYPE=strict$/SELINUXTYPE=mcs/' /etc/selinux/._cfg0000_config && \
echo -e "\e[1;32mSUCCESS\e[0m"

Update packages:

FEATURES="-selinux -sesandbox" emerge --oneshot selinux-base && \
FEATURES="-selinux -sesandbox" emerge --oneshot selinux-dbus && \
FEATURES="-selinux -sesandbox" emerge --oneshot selinux-base-policy && \
emerge -atuDN @world

Enable auditd logging:

systemctl enable auditd.service && \
echo -e "\e[1;32mSUCCESS\e[0m"

Rebuild the kernel with SELinux support:

emerge -at --oneshot \
$(qlist -eI sys-kernel/gentoo-kernel-bin >/dev/null && echo sys-kernel/gentoo-kernel-bin) \
$(qlist -eI sys-kernel/gentoo-kernel >/dev/null && echo sys-kernel/gentoo-kernel) && \
echo -e "\e[1;32mSUCCESS\e[0m"

Reboot with permissive kernel.

Make sure that UBAC gets disabled:

bash -c '(
    cd /usr/share/selinux/mcs && \
    semodule -i base.pp -i $(ls *.pp | grep -v base.pp) && \
    echo -e "\e[1;32mSUCCESS\e[0m"
)'

14.2. Relabel

Relabel the entire system:

mkdir /mnt/gentoo && \
mount -o bind / /mnt/gentoo && \
setfiles -r /mnt/gentoo /etc/selinux/mcs/contexts/files/file_contexts /mnt/gentoo/{dev,home,proc,run,sys,tmp,boot/efi*,var/cache/binpkgs,var/cache/distfiles,var/db/repos/gentoo,var/tmp} && \
umount /mnt/gentoo && \
rlpkg -a -r && \
echo -e "\e[1;32mSUCCESS\e[0m"

Make sure that nothing (except perhaps ".keep" files) is unlabeled:

export tmpdir="$(mktemp -d)" && \
mount --bind / "$tmpdir" && \
find "$tmpdir" -context '*unlabeled_t*' -exec ls -dZ {} + && \
umount "$tmpdir" && \
echo -e "\e[1;32mSUCCESS\e[0m"

If "/proc" was listed by above codeblock you have to relabel to avoid a denial:

# [   19.902620] audit: type=1400 audit(1663630933.439:3): avc:  denied  { mounton } for  pid=1062 comm="(auditd)" path="/run/systemd/unit-root/proc" dev="dm-3" ino=67581 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=dir permissive=1

# Credits: grift :)
export tmpdir="$(mktemp -d)" && mount --bind / "$tmpdir" && chcon system_u:object_r:proc_t:s0 "$tmpdir"/proc && umount "$tmpdir" && echo -e "\e[1;32mSUCCESS\e[0m"

In section 10. SSH Server (optional), the SSH port has been changed to 50022. This needs to be considered for no SELinux denials to occur:

 semanage port -l | grep -e ssh -e Port
SELinux Port Type              Proto    Port Number
ssh_port_t                     tcp      22 semanage port -a -t ssh_port_t -p tcp 50022 semanage port -l | grep -e ssh -e Port
SELinux Port Type              Proto    Port Number
ssh_port_t                     tcp      50022, 22

14.3. Users and services

Default "mcs" SELinux "login"settings:

 semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0                *
root                 root                 s0-s0:c0.c1023       *
 semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          user_u               s0-s0                *
root                 root                 s0-s0:c0.c1023       *

Default "mcs" SELinux "user" settings:

 semanage user -l

                Labeling   MLS/       MLS/
SELinux User    Prefix     MCS Level  MCS Range                      SELinux Roles

root            sysadm     s0         s0-s0:c0.c1023                 staff_r sysadm_r
staff_u         staff      s0         s0-s0:c0.c1023                 staff_r sysadm_r
sysadm_u        sysadm     s0         s0-s0:c0.c1023                 sysadm_r
system_u        user       s0         s0-s0:c0.c1023                 system_r
unconfined_u    unconfined s0         s0-s0:c0.c1023                 unconfined_r
user_u          user       s0         s0                             user_r

Add the initial user to the administration SELinux user:

semanage login -a -s staff_u david && \
restorecon -RFv /home/david && \
bash -c 'echo "%wheel ALL=(ALL) TYPE=sysadm_t ROLE=sysadm_r ALL" | EDITOR="tee" visudo -f /etc/sudoers.d/wheel && \
echo -e "\e[1;32mSUCCESS\e[0m"'

Now, we should have:

 semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0                *
david                staff_u              s0-s0:c0.c1023       *
root                 root                 s0-s0:c0.c1023       *
 semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          user_u               s0-s0                *
david                staff_u              s0-s0:c0.c1023       *
root                 root                 s0-s0:c0.c1023       *

Create "/var/lib/sepolgen/interface_info" for "audit2why -R" to work:

sepolgen-ifgen -i /usr/share/selinux/mcs/include/support/ && \
echo -e "\e[1;32mSUCCESS\e[0m"

14.4. SELinux policies

At this point, you can reboot into permissive mode again and use the selinux-policy-creator.sh script.