Transitioning to Secmark

UPDATE: Laszlo Beres has been kind enough to provide a Hungarian translation, although the link appears to be dead in 2019.

Back in the 2.6.18 timeframe James Morris developed a replacement for the now-named “compat_net” SELinux access controls which filtered packets based on network attributes, the replacement was called Secmark. Secmark was introduced to fix two major issues with the aging compat_net access controls; the first problem being that they were not as flexible as iptables/netfilter rules and the second, very related problem, was that the compat_net controls were slow and likely would always be slow due to fundamental design issues. The solution to both these problems was to leverage the existing iptables/netfilter mechanism to label packets and replace the crude packet matching mechanisms of the compat_net design. As of 2.6.29 release the compat_net functionality is deprecated and patches completely removing it from the kernel have been merged into the 2.6.30 release candidates.

Secmark works by using iptables/netfilter to assign a label, or “security mark” aka Secmark, to specific packets which are later used by SELinux when the per-packet access controls are applied. This approach allows administrators to match packets based not only on the existing compat_net attributes such as port and host, but also any network attribute supported by iptables/netfilter, including stateful connection matching. The article by James Morris (linked above) is an excellent introduction to Secmark and for those of you looking to get the most out of the new functionality I encourage you to head over there first. What I hope to do here is not duplicate James’ article, but rather provide a quick guide on how to duplicate basic compat_net functionality using the new Secmark controls.

Before we start it is important to first identify if the system you are using has Secmark enabled, you can do this by looking at the value in “/selinux/compat_net”. If the file does not exist on your system and you have SELinux enabled then you are either using a very old kernel which does not support Secmark, or a new kernel (2.6.30 or greater) that only supports Secmark. However, for those systems that do have the file, if the contents are “0” then Secmark is enabled, otherwise you are still using the older compat_net controls. If you want to enable Secmark you can do so by writing a “0” to the file but you may first want to ensure that your SELinux policy and iptables/netfilter toolchain are up to date and provides Secmark support.

# cat /selinux/compat_net
0

The first step in using Secmark is to define a new SELinux label for the network traffic we are labeling and write the corresponding SELinux policy to handle the newly labeled traffic. This highlights the major difference between compat_net and Secmark: with the compat_net controls you assign labels to ports and hosts, but with Secmark you label the packets themselves. The second step is to determine which network attributes you want to match on when you are labeling packets. Both compat_net and Secmark can match on any combination of port and host so you should be able to transition all of your existing compat_net rules to Secmark; the only difference here is that with compat_net each port and host entry received its own label but with Secmark each combination of port and host receives a label. In the example below, we are going to configure Secmark to label SSH packets from host foo.lan with the label “foo_ssh_packet_t” and allow it to connect to the SSH daemon running on our local system with label “sshd_t”. Careful observers will note that I currently have the MLS policy installed but the same procedure will work equally well with the default targeted policy.

Since we using a custom SELinux label the first thing we need to do is write a SELinux policy module to define the new type and the policy allowing this type to be received by the SSH daemon over the network. The policy we are using is shown below:

# policy header
policy_module(secmark_example,0.1.0)
gen_require(`
        type sshd_t;
')

# our new secmark packet type
type foo_ssh_packet_t;

# allow sshd_t to receive our new packet type
allow sshd_t foo_ssh_packet_t:packet recv;

We can quickly compile and install our new policy module with the following commands:

# cp /usr/share/selinux/devel/Makefile .
# make
Compiling mls secmark_example module
/usr/bin/checkmodule:  loading policy configuration from tmp/secmark_example.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 10) to 
tmp/secmark_example.mod
           
Creating mls secmark_example.pp policy package
rm tmp/secmark_example.mod tmp/secmark_example.mod.fc
# ls
Makefile            secmark_example.if  secmark_example.te
secmark_example.fc  secmark_example.pp  tmp
# semodule -i secmark_example.pp
# semodule -l | grep secmark_example
secmark_example 0.1.0

The next and final step is to setup the iptables/netfilter Secmark rules to label the packets correctly:

# host foo.lan
foo.lan has address 192.168.0.16
# iptables -t mangle -A INPUT -p tcp --src 192.168.0.16 --dport 22 -j SECMARK --selctx system_u:object_r:foo_ssh_packet_t:s0
# iptables -t mangle -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source          destination

Chain INPUT (policy ACCEPT)
target     prot opt source          destination
SECMARK    tcp  --  foo.lan         anywhere     tcp dpt:ssh SECMARK selctx system_u:object_r:foo_ssh_packet_t:s0

Chain FORWARD (policy ACCEPT)
target     prot opt source          destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source          destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source          destination

At this point we are finished, packets coming from foo.lan and destined for port TCP/22 on our system will be labeled as “foo_ssh_packet_t” with SELinux providing assurance that only “sshd_t” can read “foo_ssh_packet_t” packets. You can verify this quite easily be removing the allow rule from the custom SELinux policy and watching SSH traffic from foo.lan stop, you will also see new SELinux AVC denial messages with the “foo_ssh_packet_t” type.

One final note, remember that with modern Linux Kernels there are two types of SELinux security labels assigned to a packet, the Secmark labels described here and the peer labels described previously. These two types of packet labels operate differently and subject to their own, independent set of SELinux access controls. The Secmark packet labels are used to represent the network attributes of a packet such as IP addresses and ports, while the peer packet labels are used to represent the security attributes of the sender such as the SELinux label of the process which generated the network packet.