Lab: Finish a Custom SELinux Policy Module for a Confined Domain

Estimated reading time: 7 minutes.

Objective

Resolve the latest AVC errors from a custom policy module and ensure that it works in enforcing mode.

All activities in this chapter require the results of the previous activities. You are not required to perform all of them at once, but it’s certainly easier. If you must pause between activities, ensure you can continue using the same RHEL machine.

Before you Begin

You need a RHEL machine to which you have either root password or unrestricted sudo, and also access to Red Hat Enterprise Linux package repositories, to install any missing packages. That machine must be configured with:

  • SELinux enabled and enforced, using the targeted policy.

  • The setools-console package.

  • The policycoreutils-python-utils package.

  • The selinux-policy-devel package.

  • The rpm-build package.

These instructions were tested on RHEL 9.3 but should work with minimal change on older releases (since RHEL 7) and newer releases.

Instructions

  1. This activity is a continuation of the previous activity. It requires that the third-party application be already installed as a system service, with a custom policy which runs the application in a permissive domain and allows network connections to remote HTTP servers and access to system files.

    1.1. Check that the test application service is still active. It will terminate when it’s done retrieving the weather from all cities, and in that case you need to restart it. Remember to record the timestamp so you can filter audit events.

    $ systemctl is-active testapp
    inactive
    $ TIME=$(date +%T) ; sudo systemctl restart testapp

    1.2. Find the PID of the test application service and check that it’s running in a permissive domain:

    $ systemctl show --property MainPID --value testapp
    39521
    $ ps -Z 39521
    LABEL                               PID TTY      STAT   TIME COMMAND
    system_u:system_r:testapp_t:s0    39521 ?        S      0:00 /usr/local/sbin/testapp -d
    $ sudo semanage permissive -l | grep -c testapp_t
    1

    1.3. If you need, enter the directory with the custom policy module from the previous activity:

    $ cd selinux-testapp
    $ ls
    noarch  testapp.fc  testapp.if  testapp.pp  testapp.sh  testapp.te  testapp_selinux-1.0-1.el9.src.rpm  testapp_selinux.8  testapp_selinux.spec  tmp
  2. Review what should be the latest AVC error from the third-party application.

    2.1. The AVC error relates to another system configuration file:

    $ sudo ausearch -m AVC -x /usr/local/sbin/testapp --start $TIME --just-one
    ----
    time->Thu Jul 25 20:01:13 2024
    type=PROCTITLE msg=audit(1721937673.666:665): proctitle=2F7573722F6C6F63616C2F7362696E2F74657374617070002D64
    type=SYSCALL msg=audit(1721937673.666:665): arch=c000003e syscall=262 success=yes exit=0 a0=ffffff9c a1=7f9e947bbab9 a2=7f9e93cda340 a3=0 items=0 ppid=1 pid=6537 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="testapp" exe="/usr/local/sbin/testapp" subj=system_u:system_r:testapp_t:s0 key=(null)
    type=AVC msg=audit(1721937673.666:665): avc:  denied  { getattr } for  pid=6537 comm="testapp" path="/etc/resolv.conf" dev="vda4" ino=67109244 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:net_conf_t:s0 tclass=file permissive=1

    Did you know that, in Linux, it’s not the operating system, but it’s user applications, that perform DNS searches? All those details are hidden from developers by library code.

    2.2. Find an interface that matches the AVC error:

    $ sudo ausearch -m AVC -x /usr/local/sbin/testapp --start $TIME --just-one | audit2allow -R
    ...
    #============= testapp_t ==============
    sysnet_read_config(testapp_t)

    Remember that you should review the comments on the source of this interface and also expand it to review all allow statements it generates. In the interest of time, let’s move on, knowing this is the expected interface for network clients that perform DNS queries.

    2.3. Add the interface to the end of the testapp.te file:

    ...
    logging_send_syslog_msg(testapp_t)
    
    miscfiles_read_localization(testapp_t)
    
    # Before this line, all rules were auto-generated
    
    kernel_read_system_state(testapp_t)
    
    miscfiles_read_certs(testapp_t)
    miscfiles_search_generic_cert_dirs(testapp_t)
    
    allow testapp_t self:tcp_socket { connect create getattr getopt setopt };
    allow testapp_t self:udp_socket { connect create getattr setopt };
    corenet_tcp_connect_http_port(testapp_t)
    
    # Before this line, all rules come from the previous activities
    
    sysnet_read_config(testapp_t)
  3. Verify that the updated policy module fixes all remaining AVC errors from the third-party application.

    3.1. Build and reload the policy module:

    $ sudo ./testapp.sh
    Building and Loading Policy
    + make -f /usr/share/selinux/devel/Makefile testapp.pp
    Compiling targeted testapp module
    Creating targeted testapp.pp policy package
    ...
    + exit 0

    3.3. Restart the test application, recording a timer so you can filter AVC errors from before and after the operation, and check that there are no more AVC errors related to UDP and TCP sockets:

    $ TIME=$(date +%T) ; sudo systemctl restart testapp
    $ sudo ausearch -m AVC -x /usr/local/sbin/testapp --start $TIME | grep -c resolv.conf
    <no matches>
    0

    Notice the string "no matches" in the previous output. It means that the ausearch command found no audit events matching the search criteria. There was nothing for the grep command to search in.

  4. Remove the permissive flag and perform a final check for no remaining AVCs.

    4.1. Delete the following line from the testapp.te file:

    permissive testapp_t;

    4.2. Build and reload the policy module one last time:

    $ sudo ./testapp.sh
    Building and Loading Policy
    + make -f /usr/share/selinux/devel/Makefile testapp.pp
    Compiling targeted testapp module
    Creating targeted testapp.pp policy package
    ...
    + exit 0

    4.3. Restart the test application, recording a timer so you can filter AVC errors from before and after the operation, and check that there are no more AVC errors:

    $ TIME=$(date +%T) ; sudo systemctl restart testapp
    $ sudo ausearch -m AVC -x /usr/local/sbin/testapp --start $TIME
    <no matches>

    4.4. Ensure the test application service is not running in a permissive domain anymore:

    $ sudo semanage permissive -l | grep -c testapp_t
    0

    Good work! Now the third-party applications runs fully confined by SELinux.

  5. Review the generated RPM package containing the policy module, for redistribution to other machines running the third-party application.

    5.1. The autogenerated script testapp.sh already includes commands to build RPM packages and source RPM packages. We are interested in the RPM package:

    $ ls noarch
    testapp_selinux-1.0-1.el9.noarch.rpm

    5.2. Review the contents of the generated RPM package:

    $ rpm -ql -p noarch/testapp_selinux-1.0-1.el9.noarch.rpm
    /usr/share/man/man8/testapp_selinux.8.gz
    /usr/share/selinux/devel/include/contrib/testapp.if
    /usr/share/selinux/packages/testapp.pp

    Notice that it includes the binary policy module, its interface definition file (in case it provides reusable interfaces for other modules), and an autogenerated manual page which is not very useful as-is but could be if you add comments to your interface file.

    5.3. Review the scriptlets of the generated RPM package:

    $ rpm -q --scripts -p noarch/testapp_selinux-1.0-1.el9.noarch.rpm
    postinstall scriptlet (using /bin/sh):
    semodule -n -i /usr/share/selinux/packages/testapp.pp
    if /usr/sbin/selinuxenabled ; then
        /usr/sbin/load_policy
    
    restorecon -R /usr/local/sbin/testapp;
    restorecon -R /var/run/testapp.pid;
    
    fi;
    exit 0
    ...

    The postuninstall scriplet was omitted from the previous output, but it just reverses the effects of the postinstall scriptlet: it unloads the binary policy module and relabels all files affected by it.

Next Steps

This is the final activity of this course. For a real-world application you would require more iterations, as you exercise different features of the application and review the AVCs errors it generates, but you would still follow this same process and grow your policy module incrementally.