IPv6 Labeled Networking with CALIPSO

With the release of Linux v4.8 and NetLabel Tools v0.30.0 we now have a working implementation of RFC5570, aka CALIPSO, on Linux. This post will demonstrate how to configure NetLabel/CALIPSO on a SELinux based system through the use of a simple example which should be easy to duplicate on any single system. The example is based on Fedora Rawhide, but the basic commands should apply to other distributions as well.

I’m going to focus on the CALIPSO configuration in this post, and not the NetLabel and SELinux per-packet access control basics. If you need a refresher, please see the posts below:

The first step is to ensure that the kernel and netlabel_tools package both support CALIPSO; Linux v4.8 (or greater) and netlabel_tools v0.30.0 (or greater) are required.

# uname -r
4.10.0-0.rc0.git9.1.2.secnext.fc26.x86_64
# netlabelctl -V
NetLabel Control Utility, version 0.30.0

While not required for CALIPSO configuration or use, this example does make use of a simple test/debugging tool, getpeercon_server, which can be found at the link below:

You can download and build the tool with the following commands; although you may also need to install some additional packages to build the getpeercon_server tool, the libselinux-devel package is one likely example.

# git clone https://github.com/pcmoore/misc-getpeercon_server.git
Cloning into 'misc-getpeercon_server'...
remote: Counting objects: 61, done.
remote: Total 61 (delta 0), reused 0 (delta 0), pack-reused 61
Unpacking objects: 100% (61/61), done.
# cd misc-getpeercon_server/src
# make
cc  -g -o getpeercon_server -lselinux getpeercon_server.c

Once you have verified the necessary packages, and built getpeercon_server, it is time to start configuring NetLabel/CALIPSO in the kernel. The first step is to define a CALIPSO Domain Of Interpretation (DOI). The CALIPSO DOI provides a context in which the CALIPSO labels can be interpreted by all the nodes in the network, in this example we are using a DOI value of 2, but if you are deploying your Linux system in an existing CALIPSO network, you will need to contact your network/security administrator to determine which CALIPSO DOI is appropriate (NOTE: Linux supports multiple CALIPSO DOIs).

# netlabelctl calipso add pass doi:2
# netlabelctl -p calipso list
Configured CALIPSO mappings (1)
 DOI value : 2
   mapping type : PASS_THROUGH

After we have defined the CALIPSO DOI, we need to instruct the kernel to use this DOI to label outgoing traffic. In this example we are going to assign CALIPSO labels to all of our IPv6 localhost traffic (::1). You will note that the commands below assume a default NetLabel mapping configuration.

# netlabelctl -p map list
Configured NetLabel domain mappings (1)
 domain: DEFAULT (IPv4/IPv6)
   protocol: UNLABELED
# netlabelctl map del default
# netlabelctl map add default address:0.0.0.0/0 protocol:unlbl
# netlabelctl map add default address:::0/0 protocol:unlbl
# netlabelctl map add default address:::1 protocol:calipso,2
# netlabelctl -p map list
Configured NetLabel domain mappings (2)
 domain: DEFAULT (IPv4)
   address: 0.0.0.0/0
    protocol: UNLABELED
 domain: DEFAULT (IPv6)
   address: ::1/128
    protocol: CALIPSO, DOI = 2
   address: ::/0
    protocol: UNLABELED

We do not need to worry about incoming traffic, once the Linux Kernel knows about a CALIPSO DOI it will automatically interpret the CALIPSO labels on any traffic that enters the system.

That completes the configuration steps for this example, the next step is to verify a basic unlabeled IPv4 connection:

Terminal A:

# id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
# nc 127.0.0.1 5000
hello!

Terminal B:

# cd misc-getpeercon_server/src
# ./getpeercon_server 5000
-> running as unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
-> creating socket ... ok
-> listening on TCP port 5000 ... ok
-> waiting ... connect(127.0.0.1,NO_CONTEXT)
hello!
-> connection closed

We can see that we were able to connect to the getpeercon_server over IPv4 localhost (127.0.0.1) and that our test tool registered it as an unlabeled connection (NO_CONTEXT). Let’s try this again, this time using IPv6’s localhost address (::1):

Terminal A:

# id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
# nc ::1 5000
hello again!

Terminal B:

# cd misc-getpeercon_server/src
# ./getpeercon_server 5000
-> running as unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
-> creating socket ... ok
-> listening on TCP port 5000 ... ok
-> waiting ... connect(::1,system_u:object_r:netlabel_peer_t:s0)
hello again!
-> connection closed

Here we see CALIPSO in action as it labeled our IPv6 localhost connection with our effective MLS/MCS label (s0). We can play with this a bit more using the runcon command to connect to the getpeercon_server with different MLS/MCS labels (NOTE: I’m using the SELinux “targeted” policy so I’m restricted to a single sensitivity level in this example (s0), the SELinux “mls” policy supports multiple sensitivity levels).

Terminal A:

# id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
# runcon -l s0:c0 -- id -Z
unconfined_u:unconfined_r:unconfined_t:s0:c0
# runcon -l s0:c0 -- nc ::1 5000
test #1
# runcon -l s0:c1 -- nc ::1 5000
test #2
# runcon -l s0:c1,c2,c5,c7.c300 -- nc ::1 5000
test #3
# runcon -l s0:c1.c300,c400.c500 -- nc ::1 5000
test #4

Terminal B:

# cd misc-getpeercon_server/src
# ./getpeercon_server 5000
-> running as unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
-> creating socket ... ok
-> listening on TCP port 5000 ... ok
-> waiting ... connect(::1,system_u:object_r:netlabel_peer_t:s0:c0)
test #1
-> connection closed
-> waiting ... connect(::1,system_u:object_r:netlabel_peer_t:s0:c1)
test #2
-> connection closed
-> waiting ... connect(::1,system_u:object_r:netlabel_peer_t:s0:c1,c2,c5,c7.c300)
test #3
-> connection closed
-> waiting ... connect(::1,system_u:object_r:netlabel_peer_t:s0:c1.c300,c400.c500)
test #4
-> connection closed

That’s all there is to configuring NetLabel/CALIPSO on Linux. If you have any questions you know how to contact me. Good luck!