Add an extra layer of security with systrace


Author: Manolis Tzanidakis

Niels Provos’ Systrace is a utility that monitors and controls what an application can access on a system by creating and enforcing access policies for system calls. For the Linux crowd, it’s something like the US National Security Agency’s SE Linux, but it’s more flexible and, if used properly, it can improve a system’s overall security by “sandboxing” untrusted applications and users.

Systrace is included by default in NetBSD and OpenBSD, and the project lists ports for Mac OS X, FreeBSD, and Linux on its home page — though the Mac OS X port is not being maintained at the moment.

Systrace acts as a wrapper to the actual application. It intercepts the system calls made by the application, processes them through the kernel using the /dev/systrace device, and then handles the system calls according to your policies.

You can use Systrace to restrict a daemon’s access to the system by defining which files it can access and how (such as read-only), and which port it can bind to. Also, if a daemon doesn’t support privilege separation, you can avoid running it as root the whole time and keeping setuid and setgid binaries on the system. It’s obvious how this can enhance the security of an untrusted daemon, or at least minimize the damage on a system if someone manages to exploit it.

Applications that access the Internet, such as Web browsers, can also be “sandboxed” in order to eliminate common threats such as DNS spoofing (by permitting the browser to access only your trusted internal DNS server) or, even worse, privilege elevation by buffer overflows. In the case of multi-user systems — especially ones with remote access — you can further restrict a user’s access to only his home directory and allow him to run only specific commands using stsh, the Systrace shell.

You can also use Systrace for quick auditing and debugging of applications. For example, you can check why a binary-distributed application, built without debug flags, crashes without any error messages. You may discover that it tries to access a non-existing file. Of course, for proper code debugging, you need a real debugger, such as the GNU Debugger (GDB).


You can create policies interactively (using a “notification user agent” such as xsystrace or in text mode by using the -t option) or obtain them from a policy file. This image on the project’s home page shows an example of interactive generation of access policies.

Systrace policy files are named after the full path of a program, replacing slashes with underscores (such as usr_sbin_named), and are stored in $HOME/.systrace, or /etc/systrace for system-wide access. The systrace man page includes a detailed explanation of policy grammar. Reading it is mandatory in order to fully understand and use Systrace correctly. OpenBSD includes examples of fully working policies for named and lpd in /etc/systrace, which are also worth reading.

The easiest way to get started with Systrace and create a policy is to run it with the -A option, leave the program running for some time, and have it create a policy automatically. Note that this automatic process assumes the program being monitored is behaving correctly and does exactly what it’s supposed to do. Systrace won’t protect you if your system is compromised already and an attacker has modified a program binary. You should apply policies only on trusted binaries and always examine carefully the automatically generated ones before putting them into production.

You can also generate a policy interactively simply by running a command such as this (replace BINARY with the full path of the program):

/bin/systrace BINARY

If you don’t have X installed on your system, add the -t argument to have Systrace ask questions about what to do on every system call the program makes. The interactive process makes it easier to check whether the system is compromised or the binary is modified. For example, OpenVPN should bind to only UDP port 1194 and not TCP port 6666. That might indicate an SSH daemon installed by an attacker to gain shell access.


This article offers an introduction to Systrace and by no means replaces the man pages or other documentation, but to get you started, I’ll provide an example of an application on a network daemon, based on my previous article, Creating secure wireless access points with OpenBSD and OpenVPN.

Change the paths and the filenames below to match your setup. Start by running OpenVPN wrapped under Systrace, and create a policy automatically using the -A option:

/bin/systrace -A /usr/local/sbin/openvpn --config /etc/openvpn/server.conf

Leave it running for a long time and access the wireless network to perform regular network operations such as file transfers and browsing the Web. When you’re ready, issue this command to kill the OpenVPN daemon:

kill 'cat /var/openvpn/pid'

Now you should have a file named usr_local_sbin_openvpn in $HOME/.systrace. You should be able to understand the syntax after you read the systrace man page. I’ll explain the “Emulation: native” part on the first line, since the man page doesn’t mention it. It shows which Application Binary Interface (ABI) this policy is for. Since the BSDs have emulation for other Unix-like ABIs, including Linux, it should be possible to apply a Systrace policy on a Linux binary running under emulation, such as acroread.

Now, make your policy safe for upgrades. You should notice a line like this:

native-fsread: filename eq "/usr/lib/" then permit

Replace that line with this:

native-fsread: filename match "/usr/lib/*" then permit

This allows your policy to work correctly if libcrypto is updated on your next upgrade.

After reviewing your policy file, move it to /etc/systrace and run OpenVPN again, enforcing that policy with:

/bin/systrace -a /usr/local/sbin/openvpn --config /etc/openvpn/server.conf

Check /var/log/messages for any errors or possible policy violations. If something goes wrong or the daemon doesn’t come up, replace the previous command with:

/bin/systrace -a -e /usr/local/sbin/openvpn --config /etc/openvpn/server.conf

This logs to stderr instead of syslog. You can then find the problem and correct your policy file.

Make sure everything works correctly, then replace the OpenVPN lines that I showed you how to create in the previous article in your /etc/rc.local file:

if [ -x /usr/local/sbin/openvpn ]; then
/usr/local/sbin/openvpn --config /etc/openvpn/server.conf


if [ -x /usr/local/sbin/openvpn -a -e /etc/systrace/usr_local_sbin_openvpn ]; then
/bin/systrace -a /usr/local/sbin/openvpn --config /etc/openvpn/server.conf

It’s not possible to have a 100% secure and unbreakable system, but multiple layers of security make it harder for an attacker to gain unauthorized access, and Systrace is a good tool for adding one such layer.


  • Security