AppArmor is an LSM based on the MAC model that confines applications to a limited set of resources. AppArmor uses an ACM based on security profiles that have been loaded into the kernel. Each profile contains a collection of rules for accessing various system resources. AppArmor can be configured to either enforce access control or just complain about access control violations.
AppArmor proactively protects applications and operating system resources from internal and external threats, including zero-day attacks, by preventing both known and unknown vulnerabilities from being exploited.
AppArmor has been built into the mainline Linux kernel since version 2.6.36 and is currently shipped with Ubuntu, Debian, OpenSUSE, and similar distributions.
In the following sections we'll use an Ubuntu 20.04 environment to showcase a few practical examples with AppArmor. Most of the related command-line utilities will work the same on any platform with AppArmor installed.
Working with AppArmor
AppArmor command-line utilities usually require superuser privileges.
The following command checks the current status of AppArmor:
Here's an excerpt from the command's output:
Figure 1 – Getting the status of AppArmor
The aa-status (or apparmor_status) command provides a full list of the currently loaded AppArmor profiles (not shown in the preceding excerpt). We'll examine AppArmor profiles next.
Introducing AppArmor profiles
With AppArmor, processes are confined (or restricted) by profiles. AppArmor profiles are loaded upon system start and run either in enforce mode or complain mode. We'll explain these modes next.
AppArmor prevents applications running in enforce mode from performing restricted actions. Access violations are signaled with log entries in syslog. Ubuntu, by default, loads the application profiles in enforce mode.
Applications running in complain mode can take restricted actions, while AppArmor creates a log entry for the related violation. complain mode is ideal for testing AppArmor profiles. Potential errors or access violations can be caught and fixed before switching the profiles to enforce mode.
With these introductory notes in mind, let's create a simple application with an AppArmor profile.
Creating a profile
In this section, we'll create a simple application guarded by AppArmor. We hope this exercise will help you get a sensible idea of the inner workings of AppArmor. Let's name this application appackt. We'll make it a simple script that creates a file, writes to it, and then deletes the file. The goal is to have AppArmor prevent our app from accessing any other paths in the local system. To try and make some sense of this, think of it as trivial log recycling.
Here's the appackt script, and please pardon the thrifty implementation:
Figure 2 – The appackt script
We are assuming that the log directory already exists at the same location as the script:
Let's make the script executable and run it:
chmod a+x appackt
The output is as follows:
Figure 3 – The output of the appackt script
Now, let's work on guarding and enforcing our script with AppArmor. Before we start, we need to install the apparmor-utils package – the AppArmor toolset:
sudo apt-get install -y apparmor-utils
We'll use a couple of tools to help create the profile:
- aa-genprof: Generates an AppArmor security profile
- aa-logprof: Updates an AppArmor security profile
We use aa-genprof to monitor our application at runtime and have AppArmor learn about it. In the process, we'll be prompted to acknowledge and choose the behavior that's required in specific circumstances.
Once the profile has been created, we'll use the aa-logprof utility to make further adjustments while testing in complain mode, should any violations occur.
Let's start with aa-genprof. We need two terminals: one for the aa-genprof monitoring session (in terminal 1) and the other for running our script (in terminal 2).
We will start with terminal 1 and run the following command:
sudo aa-genprof ./appackt
There is a first prompt waiting for us. Next, while the prompt in terminal 1 is waiting, we will switch to terminal 2 and run the following command:
Now, we must go back to terminal 1 and answer the prompts sent by aa-genprof, as follows:
Prompt 1 – Waiting to scan
This prompt asks to scan the system log for AppArmor events in order to detect possible complaints (violations).
Answer: S (Scan):
Figure 4 – Prompt 1 – Waiting to scan with aa-genprof
Let's look at the next prompt.
Prompt 2 – Execute permissions for /usr/bin/bash
This prompt requests execute permissions for the process (/usr/bin/bash) running our app.
Answer: I (Inherit):
Figure 5 – Prompt 2 – Execute permissions for /usr/bin/bash
Let's look at the next prompt.
Prompt 3 – Read/write permissions to /dev/tty
This prompt requests read/write permissions for the app to control the terminal (/dev/tty).
Answer: A (Allow):
Figure 6 – Prompt 3 – Read/write permissions to /dev/tty
Now, let's look at the final prompt.
Prompt 4 – save changes
The prompt is asking to save or review the changes.
Answer: S (Save):
Figure 7 – Prompt 4 – Save changes
At this point, we have finished scanning with aa-genprof, and we can answer with F (Finish) to the last prompt. Our app (appackt) is now enforced by AppArmor in complain mode (by default). If we try to run our script, we'll get the following output:
Figure 8 – The first run of appackt with AppArmor confined
As the output suggests, things are not quite right yet. This is where the aa-logprof tool comes to the rescue. For the rest of the steps, we only need one terminal window.
Let's run the aa-logprof command to further tune our appackt security profile:
We'll get several prompts again, similar to the previous ones, asking for further permissions needed by our script, namely for the touch, cat, and rm commands. The prompts alternate between Inherit and Allow answers, where appropriate. We won't go into the details here due to space. By now, you should have a general idea about these prompts and their meaning. It's always recommended, though, to ponder upon the permissions asked for and act accordingly.
We may have to run the aa-logprof command a couple of times because, with each iteration, new permissions will be discovered and addressed, depending on the child processes that are spawned by our script and so on. Eventually, the appackt script will run successfully.
During the iterative process described previously, we may end up with a few unknown or orphaned entries in the AppArmor database, which are artifacts of our previous attempts, to secure our application:
Figure 9 – Remnants of the iterative process
They will all be named according to the path of or our application (/home/packt/appackt). We can clean up these entries with the following command:
We can now verify that our app is indeed guarded with AppArmor:
The relevant excerpt from the output is as follows:
Figure 10 – appackt in complain mode
Our application (/home/packt/appackt) is shown, as expected, in complain mode. The other two are system application-related and are not relevant for us.
Next, we need to validate that our app complies with the security policies enforced by AppArmor. Let's edit the appackt script and change the LOG_FILE path in line 6 to the following:
We have changed the output directory from log to logs. Let's create the logs directory and run our app:
The preceding output suggests that appackt is attempting to access a path outside the permitted boundaries by AppArmor, thus validating our profile:
Figure 11 – appackt acting outside security boundaries
Let's revert the preceding changes and have the appackt script act normally. We are now ready to enforce our app by changing its profile mode with the following command:
sudo aa-enforce /home/packt/appackt
The output is as follows:
Figure 12 – Changing the appackt profile to enforce mode
We can verify that our application is indeed running in enforce mode with the following command:
The relevant output is as follows:
Figure 13 – appackt running in enforce mode
If we wanted to make further adjustments to our application and then test it with the related changes, we would have to change the profile mode to complain and then reiterate the steps described earlier in this section. The following command sets the application profile to complain mode:
sudo aa-complain /home/packt/appackt
AppArmor profiles are plain text files stored in the /etc/apparmor.d/ directory. Creating or modifying AppArmor profiles usually involves manually editing the corresponding files or the procedure described in this section using the aa-genprof and aa-logprof tools.
Next, let's look at how to disable or enable AppArmor application profiles.
Disabling and enabling profiles
Sometimes, we may want to disable a problematic application profile while working on a better version. Here's how we do this.
First, we need to locate the application profile we want to disable (for example, appackt). The related file is in the /etc/apparmor.d/ directory, and it's named according to its full path, with dots (.) instead of slashes (/). In our case, the file is /etc/apparmor.d/home.packt.appackt.
To disable the profile, we must run the following commands:
sudo ln -s /etc/apparmor.d/home.packt.appackt /etc/apparmor.d/disable/
sudo apparmor_parser -R /etc/apparmor.d/home.packt.appackt
If we run the aa-status command, we won't see our appackt profile anymore. The related profile is still present in the filesystem, at /etc/apparmor.d/disable/home.packt.appackt:
Figure 14 – The disabled appackt profile
In this situation, the appackt script is not enforced by any restrictions. To reenable the related security profile, we can run the following commands:
sudo rm /etc/apparmor.d/disable/home.packt.appackt
sudo apparmor_parser -r /etc/apparmor.d/home.packt.appackt
The appackt profile should now show up in the aa-status output as running in complain mode. We can bring it into enforce mode with the following:
sudo aa-enforce /home/packt/appackt
To disable or enable the profile, we used the apparmor_parser command, besides the related filesystem operations. This utility assists with loading (-r, --replace) or unloading (-R, --remove) security profiles to and from the kernel.
Deleting AppArmor security profiles is functionally equivalent to disabling them. We can also choose to remove the related file from the filesystem altogether. If we delete a profile without removing it from the kernel first (with apparmor_parser -R), we can use the aa-remove-unknown command to clean up orphaned entries.
Let's conclude our relatively brief study of AppArmor internals with some final thoughts.
Working with AppArmor is relatively easier than SELinux, especially when it comes to generating security policies or switching back and forth between permissive mode and non-permissive mode. SELinux can only toggle the permissive context for the entire system, while AppArmor does it at the application level. On the other hand, there might be no choice between the two, as some major Linux distributions either support one or the other. AppArmor is a prodigy of Debian, Ubuntu, and, recently, OpenSUSE, while SELinux runs on RHEL/CentOS. Theoretically, you can always try to port the related kernel modules across distros, but that's not a trivial task.
As a final note, we should reiterate that in the big picture of Linux security, SELinux and AppArmor are ACMs that act locally on a system, at the application level. When it comes to securing applications and computer systems from the outside world, firewalls come into play. We'll look at firewalls next.