CerbNG Installation Guide

This is the test version of CerbNG. There are many bugs yet, that's for sure. We would be grateful for helping us find them, since stability and security of CerbNG are our main concerns. Because of current state of CerbNG, deploying it on a production machine is a twisted idea.

CerbNG is built from two basic parts: the kernel module and the rule compiler. Compilation and installation is easy and straightforward.

Let's get started with building and installing Cerb:

# cd <path to cerb sources>
# make clean all install

If everything went as expected, we are ready to load the module into running kernel. After installation you can find cerb.ko in standard directory for modules: /modules. Let's load the module:

# kldload cerb 

Line like "CerbNG vX.Y loaded" should appear in the logs. After loading the module, Cerb specific sysctls should be present in the system. One can list them running:

# sysctl cerb

Unless you are sure, that Cerb is running fine on your system, you should run the regression test suite. The tests should be already compiled.

The tests can be run like this:

# cd /path/to/cerb-ng/test
# cbctl -f optests/optests.cb
# ./test.sh

The tests will check most parts of the kernel module and the compiler (cbctl). Three phases of tests will be run: kcerb-test, op-test and recompile. Checks performed during kcerb-test and op-test are critical. If any of them fails, you should definitely turn off Cerb, and send all test results to Cerb developers. There is a danger of causing kernel panic if you proceed despite failure of the tests. Tests performed during recompile are not critical. Their failure might indicate a bug under some specific circumstances. But yes - any bugs cropping up during this phase, tell you that you can be unable to compile your rules. In this case also, please send us feedback on your findings using output from test.sh with -v option. During kcerb-test, CerbNG's immunity for unexpected and michevious user behaviour is tested. For example loading the rules by unauthorized user, loading invalid rules, ussing illegal memory addresses. If such an error occurs, an error message is written to the console. In general you should not be concerned by number of messages printed to the console, but an error message about test failing printed in the test summary.

Now let's compile and load the rules:

# cbctl -f /path/to/cerb-ng/examples/main.cb 

File main.cb loads most example policies you can find in the examples directory, ie.:

noexec-by-group.cb, openssh.cb, passwd.cb, ping.cb, su.cb,
degrade-unknown-sugids.cb, restricted-debug.cb, restricted-link.cb,

Short description of selected policies:

Controls sshd(8) (if sshd is running when the policy is being loaded, it has to be restarted). The policy degrades sshd privileges after it's been started to UID and GID for user/group sshd. CerbNG elevates sshd rights for performing privileged operations.
Controls passwd(1). Similarly to openssh.cb privileges of the passwd process are changed to those of user running this program. Privileges are degraded regardless of the setuid bit.
ping.cb, su.cb
Similar privilege degradation examples.
Noexec for all users but root and members of exec group. Additionally environment variables with names beginning with LD_ are verified.
All setuid/setgid files, which are not controlled by Cerb are denied elevated privileges and run with credentials of user performing the execve(2) syscall.
Using ptrace(2) and ktrace(2) syscalls will be limited to root user and members of group debug.
Non-root users will be denied the right to create hard links to other users' files.
All execve(2) calls performed by non-privileged users will be logged.

Loaded rules can by listed by:

# sysctl cerb.syscalls.show=<slot number>

If we load the rules using cbctl, they are placed by default in slot number 0. One can change it using option -r for cbctl. There are 3 slots by default. This value is defined in kcerb/cerb_globals.h as CB_MAXTABS_G.

If rules are loaded into several slots, they can be switched on the fly by:

# sysctl cerb.rules.set=<new active slot number> 

To test correctness of the rule file, or just list the rules to check how the compiler sees them (options -l and -t), one should use option -a for cbctl.

Loading rules to slot causes overwriting of the rules present in the slot. To remove rules from a slot, run:

# sysctl cerb.rules.rm=<slot number>

To freeze Cerb rule state, ie. prevent new rules from loading, switching active slots etc, set sysctl cerb.rules.lock to 1. After this sysctl is set to 1, it cannot be reset back to 0 without system restart. Similar effect can be obtained setting kern.securelevel sysctl to 1 or more.

Creating Cerb rules securing critical applications we started from the only sensible point of degrading all credentials and rising them only for executing possibly shortest slices of privileged operations. Additionally every context when privileges are elevated, is logged by default. Because of the above assumption one can intercept only those syscalls which are used by the application, possibly improving overall system performance.

Also by default (it's still a test version of Cerb) sysctl cerb.misc.off_on_error is set to 1, which means, that when an runtime error is encountered Cerb is automatically disabled. In production environment, this sysctl should be set to 0, which causes return of error value to userland when such error is found. It stops circumventing Cerb protections by playing with syscalls which are mishandled by Cerb. Additionally Cerb using trace mechanism will list erroneous rule when such an error is found.

Because this is still a test version, sysctl cerb.misc.off_on_error is set to 1, which makes Cerb unload when a runtime error is encountered. In production environment this sysctl should be set to 0, to prevent messing with the rules to find an error condition in the rules and subsequently circumvent Cerb protections. On error Cerb will display rule containing the error when it occurs using trace() mechanism.

When preparing custom rules for Cerb, one should use helpful macro CB_PREPARE(), defined in addons.cbh, which can display all syscall arguments using %A format character. Moreover this macro exploits Tomasz Pilat's idea, which was running the syscall with degraded credentials and when it fails, restarting it with UID = 0. If it succeeds then, appropriate message is logged. It doesn't solve all the problems. Programs often rely on values returned by syscalls for checking their rights (like getuid(2)), which in such scenarios are best intercepted and rewritten to return 0 value.