Exercise 2.3 - Creating Policy Rules for Network Access

Return to Workshop

Exercise Description

In this exercise, we will continue the modification of the generic application polocy to allow network traffic to be generated by the test application. There are several rules that will be needed, to handle a variety of AVC denials.

Section 1: HTTP Port Connectivity

Step 1: Check for AVC denials

Let’s restart our app, to get an updated list of denials.

sudo systemctl restart testapp

Let’s start with one of the denials that will be present in the audit search results, related to connecting on TCP port 80:

sudo ausearch -m AVC -ts recent | grep 'dest=80'
type=AVC msg=audit(1553181380.518:2594): avc:  denied  { name_connect } for  pid=31221 comm="testapp" dest=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:http_port_t:s0 tclass=tcp_socket permissive=1

You may receive more than one of these messages, depending on timing.

Step 2: Interpret AVC messages

In the message above, you can see that the system has attempted to connect to a remote host on port 80. This is something that we want to allow. So, let’s search for an interface that would allow this kind of connection. Since networking is controlled by the kernel, we will search in the kernel directory for interface files related to networking:

ls /usr/share/selinux/devel/include/kernel/*.if | grep net
/usr/share/selinux/devel/include/kernel/corenetwork.if
/usr/share/selinux/devel/include/kernel/unlabelednet.if

Since this isn’t an unlabeled network, let’s look in corenetwork.if. Since this is a name_connect action, let’s look for connect and http. Using those criteria, we find:

########################################
## <summary>
##      Make a TCP connection to the http port.
## </summary>
## <param name="domain">
##      <summary>
##      Domain allowed access.
##      </summary>
## </param>
#
interface(`corenet_tcp_connect_http_port',`
        gen_require(`
                type http_port_t;
        ')

        allow $1 http_port_t:tcp_socket name_connect;
')

Step 3: Add the interface

So, as before, add an interface to your testapp.te file, using a line like this:

corenet_tcp_connect_http_port(testapp_t)

And compile your policy, and restart the service:

sudo ./testapp.sh
sudo systemctl restart testapp

…​and last, check to see if there is still an AVC denial:

sudo systemctl restart testapp
sudo ausearch -m AVC -ts recent | grep 'dest=80' | wc -l
0

Yes! Another AVC denial fixed.

If you ausearch still shows AVCs above, you can alternatively limit the query to the testapp’s current PID; this should confirm the policy is in effect:

sudo ausearch -m AVC -ts recent -p $(systemctl show testapp --property=MainPID | cut -d= -f2) | grep 'dest=80' | wc -l

Section 2: Socket Permissions

Step 1: Take a Shortcut!

Now, we’re going to move on, temporarily, from interfaces to allow/deny settings, in the testapp.te file. We have several AVC denials around TCP and UDP socket system calls, that SELinux is blocking.

The AVCs in question are here (there will likely be more, on your host, but they are all in the same sategory):

sudo ausearch -m AVC -ts recent | egrep 'tcp|udp'
type=AVC msg=audit(1553190979.189:2931): avc:  denied  { create } for  pid=2042 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1553190979.189:2932): avc:  denied  { connect } for  pid=2042 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1553190979.379:2933): avc:  denied  { getopt } for  pid=2042 comm="testapp" laddr=10.0.2.5 lport=60848 faddr=5.9.243.187 fport=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1553190979.379:2934): avc:  denied  { getattr } for  pid=2042 comm="testapp" laddr=10.0.2.5 lport=60848 faddr=5.9.243.187 fport=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1553191083.531:2965): avc:  denied  { create } for  pid=2042 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1
type=AVC msg=audit(1553191083.531:2966): avc:  denied  { connect } for  pid=2042 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1
type=AVC msg=audit(1553191083.548:2967): avc:  denied  { getattr } for  pid=2042 comm="testapp" path="socket:[314409]" dev="sockfs" ino=314409 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1

Step 2: Let audit2why interpret for you

Next, let’s ask the system to convert an AVC to English, for us:

sudo ausearch -m AVC | egrep 'udp' | head -1 | audit2why
type=AVC msg=audit(1553112150.906:909): avc:  denied  { create } for  pid=9772 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1

	Was caused by:
		Missing type enforcement (TE) allow rule.

		You can use audit2allow to generate a loadable module to allow this access.

You can see that, in this case, audit2why wasn’t significantly helpful, other than telling us that we need to add a new allow statement to our testapp.te file.

Step 3: Use audit2allow to suggest changes

So, now we’ll take the advice that audit2why gave us, and let audit2allow generate some policy allow statements. You always have to be careful with audit2allow, since it will generate rules for anything that you feed it, and won’t necessarily pick the most optimal solution.

sudo ausearch -m AVC -ts recent | egrep 'tcp|udp' | audit2allow
#============= testapp_t ==============
allow testapp_t self:tcp_socket { connect create getattr getopt setopt };
allow testapp_t self:udp_socket { connect create getattr setopt };

These rules look good, so go ahead and add them to your testapp.te file.

The file should now look like this:

cat ~/src/policy/testapp.te
policy_module(testapp, 1.0.0)

########################################
#
# Declarations
#

type testapp_t;
type testapp_exec_t;
init_daemon_domain(testapp_t, testapp_exec_t)

permissive testapp_t;

type testapp_var_run_t;
files_pid_file(testapp_var_run_t)

########################################
#
# testapp local policy
#
allow testapp_t self:process { fork };
allow testapp_t self:fifo_file rw_fifo_file_perms;
allow testapp_t self:tcp_socket { connect create getattr getopt };
allow testapp_t self:udp_socket { connect create getattr };
allow testapp_t self:unix_stream_socket create_stream_socket_perms;

manage_dirs_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_lnk_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
files_pid_filetrans(testapp_t, testapp_var_run_t, { dir file lnk_file })

corenet_tcp_connect_http_port(testapp_t)

domain_use_interactive_fds(testapp_t)

files_read_etc_files(testapp_t)

kernel_read_system_state(testapp_t)

logging_send_syslog_msg(testapp_t)

miscfiles_read_localization(testapp_t)

Keeping things in alphabetic order doesn’t make any difference to SELinux, but makes the file easier to read.

Step 4: Recompile and reload the policy

Now, let’s recompile the policy, and reload it into memory.

sudo ./testapp.sh

Step 5: Restart the application

To see if that fixed the problem, let’s restart the application:

sudo systemctl restart testapp

…​and see if there are any AVC messages about TCP or UDP:

sudo ausearch -m AVC -ts recent -p $(systemctl show testapp --property=MainPID | cut -d= -f2) | egrep 'tcp|udp' | wc -l
0

Fantastic! More AVC denials stomped.

End Result

At this point, we’re down to only a few more AVC denials to handle. Hopefully, you’re getting a handle on how to find appropriate interfaces and allow rules, for SELinux.


Workshop Details

Domain Red Hat Logo
Workshop
Student ID

Return to Workshop