Skip to content

Rules basics

Note

This document covers basic building blocks of AppArmor profile. It is aimed at beginners and provides only introductory material. For a comprehensive reference of the AppArmor profile language, see Core policy reference

AppArmor can control various aspects of applicaion behavior. Let's take a look at some common use cases.

AppArmor rules contain the following elements:

  • Actors: processes, tasks, or applications whose behavior is mediated

  • Actions: operations that actors performs, for example, read, write, execute, and so on

  • Targets: objects that actors target when performing operations, for example, files, sockets,IPs, ports, and so on

Any application can be confined by AppArmor. Let's now look at the possible types of actions and corresponding targets that AppArmor can restrict.

Capabilities

Linux capabilities (see also man capabilities) is a Linux feature for permission control over processes. Capabilities are a set of predefined operations and priviligies associated with them that can be configured on a process level. Capabilities can be configured by various tools, including AppArmor.

For example, CAP_SETUID and CAP_SETGID are capabiltiies for changing user ID and group ID respectively. A setuid application which drops privileges might need:

 /profile {
    capability setuid,
    capability setgid,
 }

Network access control

AppArmor can control network access of the applicaitions based on a domain, address family, socket type, or protocol.

For example, a packet analyer might need:

 /profile {
   network raw,
   network packet,
 }

Resource limit control

AppArmor can control how high a process is allowed to set its own resource limits (rlimits) also known as "hard limits" of the setrlimit function.

You can you AppArmor profiles to specify maximum allowed values for certain rlimit types such as number of open files, max file size. However, since the enforcement of rlimits is handled by a different part of the Linux kernel, not AppArmor, AppArmor won’t log events when an rlimit is enforced.

Example of a signal rule:

 /profile {
    set rlimit resource <= value,
 }

Signal

AppArmor can control inter-process signals.

Example of a signal rule:

/profile {
    # Can receive signals from unconfined processes
    signal (receive) peer=unconfined,
    # Can send and receive SIGUSR1 and SIGUSR2 signals to other instances of ourselves
    signal (send,receive) set=(usr1,usr2) peer=/profile,
}

For a signal to succeed, its sender must have permission to send the signal and its target must have permission to receive the signal.

Note

peer=unconfined does not mean allowing signals between the confined process and any other process; instead, it only allows signals when the other process is not running under an AppArmor profile.

Ptrace

There are four classes of ptrace operations:

  • trace: trace another process using ptrace

  • tracedby: be traced using ptrace by another process

  • read: read certain proc filesystem information, kcmp, futexes, and perf trace events about another process

  • readby: have certain proc filesystem information, kcmp, futexes, and perf trace events about oneself read by another process

The peer= option can be included to limit the profiles that the other process must be running under.

Example a ptrace rule:

 /profile {
   ptrace, # Allow all ptrace operations
   ptrace (readby, tracedby) peer=unconfined, # Allow unconfined processes to ptrace us
 }

File access control

With AppArmor, you can limit which operations an application can perform with specific files. Normally, a permission to read, write, or execute a file is with DAC (for example, by usicng chmod). AppArmor always checks DAC permissions and applies additional restrictions sepcified in a profile associated with the application. AppArmor cannot override DAC permissions. be normally allowed.

For example, if a file has read-only permissions, AppArmor cannot grant the program permissions to write in this file. However, if a file has write permissions, AppArmor can restrict a specific program writing in it. For a detailed explanation of the logic of how AppArmor deal with file access, see

The permissions to create and/or delete a file are declared with the following syntax:

/path/to/a/file      w,

Where path/to/a/file is an absolute path to a file and w is an access-mode rule that AppArmor will enforce on the application. r is used for read permissions, w for write, and x for execute.

A valid pathname always begins with a /. Eg:

 /profile {
    /path/to/file  rw,   # file rule beginning with a pathname (convention)
    rw /path/to/file2,   # file rule beginning with permissions
    /path/to/file3       # file rule split over multiple lines
         rw,
 }

Entries for specific (non-globbed) directories should have a trailing slash at the end. For example, /path/to/obj rw grants permissions for a file obj, while /path/to/obj/ rw grants permissions for a directory obj.

File rules can contain special globbing characters that allow matching to multiple files (see File Globbing, below)

File Globbing

