LinuxCon NetLabel Presentation

I’m more than a little late posting this, but below you’ll find a copy of the NetLabel presentation I gave at this past year’s LinuxCon. Sure, it is old news, but if you ever wanted to know what NetLabel does and why it exists in the first place this is still good stuff.

  • Presentation: PDF

Enabling the Network Ingress/Egress Controls

There have been quite a few questions lately about how to enable the SELinux network ingress/egress controls on recent Fedora releases. This is good because it means people actually want to use this stuff, but it is also bad because it tells me that I haven’t done a very good job explaining how to use them. Actually, looking back on this site I see that while I’ve written about the ingress/egress controls I haven’t described how you would enable them on a modern Linux distribution. Ooops.

However, fear not faithful readers and confused administrators, for this is post shall explain, in four easy steps, how to enable the SELinux network ingress/egress controls. Without further ado, let’s begin …

The first step is to ensure you have the netlabel_tools package installed. This is necessary because the netlabel_tools package contains the netlabelctl application which we will be using to configure NetLabel in the final step. Using yum you should be able to install netlabel_tools with the following command:

# yum install netlabel_tools

After you have installed the netlabel_tools package the next step is to ensure you have a SELinux policy module loaded which defines at least two new types: one to be assigned to a network interface and another to be assigned to network traffic. To be honest, you’ll probably want to create more than just those two types but that will be dependent on your particular configuration; for more information I suggest you look at my original post on the ingress/egress controls where I go into more detail on the policy aspect of these controls. Regardless of what you decide, for the example here I will be using “foo_netif_t” for the network interface type and “foo_peer_t” for the network traffic type.

With the SELinux policy module loaded and our new types defined, it is time to assign our new network interface type to an interface on the system. We use the semanage tool to manipulate the SELinux types assigned to network interfaces, in order to add a type to an interface, “eth2” in this example, we use the following command:

# semanage interface -a -t foo_netif_t eth2

We can verify that the interface is assigned the correct type with the following command:

# semanage interface -l
SELinux Interface              Context
eth2                           system_u:object_r:foo_netif_t:s0

Now that we have the network interface labeled the final step is to setup some form of peer labeling on the network. For many of you not using CIPSO or labeled IPsec, this means configuring NetLabel’s static/fallback labels. In this example, we are going to configure all of the IPv4 and IPv6 traffic entering the system via “eth2” as having the “foo_peer_t” label; we do this with the following command:

# netlabelctl unlbl add interface:eth2 address:0.0.0.0/0 
label:system_u:object_r:foo_peer_t:s0
# netlabelctl unlbl add interface:eth2 address:::/0 
label:system_u:object_r:foo_peer_t:s0

We can verify the configuration with the following command:

# netlabelctl -p unlbl list
Accept unlabeled packets : on
Configured NetLabel address mappings (2)
 interface: eth2
   address: 0.0.0.0/0
    label: "system_u:object_r:foo_peer_t:s0"
   address: ::/0
    label: "system_u:object_r:foo_peer_t:s0"

At this point the SELinux network ingress/egress control should be up and running on your system. As a reminder, you’ll want to be sure to have all the right allow rules for these new controls in your SELinux policy as simply adding the types without the allow rules could result in a total loss of network access; because of this I recommend you do this with SELinux in permissive mode until you are comfortable with how the system operates.

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.