Skip to content

Create a comprehensive profile manually

In this guide, you will learn: - How to write a complete AppArmor profile from scratch - How to debug and refine the profile

Prerequisites

  • Install apparmor-utils
  • In this tutorial, we will use an instant messaging client Dino as an example of an application to be confined.

Prepare a test plan

When writing profiles for applications, it is important to understand what behavior of your application you want to limit. Make sure you write down all the functionality your application has such as:

  • Start, stop, and restart the application
  • Use every command-line option available
  • Open files, save files, and perform standard operations with files, create accounts, send messages, log messages, and so on

If your package or your distribution provide tests for this package, execute them all.

Without understanding your application behavior, it is difficult to write a useful profile that will enchance your users' security, so take time to document it properly.

Create a profile

Create a preliminary profile for your application at /etc/apparmor.d/dino-im. In case of Dino, it will be a file /etc/apparmor.d/dino-im.

Add global variables and an executable binary

In the header of the file, add

#include <tunables/global>
profile dino-im /usr/bin/dino-im {
}
#include <tunables/global> points to /etc/apparmor.d/tunables/global -- a file that contains global variables for your system including some default pre-configured variables such as $HOME. /usr/bin/dino-im indicates the location of the executable binary of your application.

Add abstractions

Abstractions are files can be reused across multiple profiles. They allow you to avoid writing the same rules in every profile, making profiles shorter and easier to maintain.

Study all the abstractions available by default:

ls /etc/apparmor.d/abstractions

We know that Fractal needs access to system libraries, to connect over the network, and it is a GNOME application.

base abstraction is a collection of permissions that every Linux application likely needs to function such as accesss to the terminal, logging, access to timezones and locales, and many others. It is recommended to include it in most AppArmor profiles by default.

nameservice abstraction provides a collection of standard nameservice operations such as looking up users, groups, or hosts by name or ID, and so on.

gnome abstraction provides a collection of operations with GNOME resources (fonts, themes, and so on)

Let us add both of the abstractions to the profile:

#include <tunables/global>
/usr/bin/dino-im {
  #include <abstractions/base>
  #include <abstractions/nameservice>
  #include <abstractions/gnome>
}

Restrict the application to a home user's directory and access to the binary

owner directive ensures that the process can only interact with the files that are owned by the same user as the process. In this case, we allow the application to access home directory only if the user who started the application is also the owner of the directory.

@{exec_path} mr directive allows the application to access its own executable binary. This is important so that the confined application is able to to restart itself securely.

#include <tunables/global>
/usr/bin/dino-im {
  #include <abstractions/nameservice>
  #include <abstractions/gnome>
  owner @{HOME}/** r
  @{exec_path} mr,
}

The basic profile is ready.

Set the application in complain mode

To be able to identify all the events and add appropriate rules to them, run the application in complain mode:

$ sudo aa-complain /etc/apparmor.d/dino-im

Test and monitor your application

Now that your application is running, you must note down all the operations it performs. The most convenient method is to monitor the logs while you perform your test plan on the application.

The logs can be accessed in various ways:

  • dmesg
  • /var/log/kern.log
  • /var/log/messages
  • aa-notify
  • journalctl
  • aa-logprof
  • aa-genprof

First, let's temporarily disable kernel rate limiting:

# sysctl -w kernel.printk_ratelimit=0
# sysctl -w kernel.printk_ratelimit_burst=100000
Kernel rate limiting is a kernel mechanism that controls the frequency of messages printed to the log. If it is enabled, the kernel might limit the logs you are able to see. It is normally not necessary if you are using auditd but it is recommended to disable it for the purpose of this tutorial.

Let's use journalctl to find logs about Dino. In a new terminal window, search for log entries with dino-im:

sudo journalctl -fx | grep "dino-im"

Switch to the new terminal Window, start Dino and try performing a few operations with it. In the terminal window with journalctl, log entries similar to this will start appearing:

Jan 28 11:38:45 localhost.localdomain kernel: audit: type=1400 audit(1769596725.729:2389): apparmor="ALLOWED" operation="file_lock" class="file" profile="/usr/bin/dino-im" name="/home/yhontyk/.local/share/dino/dino.db-shm" pid=6287 comm="dino-im" requested_mask="k" denied_mask="k" fsuid=1000 ouid=1000

According to this log entry, dino-im is attempting to lock a .local/share/dino/dino.db-shm file. Files in .local/share/dino are used by Dino for storing account data, media, attachments in chats, and so on which means that for Dino to function properly, this operation should be allowed.

Modify and reload the profile

Keep adding rules for your application's profile as you test them. A good practice is to group the rules according to the functionality they cover, for example:

#include <tunables/global>
/usr/bin/dino-im {
  #include <abstractions/nameservice>
  #include <abstractions/gnome>
  owner @{HOME}/ r,

  # Rules for reading/writing the database and attachments
  owner @{HOME}/.local/share/dino/ rw,
  owner @{HOME}/.local/share/dino/** rwk,
 }

When granting permissions

After updating the profile, reload it so that AppArmor starts to enforce the latest changes:

 $ sudo apparmor_parser -r /etc/apparmor.d/dino-im 

When removing permissions

  1. Unload the profile:
    apparmor\_parser -R /etc/apparmor.d/dino-im
    
  2. Load the profile with

apparmor\_parser -a /etc/apparmor.d/dino-im
3. Restart the application.

Put in enforce mode

Once you have the profile configured and tested, you can put it in the enforce mode which means that AppArmor will start denying all the restricted operations:

$ sudo aa-enforce /etc/apparmor.d/dino-im

You now have a profile that adds an extra security layer to your application.

It is recommended to monitor the logs for a while as the application is running in the enforce mode. Even if you tested your application thoroughly in the complain mode, users might trigger new denials by using rare features, encountering errors, or using a different environment. Adjust the profile as necessary as you monitor it.

Ship profiles

If you are developing profile for an application that you develop or for an application that is not covered by an AppArmor profile by default but can be useful for other users, we encourage you to submit it to the AppArmor repository. The profile will then be maintained by the community and can be readily shipped into many distributions.

Further reading