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.
UPDATE: Laszlo Beres has been kind enough to provide a Hungarian translation.