Apparmor - How to use it

By Rainer Wichmann []    (last update: Dec 30, 2011)

What is Apparmor

Apparmor is a Linux security mechanism that restricts what a process can do. E.g. if you are browsing the web using firefox, and firefox is restricted by an apparmor profile, then the firefox process can only do things that are allowed by this profile. As an example, one of these restrictions might be that within the user's home directory, firefox can only write to ~/Downloads (for downloaded files) and to the ~/.firefox directory tree (configuration data, cache, etc.). In reality, at least the Ubuntu apparmor profile for firefox allows much more, and its usefulness seems a little questionable.

An important property of apparmor is that apparmor profiles restrict processes based on the path of the executable. Thus apparmor does not provide any protection against a rogue local user who may copy an executable to another location and run it under a different path.

What it does protect against, however, is the scenario that a remote attacker may gain control over a network facing application and make it do bad things to the filesystem. E.g. when a malicious website exploits some firefox to take control of the browser, the apparmor profile could prevent firefox from doing things you may disapprove of.

How can I use Apparmor to protect a process?

Say you are using application /usr/bin/foobar to offer a publicly available service on your machine (e.g. a mail server, or whatever). You want to restrict this process with an apparmor profile. How would you do that?

What you need is to stop the foobar process, and execute (as root) the following command:

aa-genprof /usr/bin/foobar

This will create an initial profile, set it to complain mode, write a start marker to the system log, and ask you to execute the application /usr/bin/foobar. Complain mode means that violations of the profile will be logged, rather than enforcing the profile.

If you use aa-genprof to refine a existing profile, you need to run the following command after starting aa-genprof and before starting your application. The reason is that aa-genprof is not smart enough to make apparmor reload the apparmor profiles:

/etc/init.d/apparmor reload

Once you have exercised the functionality of the foobar program, you can then hit S, which will cause aa-genprof to scan the system logs and refine the profile based on the reported violations. Finally, you can hit F to finish profile generation and set the profile to enforce mode.

You can always switch back to complain mode with:

aa-complain /usr/bin/foobar

and there are also the corresponding commands aa-enforce and aa-disable, to switch back a profile to enforce mode or disable it.

Well, it isn't quite that easy...

Hitting the first bug

Following the instructions on screen, I hit 'S'. Nothing interesting happens, and the generated profile looks quite empty. Uh huh..

#include 

/usr/bin/foobar flags=(complain) {
  #include 
  #include 


  /bin/bash ix,

}

Ahem. A little search on google turns up a reference to /etc/apparmor/logprof.conf, where we have the line:

  logfiles = /var/log/audit/audit.log /var/log/messages /var/log/syslog

It turns out that on my system there is no /var/log/audit/audit.log, but /var/log/messages exists yet is empty (on Debian/Ubuntu, syslog logs most stuff to /var/log/syslog). So apparently aa-genprof uses the first existing file in the list, even if it provably contains nothing, not even the start marker logged by aa-genprof itself. At least this problem can easily be solved by removing the empty /var/log/messages file.

Ok, aa-genprof does something now

Obviously aa-genprof can't do magic, so you need to help it a little. What it does is, it will ask you questions about the rule violations it has detected. E.g. it may ask you:

Profile:  /usr/bin/foobar
Path:     /proc/4381/stat
Mode:     r
Severity: 6

  1 - /proc/4381/stat 
 [2 - /proc/*/stat]

[(A)llow] / (D)eny / (G)lob / Glob w/(E)xt / (N)ew / Abo(r)t / (F)inish / (O)pts

As you see, it asks whether you want to grant read access (Mode: r) to the path /proc/4381/stat, and offers as default the more permissive alternative /proc/*/stat. You can now either hit 'a' to select the second option (the default), or choose '1' and then 'a' for the first. Hitting 'g' will probably offer /proc/**, which is short for 'anything in /proc'. Actually, what you probably want in this particular case is to hit 'a' and allow /proc/*/stat, because the number is the process ID and hence will be different for every new process.

Refining the rules

The rules for an apparmor profile are stored in a file within /etc/apparmor.d/, named after the executable, with slashes in the path replaces by dots. So the apparmor profile for /usr/bin/foobar would be:

/etc/apparmor.d/usr.bin.foobar

After finishing with aa-genprof, you may want to have a look at the file. The syntax is pretty self-explanatory. Access rules for files or directories look like:

/path/to/file/or/directory  access_permissions,

For paths, the following shell style wildcard patterns are supported (see the manpage for apparmor.d). Note that noly rules that end with a trailing slash will match a directory.

*  Substitutes for any number of characters, except /.
   I.e., /tmp/* matches any file in /tmp. /tmp/*/ matches any
   directory in /tmp.

** Substitutes for any number of characters, including /.
    I.e., /tmp/** matches all files and directories underneath /tmp.
    /tmp/**/ matches all directories underneath /tmp.

?  Substitutes for any single character, except /. 

[abc] Substitutes for the single character a, b, or c.
      Note that this is a list.

[a-c] Substitutes for the single character a, b, or c.
      Note that this is a range. 

{ab,cd} Expand to one rule to match ab and another to match cd. 

[^a] Substitutes for any character except a.

Access permissions are a combination of letters like 'r' (read), 'w' (write), or 'rw' (read and write). The full list (again from the manpage for apparmor.d) is:

r    - read
w    - write -- conflicts with append
a    - append -- conflicts with write
ux   - unconfined execute
Ux   - unconfined execute -- scrub the environment
px   - discrete profile execute
Px   - discrete profile execute -- scrub the environment
cx   - transition to subprofile on execute
Cx   - transition to subprofile on execute -- scrub the environment
ix   - inherit execute
m    - allow PROT_EXEC with mmap(2) calls
l    - link
k    - lock
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.0 Germany License.