AppArmor uses a file globbing syntax similar to that used by the bash shell. Globbing is not standard full regular expression syntax, but instead uses a few characters known as wild cards. The AppArmor wildcards have slightly different semantics than that of bash.

  • * - match zero or more characters at the directory level. When looking at the path as a string this will match every character except /
    • This will match dot files (file names starting with .), excepting the special dot files . and .., if it is placed immediately after the directory / eg. /dir/*
    • This will not match an empty directory string eg. /dir//
    • pcre equivalent is ([^/\000]*)
  • ** - match 0 or more characters over multiple directory levels.
    • This will match dot files (file names starting with .), excepting the special dot files . and .., if it is placed immediately after the directory / eg. /dir/**
    • pcre equivalent is ([^\000]*)
  • ? - match a single character that is not /
    • pcre equivalent is [^/]
  • {} - alternation - a comma separated list of alternate strings that can be matched. An empty string is allowed and means the empty string is a viable alternative
    • pcre equivalent is (|)
  • [] - character class
    • same as pcre syntax
  • [^] - inverted character class
    • same as pcre syntax
  • Nesting expressions in alternations (as of AppArmor 2.3):
    • escaping characters \*
    • expressing characters as # \001

File Globbing examples

/dir/file     - match a specific file
/dir/*        - match any files in a directory (including dot files)
/dir/a*       - match any file in a directory starting with 'a'
/dir/*.png    - match any file in a directory ending with '.png'
/dir/[^.]*    - match any file in a directory except dot files

/dir/         - match a directory
/dir/*/       - match any directory within /dir/
/dir/a*/      - match any directory within /dir/ starting with a
/dir/*a/      - match any directory within /dir/ ending with a

/dir/**       - match any file or directory in or below /dir/
/dir/**/      - match any directory in or below /dir/
/dir/**[^/]   - match any file in or below /dir/

/dir{,1,2}/** - match any file or directory in or below /dir/, /dir1/, and /dir2/

File permissions

The following file permissions are supported:

  • r - read
  • w - write
  • a - append (implied by w)
  • x - execute
    • ux - Execute unconfined -- WARNING: should only be used in very special cases
    • Ux - Execute unconfined (use ld.so(8) secure-execution mode)
    • px - Execute under a specific profile -- WARNING: should only be used in special cases
    • Px - Execute under a specific profile (use ld.so(8) secure-execution mode)
    • pix - as px but fallback to inheriting the current profile if the target profile is not found
    • Pix - as Px but fallback to inheriting the current profile if the target profile is not found
    • pux - as px but fallback to executing unconfined if the target profile is not found
    • Pux - as Px but fallback to executing unconfined if the target profile is not found
    • ix - Execute and inherit the current profile
    • cx - Execute and transition to a child profile
    • Cx - Execute and transition to a child profile (use ld.so(8) secure-execution mode)
    • cix - as cx but fallback to inheriting the current profile if the target profile is not found
    • Cix - as Cx but fallback to inheriting the current profile if the target profile is not found
    • cux - as cx but fallback to executing unconfined if the target profile is not found
    • Cux - as Cx but fallback to executing unconfined if the target profile is not found
  • m - memory map executable
  • k - lock (requires r or w, AppArmor 2.1 and later)
  • l - link

The owner keyword can be used as a qualifier making permission conditional on owning the file (process fsuid == file's uid).

 owner /foo rw,

The following are in development:

  • create (implied by w)
  • delete (implied by w)
  • chown - change ownership (implied by w)
  • chmod - change mode (implied by w)

Execute permissions

AppArmor distinguishes between the different ways a file may be executed. Because a new process is created when executing a file, the process is said to transition to another (possibly same) profile across execute.

The base execute permission are:

  • ix - the new process should run under the current profile
  • cx - the new process should run under a child profile that matches the name of the executable
  • px - the new process should run under another profile that matches the name of the executable
  • ux - the new process should run unconfined

A process that runs unconfined is actually in the built-in unconfined profile, which allows everything with no logging.

The px, cx, and ux permission when written using a capitalized leading character (Px, Cx, Ux) will trigger libc's Secure Execution. When developing profiles, the Secure Execution variants should in general be used so that the executed program starts in a clean environment.

The px and cx rules (and their Secure Execution variants) may also have an ix, or ux fallback, expressed as pix, pux, cix, or cux. Using the fallback indicates that the process should run under the profile if it exists, otherwise the profile transition should use the specified ix or ux transition. It is often a good idea to use 'PUx' instead of 'Ux' so you don't have to update your profile for when the executed program has an AppArmor profile added later. For example, if a confined program should be allowed to run 'evince', then the profile might have:

 /usr/bin/evince PUx,

The px and cx rules (and all their variants) can also be modified to specify a profile by name instead of using the profile that matches the name of the executable. This is done by providing a -> transition arrow and the name of the profile.

 /foo px -> profile1,

For directories, the UNIX execute permission maps to search access and AppArmor does not further control directory search access. In other words, traversing directories is granted if DAC permits it.

Directory vs file permissions

Directory and file permissions are treated separately by AppArmor. It means that a directory with write permission does not imply permission to write to or create files in a directory. Write permission for a directory gives write access to the directory itself, which means you are allowed to change the directory meta information (chown, chmod, ..), and create/remove the directory.

File rename and file copy

AppArmor does not differenciate between file rename and file copy permissions. Having a separate rename permission only gives a small amount of extra control over just mediating file copy. That is having a separate file rename permission encodes expected behavior of how the data is expected to be moved, while file copy just worries about whether a files contents can be copied. As long as reading the source and writing to the destination are allowed rename restrictions can be worked around simply by copying the files' contents to the destination.

In the end the trade off in extra control over complexity this feature was considered not worth implementing at this time.

How AppArmor file permissions differ from DAC

On traditional UNIX systems using DAC, when files are looked up by name, the lookup starts either at the root or the current working directory of a process. From there, each directory reached is checked for search permission (x). The permissions on the directories leading to the current working directory are not checked. When a file is being created or deleted, the parent directory of that file is checked for write and search access (wx). When a file is being accessed, the permissions of that file are checked for r, w, or x access, or a combination thereof. Each check can result in a failure with errno set to EACCES (Permission denied).

AppArmor provides an additional permission check to DAC. DAC is always checked in addition to the AppArmor permission checks. As such, AppArmor cannot override DAC to provide more access than what would be normally allowed.

Path permission checking is calculated differently for AppArmor than in DAC. AppArmor first computes the pathname to the file. If the file is being created, the name being looked up is the name of the new file and not the name of the parent directory. If the file being looked up is a directory, AppArmor appends a slash to the pathname (this means a directory pathname in a profile must always end in a slash). As a convenience, AppArmor also allows pathnames to be rewritten via aliases.

After determining the pathname to evaluate, AppArmor then checks for file access rules in the process' profile that match that pathname, and performs permission checks based on these rules. With some exceptions for execute modes and deny mode, the permissions granted are the union of permissions of all matching rules.

More simply, creation of files requires the create permission (implied by w) on the path to be created. Permissions to write to the directory the file is created or accessed in is not required. Deletion works like creation but requires the delete permission (implied by w). Copy requires 'r' of the source with create and write at the destination (implied by w). Move is like copy, but also requires delete at source. For example:

The permissions to create and/or delete a file are:

/foo/bar      w,

The permissions to copy a file are:

 /foo/src      r,
 /foo/dst      w,

The permissions to move a file are:

 /foo/src     rw,
 /foo/dst      w,

Notice in the above that AppArmor does not require additional rules in the policy to access or write to the / or /foo/ directories (DAC rules still apply though).

For directories, read access (r) allows reading of directory meta data (eg, owner) and reading of directory contents (ie listing) but is not needed for traversal. Write access (w) allows creation, deletion, and updating of directory meta data.

AppArmor file labeling

AppArmor will assign a default label to a file (as opposed to storing that label in the file's inode). When a process opens a file, the file object is assigned a label, which can be thought of as the profile name. A file can have multiple labels for when different processes want to access the same file to allow for different processes having different access controls. In practice, when developing policy one just refers to files by name, and the kernel handles all the necessary labeling behind the scenes. As such, AppArmor is often referred to as 'pathname-based'.

Since AppArmor labels files by pathname (rather than using on disk labeling), the administrator does not need to perform a relabelling step after a file is overwritten or moved. For example, if a process is granted read access to /etc/shadow and the system administrator renames /etc/shadow to /etc/shadow.old and replaces it with a copy (that may have an additional user in it, for example), the process will have access to the new /etc/shadow, and not to /etc/shadow.old.

Rule Modifiers

When there is no corresponding rule for a resource, AppArmor will block access to the resource and log it. When there is a rule in the policy, access is allowed to the resource without logging. The following modifiers can be prepended to a rule to change this behavior:

  • audit: force logging
  • deny: explicitly deny, without logging
  • audit deny: combination to explicitly deny, but log

Eg:

 /profile {
    /path/to/file*            r,  # allow read to /path/to/file*
    /path/to/file1            w,  # allow write to /path/to/file1
    deny /path/to/file2       w,  # deny write to /path/to/file2, without logging
    audit /path/to/file3      w,  # allow write to /path/to/file3, with logging
    audit deny /path/to/file4 r,  # deny read to /path/to/file4, with logging
 }

Important

Deny rules are evaluated before allow rules and cannot be overridden by an allow rule. They are often used to override file globbing rules. For example, in the above policy the 'audit deny /path/to/file4 r' rule above overrides the '/path/to/file* r' rule.