Jump to contentJump to page navigation: previous page [access key p]/next page [access key n]
documentation.suse.com / Documentación de SUSE Linux Enterprise Server / Security and Hardening Guide / Confining privileges with AppArmor / Profile components and syntax
Applies to SUSE Linux Enterprise Server 15 SP6

34 Profile components and syntax

Building AppArmor profiles to confine an application is straightforward and intuitive. AppArmor ships with several tools that assist in profile creation. It does not require you to do any programming or script handling. The only task that is required of the administrator is to determine a policy of strictest access and execute permissions for each application that needs to be hardened.

Updates or modifications to the application profiles are only required if the software configuration or the desired range of activities changes. AppArmor offers intuitive tools to handle profile updates and modifications.

You are ready to build AppArmor profiles after you select the programs to profile. To do so, it is important to understand the components and syntax of profiles. AppArmor profiles contain several building blocks that help build simple and reusable profile code:

Include files

Include statements are used to pull in parts of other AppArmor profiles to simplify the structure of new profiles.

Abstractions

Abstractions are include statements grouped by common application tasks.

Program chunks

Program chunks are include statements that contain chunks of profiles that are specific to program suites.

Capability entries

Capability entries are profile entries for any of the POSIX.1e (https://en.wikipedia.org/wiki/POSIX#POSIX.1) Linux capabilities allowing a fine-grained control over what a confined process is allowed to do through system calls that require privileges.

Network access control entries

Network Access Control Entries mediate network access based on the address type and family.

Local variable definitions

Local variables define shortcuts for paths.

File access control entries

File Access Control Entries specify the set of files an application can access.

rlimit entries

rlimit entries set and control an application's resource limits.

For help determining the programs to profile, refer to Section 33.2, “Determining programs to immunize”. To start building AppArmor profiles with YaST, proceed to Chapter 36, Building and managing profiles with YaST. To build profiles using the AppArmor command line interface, proceed to Chapter 37, Building profiles from the command line.

For more details about creating AppArmor profiles, see man 5 apparmor.

34.1 Breaking an AppArmor profile into its parts

The easiest way of explaining what a profile consists of and how to create one is to show the details of a sample profile, in this case for a hypothetical application called /usr/bin/foo:

#include <tunables/global>1

# a comment naming the application to confine
/usr/bin/foo2 {3
   #include <abstractions/base>4

   capability setgid5,
   network inet tcp6,

   link /etc/sysconfig/foo -> /etc/foo.conf,7
   /bin/mount            ux,
   /dev/{,u}8random     r,
   /etc/ld.so.cache      r,
   /etc/foo/*            r,
   /lib/ld-*.so*         mr,
   /lib/lib*.so*         mr,
   /proc/[0-9]**         r,
   /usr/lib/**           mr,
   /tmp/                 r,9
   /tmp/foo.pid          wr,
   /tmp/foo.*            lrw,
   /@{HOME}10/.foo_file   rw,
   /@{HOME}/.foo_lock    kw,
   owner11 /shared/foo/** rw,
   /usr/bin/foobar       Cx,12
   /bin/**               Px -> bin_generic,13

   # a comment about foo's local (children) profile for /usr/bin/foobar.

   profile /usr/bin/foobar14 {
      /bin/bash          rmix,
      /bin/cat           rmix,
      /bin/more          rmix,
      /var/log/foobar*   rwl,
      /etc/foobar        r,
   }

  # foo's hat, bar.
   ^bar15 {
    /lib/ld-*.so*         mr,
    /usr/bin/bar          px,
    /var/spool/*          rwl,
   }
}

1

This loads a file containing variable definitions.

2

The normalized path to the program that is confined.

3

The curly braces ({}) serve as a container for include statements, subprofiles, path entries, capability entries, and network entries.

4

This directive pulls in components of AppArmor profiles to simplify profiles.

5

Capability entry statements enable each of the 29 POSIX.1e draft capabilities.

6

A directive determining the kind of network access allowed to the application. For details, refer to Section 34.5, “Network access control”.

7

A link pair rule specifying the source and the target of a link. See Section 34.7.6, “Link pair” for more information.

8

The curly braces ({}) here allow for each of the listed possibilities, one of which is the empty string.

9

A path entry specifying what areas of the file system the program can access. The first part of a path entry specifies the absolute path of a file (including regular expression globbing) and the second part indicates permissible access modes (for example r for read, w for write, and x for execute). A whitespace of any kind (spaces or tabs) can precede the path, but must separate the path and the mode specifier. Spaces between the access mode and the trailing comma are optional. Find a comprehensive overview of the available access modes in Section 34.7, “File permission access modes”.

10

This variable expands to a value that can be changed without changing the entire profile.

11

An owner conditional rule, granting read and write permission on files owned by the user. Refer to Section 34.7.8, “Owner conditional rules” for more information.

12

This entry defines a transition to the local profile /usr/bin/foobar. Find a comprehensive overview of the available execute modes in Section 34.12, “Execute modes”.

13

A named profile transition to the profile bin_generic located in the global scope. See Section 34.12.7, “Named profile transitions” for details.

14

The local profile /usr/bin/foobar is defined in this section.

15

This section references a hat subprofile of the application. For more details on AppArmor's ChangeHat feature, refer to Chapter 38, Profiling your Web applications using ChangeHat.

When a profile is created for a program, the program can access only the files, modes and POSIX capabilities specified in the profile. These restrictions are in addition to the Linux access controls.

Example: To gain the capability CAP_CHOWN, the program must have both access to CAP_CHOWN under conventional Linux access controls (typically, be a root-owned process) and have the capability chown in its profile. Similarly, to be able to write to the file /foo/bar the program must have both the correct user ID and mode bits set in the files attributes and have /foo/bar w in its profile.

Attempts to violate AppArmor rules are recorded in /var/log/audit/audit.log if the audit package is installed, or in /var/log/messages, or only in journalctl if no traditional syslog is installed. Often AppArmor rules prevent an attack from working because necessary files are not accessible and, in all cases, AppArmor confinement restricts the damage that the attacker can do to the set of files permitted by AppArmor.

34.2 Profile types

AppArmor knows four different types of profiles: standard profiles, unattached profiles, local profiles and hats. Standard and unattached profiles are stand-alone profiles, each stored in a file under /etc/apparmor.d/. Local profiles and hats are children profiles embedded inside of a parent profile used to provide tighter or alternate confinement for a subtask of an application.

34.2.1 Standard profiles

The default AppArmor profile is attached to a program by its name, so a profile name must match the path to the application it is to confine.

/usr/bin/foo {
...
}

This profile is automatically used whenever an unconfined process executes /usr/bin/foo.

34.2.2 Unattached profiles

Unattached profiles do not reside in the file system namespace and therefore are not automatically attached to an application. The name of an unattached profile is preceded by the keyword profile. You can freely choose a profile name, except for the following limitations: the name must not begin with a : or . character. If it contains a whitespace, it must be quoted. If the name begins with a /, the profile is considered to be a standard profile, so the following two profiles are identical:

profile /usr/bin/foo {
...
}
/usr/bin/foo {
...
}

Unattached profiles are never used automatically, nor can they be transitioned to through a Px rule. They need to be attached to a program by either using a named profile transition (see Section 34.12.7, “Named profile transitions”) or with the change_profile rule (see Section 34.2.5, “Change rules”).

Unattached profiles are useful for specialized profiles for system utilities that generally should not be confined by a system-wide profile (for example, /bin/bash). They can also be used to set up roles or to confine a user.

34.2.3 Local profiles

Local profiles provide a convenient way to provide specialized confinement for utility programs launched by a confined application. They are specified like standard profiles, except that they are embedded in a parent profile and begin with the profile keyword:

/parent/profile {
   ...
   profile /local/profile {
      ...
   }
}

To transition to a local profile, either use a cx rule (see Section 34.12.2, “Discrete local profile execute mode (cx)”) or a named profile transition (see Section 34.12.7, “Named profile transitions”).

34.2.4 Hats

AppArmor "hats" are a local profiles with certain additional restrictions and an implicit rule allowing for change_hat to be used to transition to them. Refer to Chapter 38, Profiling your Web applications using ChangeHat for a detailed description.

34.2.5 Change rules

AppArmor provides change_hat and change_profile rules that control domain transitioning. change_hat are specified by defining hats in a profile, while change_profile rules refer to another profile and start with the keyword change_profile:

change_profile -> /usr/bin/foobar,

Both change_hat and change_profile provide for an application directed profile transition, without having to launch a separate application. change_profile provides a generic one way transition between any of the loaded profiles. change_hat provides for a returnable parent child transition where an application can switch from the parent profile to the hat profile and if it provides the correct secret key return to the parent profile at a later time.

change_profile is best used in situations where an application goes through a trusted setup phase and then can lower its privilege level. Any resources mapped or opened during the start-up phase may still be accessible after the profile change, but the new profile restricts the opening of new resources, and limits certain resources opened before the switch. Specifically, memory resources are still available while capability and file resources (if they are not memory mapped) can be limited.

change_hat is best used in situations where an application runs a virtual machine or an interpreter that does not provide direct access to the applications resources (for example Apache's mod_php). Since change_hat stores the return secret key in the application's memory the phase of reduced privilege should not have direct access to memory. It is also important that file access is properly separated, since the hat can restrict accesses to a file handle but does not close it. If an application does buffering and provides access to the open files with buffering, the accesses to these files might not be seen by the kernel and hence not restricted by the new profile.

Warning
Warning: Safety of domain transitions

The change_hat and change_profile domain transitions are less secure than a domain transition done through an exec because they do not affect a process's memory mappings, nor do they close resources that have already been opened.

34.3 Include statements

Include statements are directives that pull in components of other AppArmor profiles to simplify profiles. Include files retrieve access permissions for programs. By using an include, you can give the program access to directory paths or files that are also required by other programs. Using includes can reduce the size of a profile.

Include statements normally begin with a hash (#) sign. This is confusing because the same hash sign is used for comments inside profile files. Because of this, #include is treated as an include only if there is no preceding # (##include is a comment) and there is no whitespace between # and include (# include is a comment).

You can also use include without the leading #.

include "/etc/apparmor.d/abstractions/foo"

is the same as using

#include "/etc/apparmor.d/abstractions/foo"
Note
Note: No trailing ','

Because includes follow the C pre-processor syntax, they do not have a trailing ',' like most AppArmor rules.

By slight changes in syntax, you can modify the behavior of include. If you use "" around the including path, you instruct the parser to do an absolute or relative path lookup.

include "/etc/apparmor.d/abstractions/foo"   # absolute path
include "abstractions/foo"   # relative path to the directory of current file

When using relative path includes, when the file is included, it is considered the new current file for its includes. For example, suppose you are in the /etc/apparmor.d/bar file, then

include "abstractions/foo"

includes the file /etc/apparmor.d/abstractions/foo. If then there is

include "example"

inside the /etc/apparmor.d/abstractions/foo file, it includes /etc/apparmor.d/abstractions/example.

The use of <> specifies to try the include path (specified by -I, defaults to the /etc/apparmor.d directory) in an ordered way. So assuming the include path is

-I /etc/apparmor.d/ -I /usr/share/apparmor/

then the include statement

include <abstractions/foo>

tries /etc/apparmor.d/abstractions/foo, and if that file does not exist, the next try is /usr/share/apparmor/abstractions/foo.

Tip
Tip

The default include path can be overridden manually by passing -I to the apparmor_parser, or by setting the include paths in /etc/apparmor/parser.conf:

Include /usr/share/apparmor/
Include /etc/apparmor.d/

Multiple entries are allowed, and they are taken in the same order as when they are when using -I or --Include from the apparmor_parser command line.

If an include ends with '/', this is considered a directory include, and all files within the directory are included.

To assist you in profiling your applications, AppArmor provides three classes of includes: abstractions, program chunks and tunables.

34.3.1 Abstractions

Abstractions are includes that are grouped by common application tasks. These tasks include access to authentication mechanisms, access to name service routines, common graphics requirements, and system accounting. Files listed in these abstractions are specific to the named task. Programs that require one of these files also requires other files listed in the abstraction file (depending on the local configuration and the specific requirements of the program). Find abstractions in /etc/apparmor.d/abstractions.

34.3.2 Program chunks

The program-chunks directory (/etc/apparmor.d/program-chunks) contains certain chunks of profiles that are specific to program suites and not generally useful outside of the suite, thus are never suggested for use in profiles by the profile wizards (aa-logprof and aa-genprof). Currently, program chunks are only available for the postfix program suite.

34.3.3 Tunables

The tunables directory (/etc/apparmor.d/tunables) contains global variable definitions. When used in a profile, these variables expand to a value that can be changed without changing the entire profile. Add all the tunables definitions that should be available to every profile to /etc/apparmor.d/tunables/global.

34.4 Capability entries (POSIX.1e)

Capability rules are simply the word capability followed by the name of the POSIX.1e capability as defined in the capabilities(7) man page. You can list multiple capabilities in a single rule, or grant all implemented capabilities with the bare keyword capability.

capability dac_override sys_admin,   # multiple capabilities
capability,                          # grant all capabilities

34.5 Network access control

AppArmor allows mediation of network access based on the address type and family. The following illustrates the network access rule syntax:

network [[<domain>1][<type2>][<protocol3>]]

1

Supported domains: inet, ax25, ipx, appletalk, netrom, bridge, x25, inet6, rose, netbeui, security, key, packet, ash, econet, atmsvc, sna, pppox, wanpipe, bluetooth, unix, atmpvc,netlink, llc, can, tipc, iucv, rxrpc, isdn, phonet, ieee802154, caif, alg, nfc, vsock

2

Supported types: stream, dgram, seqpacket, rdm, raw, packet

3

Supported protocols: tcp, udp, icmp

The AppArmor tools support only family and type specification. The AppArmor module emits only network DOMAIN TYPE in ACCESS DENIED messages. And only these are output by the profile generation tools, both YaST and command line.

The following examples illustrate possible network-related rules to be used in AppArmor profiles. The syntax of the last two are not currently supported by the AppArmor tools.

network1,
network inet2,
network inet63,
network inet stream4,
network inet tcp5,
network tcp6,

1

Allow all networking. No restrictions applied with regard to domain, type, or protocol.

2

Allow general use of IPv4 networking.

3

Allow general use of IPv6 networking.

4

Allow the use of IPv4 TCP networking.

5

Allow the use of IPv4 TCP networking, paraphrasing the rule above.

6

Allow the use of both IPv4 and IPv6 TCP networking.

34.6 Profile names, flags, paths, and globbing

A profile is attached to a program by specifying a full path to the program's executable. For example, a standard profile (see Section 34.2.1, “Standard profiles”), the profile is defined by

/usr/bin/foo { ... }

The following sections describe several useful techniques that can be applied when naming a profile or putting a profile in the context of other existing ones, or specifying file paths.

AppArmor explicitly distinguishes directory path names from path names. Use a trailing / for any directory path that needs to be explicitly distinguished:

/some/random/example/* r

Allow read access to files in the /some/random/example directory.

/some/random/example/ r

Allow read access to the directory only.

/some/**/ r

Give read access to any directories below /some (but not /some/ itself).

/some/random/example/** r

Give read access to files and directories under /some/random/example (but not /some/random/example/ itself).

/some/random/example/**[^/] r

Give read access to files under /some/random/example. Explicitly exclude directories ([^/]).

Globbing (or regular expression matching) is when you modify the directory path using wild cards to include a group of files or subdirectories. File resources can be specified with a globbing syntax similar to that used by popular shells, such as csh, Bash and zsh.

*

Substitutes for any number of any characters, except /.

Example: An arbitrary number of path elements.

**

Substitutes for any number of characters, including /.

Example: An arbitrary number of path elements, including entire directories.

?

Substitutes for any single character, except /.

[abc]

Substitutes for the single character a, b, or c.

Example: a rule that matches /home[01]/*/.plan allows a program to access .plan files for users in both /home0 and /home1.

[a-c]

Substitutes for the single character a, b, or c.

{ab,cd}

Expands to one rule to match ab and one rule to match cd.

Example: a rule that matches /{usr,www}/pages/** grants access to Web pages in both /usr/pages and /www/pages.

[^a]

Substitutes for any character except a.

34.6.1 Profile flags

Profile flags control the behavior of the related profile. You can add profile flags to the profile definition by editing it manually, see the following syntax:

/path/to/profiled/binary flags=(list_of_flags) {
  [...]
}

You can use multiple flags separated by a comma ',' or space ' '. There are three basic types of profile flags: mode, relative, and attach flags.

Mode flag is complain (illegal accesses are allowed and logged). If it is omitted, the profile is in enforce mode (enforces the policy).

Tip
Tip

A more flexible way of setting the whole profile into complain mode is to create a symbolic link from the profile file inside the /etc/apparmor.d/force-complain/ directory.

ln -s /etc/apparmor.d/bin.ping /etc/apparmor.d/force-complain/bin.ping

Relative flags are chroot_relative (states that the profile is relative to the chroot instead of namespace) or namespace_relative (the default, with the path being relative to outside the chroot). They are mutually exclusive.

Attach flags consist of two pairs of mutually exclusive flags: attach_disconnected or no_attach_disconnected (determine if path names resolved to be outside of the namespace are attached to the root, which means they have the '/' character at the beginning), and chroot_attach or chroot_no_attach (control path name generation when in a chroot environment while a file is accessed that is external to the chroot but within the namespace).

34.6.2 Using variables in profiles

AppArmor allows to use variables holding paths in profiles. Use global variables to make your profiles portable and local variables to create shortcuts for paths.

A typical example of when global variables come in handy are network scenarios in which user home directories are mounted in different locations. Instead of rewriting paths to home directories in all affected profiles, you only need to change the value of a variable. Global variables are defined under /etc/apparmor.d/tunables and need to be made available via an include statement. Find the variable definitions for this use case (@{HOME} and @{HOMEDIRS}) in the /etc/apparmor.d/tunables/home file.

Local variables are defined at the head of a profile. This is useful to provide the base of for a chrooted path, for example:

@{CHROOT_BASE}=/tmp/foo
/sbin/rsyslogd {
...
# chrooted applications
@{CHROOT_BASE}/var/lib/*/dev/log w,
@{CHROOT_BASE}/var/log/** w,
...
}

In the following example, while @{HOMEDIRS} lists where all the user home directories are stored, @{HOME} is a space-separated list of home directories. Later on, @{HOMEDIRS} is expanded by two new specific places where user home directories are stored.

@{HOMEDIRS}=/home/
@{HOME}=@{HOMEDIRS}/*/ /root/
[...]
@{HOMEDIRS}+=/srv/nfs/home/ /mnt/home/
Note
Note

With the current AppArmor tools, variables can only be used when manually editing and maintaining a profile.

34.6.3 Pattern matching

Profile names can contain globbing expressions allowing the profile to match against multiple binaries.

The following example is valid for systems where the foo binary resides either in /usr/bin or /bin.

/{usr/,}bin/foo { ... }

In the following example, when matching against the executable /bin/foo, the /bin/foo profile is an exact match so it is chosen. For the executable /bin/fat, the profile /bin/foo does not match, and because the /bin/f* profile is more specific (less general) than /bin/**, the /bin/f* profile is chosen.

/bin/foo { ... }

/bin/f*  { ... }

/bin/**  { ... }

For more information on profile name globbing examples, see the man page of AppArmor, man 5 apparmor.d,, section Globbing.

34.6.4 Namespaces

Namespaces are used to provide different profiles sets. Say one for the system, another for a chroot environment or container. Namespaces are hierarchical—a namespace can see its children but a child cannot see its parent. Namespace names start with a colon : followed by an alphanumeric string, a trailing colon : and an optional double slash //, such as

:childNameSpace://

Profiles loaded to a child namespace are prefixed with their namespace name (viewed from a parent's perspective):

:childNameSpace://apache

Namespaces can be entered via the change_profile API, or named profile transitions:

/path/to/executable px -> :childNameSpace://apache

34.6.5 Profile naming and attachment specification

Profiles can have a name, and an attachment specification. This allows for profiles with a logical name that can be more meaningful to users/administrators than a profile name that contains pattern matching (see Section 34.6.3, “Pattern matching”). For example, the default profile

/** { ... }

can be named

profile default /** { ... }

Also, a profile with pattern matching can be named. For example:

/usr/lib64/firefox*/firefox-*bin { ... }

can be named

profile firefox /usr/lib64/firefox*/firefox-*bin { ... }

34.6.6 Alias rules

Alias rules provide an alternative way to manipulate profile path mappings to site specific layouts. They are an alternative form of path rewriting to using variables, and are done post variable resolution. The alias rule says to treat rules that have the same source prefix as if the rules are at target prefix.

alias /home/ -> /usr/home/

All the rules that have a prefix match to /home/ provide access to /usr/home/. For example:

/home/username/** r,

allows as well access to

/usr/home/username/** r,

Aliases provide a quick way of remapping rules without the need to rewrite them. They keep the source path still accessible—in our example, the alias rule keeps the paths under /home/ still accessible.

With the alias rule, you can point to multiple targets at the same time.

alias /home/ -> /usr/home/
alias /home/ -> /mnt/home/
Note
Note

With the current AppArmor tools, alias rules can only be used when manually editing and maintaining a profile.

Tip
Tip

Insert global alias definitions in the file /etc/apparmor.d/tunables/alias.

34.7 File permission access modes

File permission access modes consist of combinations of the following modes:

r

Read mode

w

Write mode (mutually exclusive to a)

a

Append mode (mutually exclusive to w)

k

File locking mode

l

Link mode

link FILE -> TARGET

Link pair rule (cannot be combined with other access modes)

34.7.1 Read mode (r)

Allows the program to have read access to the resource. Read access is required for shell scripts and other interpreted content and determines if an executing process can core dump.

34.7.2 Write mode (w)

Allows the program to have write access to the resource. Files must have this permission if they are to be unlinked (removed).

34.7.3 Append mode (a)

Allows a program to write to the end of a file. In contrast to the w mode, the append mode does not include the ability to overwrite data, to rename, or to remove a file. The append permission is typically used with applications who need to be able to write to log files, but which should not be able to manipulate any existing data in the log files. As the append permission is a subset of the permissions associated with the write mode, the w and a permission flags cannot be used together and are mutually exclusive.

34.7.4 File locking mode (k)

The application can take file locks. Former versions of AppArmor allowed files to be locked if an application had access to them. By using a separate file locking mode, AppArmor makes sure locking is restricted only to those files which need file locking and tightens security as locking can be used in several denial-of-service attack scenarios.

34.7.7 Optional allow and file rules

The allow prefix is optional, and it is idiomatically implied if not specified and the deny (see Section 34.7.9, “Deny rules”) keyword is not used.

allow file /example r,
allow /example r,
allow network,

You can also use the optional file keyword. If you omit it and there are no other rule types that start with a keyword, such as network or mount, it is automatically implied.

file /example/rule r,

is equivalent to

/example/rule r,

The following rule grants access to all files:

file,

which is equal to

/** rwmlk,

File rules can use leading or trailing permissions. The permissions should not be specified as a trailing permission, but rather used at the start of the rule. This is important in that it makes file rules behave like any other rule types.

/path rw,            # old style
rw /path,            # leading permission
file rw /path,       # with explicit 'file' keyword
allow file rw /path, # optional 'allow' keyword added

34.7.8 Owner conditional rules

The file rules can be extended so that they can be conditional upon the user being the owner of the file (the fsuid needs to match the file's uid). For this purpose the owner keyword is put in front of the rule. Owner conditional rules accumulate like regular file rules do.

owner /home/*/** rw

When using file ownership conditions with link rules the ownership test is done against the target file so the user must own the file to be able to link to it.

Note
Note: Precedence of regular file rules

Owner conditional rules are considered a subset of regular file rules. If a regular file rule overlaps with an owner conditional file rule, the rules are merged. Consider the following example.

/foo r,
owner /foo rw,  # or w,

The rules are merged—it results in r for everybody, and w for the owner only.

Tip
Tip

To address everybody but the owner of the file, use the keyword other.

owner /foo rw,
other /foo r,

34.7.9 Deny rules

Deny rules can be used to annotate or quiet known rejects. The profile generating tools do not ask about a known reject treated with a deny rule. Such a reject does not show up in the audit logs when denied, keeping the log files lean. If this is not desired, put the keyword audit in front of the deny entry.

It is also possible to use deny rules in combination with allow rules. This allows you to specify a broad allow rule, and then subtract a few known files that should not be allowed. Deny rules can also be combined with owner rules, to deny files owned by the user. The following example allows read/write access to everything in a users directory except write access to the .ssh/ files:

deny /home/*/.ssh/** w,
owner /home/*/** rw,

The extensive use of deny rules is generally not encouraged, because it makes it much harder to understand what a profile does. However a judicious use of deny rules can simplify profiles. Therefore the tools only generate profiles denying specific files and do not use globbing in deny rules. Manually edit your profiles to add deny rules using globbing. Updating such profiles using the tools is safe, because the deny entries are not touched.

34.8 Mount rules

AppArmor can limit mount and unmount operations, including file system types and mount flags. The rule syntax is based on the mount command syntax and starts with one of the keywords mount, remount, or umount. Conditions are optional and unspecified conditionals are assumed to match all entries. For example, not specifying a file system means that all file systems are matched.

Conditionals can be specified by specifying conditionals with options= or options in.

options= specifies conditionals that have to be met exactly. The rule

mount options=ro /dev/foo -E /mnt/,

matches

# mount -o ro /dev/foo /mnt

but does not match

# mount -o ro,atime /dev/foo /mnt
# mount -o rw /dev/foo /mnt

options in requires that at least one of the listed mount options is used. The rule

mount options in (ro,atime) /dev/foo -> /mnt/,

matches

# mount -o ro /dev/foo /mnt
# mount -o ro,atime /dev/foo /mnt
# mount -o atime /dev/foo /mnt

but does not match

# mount -o ro,sync /dev/foo /mnt
# mount -o ro,atime,sync /dev/foo /mnt
# mount -o rw /dev/foo /mnt
# mount -o rw,noatime /dev/foo /mnt
# mount /dev/foo /mnt

With multiple conditionals, the rule grants permission for each set of options. The rule

mount options=ro options=atime

matches

# mount -o ro /dev/foo /mnt
# mount -o atime /dev/foo /mnt

but does not match

# mount -o ro,atime /dev/foo /mnt

Separate mount rules are distinct and the options do not accumulate. The rules

mount options=ro,
mount options=atime,

are not equivalent to

mount options=(ro,atime),
mount options in (ro,atime),

The following rule allows mounting /dev/foo on /mnt/ read only and using inode access times or allows mounting /dev/foo on /mnt/ with some combination of 'nodev' and 'user'.

mount options=(ro, atime) options in (nodev, user) /dev/foo -> /mnt/,

allows

# mount -o ro,atime /dev/foo /mnt
# mount -o nodev /dev/foo /mnt
# mount -o user /dev/foo /mnt
# mount -o nodev,user /dev/foo /mnt

34.9 Pivot root rules

AppArmor can limit changing the root file system. The syntax is

pivot_root [oldroot=OLD_ROOT] NEW_ROOT

The paths specified in 'pivot_root' rules must end with '/' since they are directories.

# Allow any pivot
pivot_root,

# Allow pivoting to any new root directory and putting the old root
# directory at /mnt/root/old/
pivot_root oldroot=/mnt/root/old/,

# Allow pivoting the root directory to /mnt/root/
pivot_root /mnt/root/,

# Allow pivoting to /mnt/root/ and putting the old root directory at
# /mnt/root/old/
pivot_root oldroot=/mnt/root/old/ /mnt/root/,

# Allow pivoting to /mnt/root/, putting the old root directory at
# /mnt/root/old/ and transition to the /mnt/root/sbin/init profile
pivot_root oldroot=/mnt/root/old/ /mnt/root/ -> /mnt/root/sbin/init,

34.10 PTrace rules

AppArmor supports limiting ptrace system calls. ptrace rules are accumulated so that the granted ptrace permissions are the union of all the listed ptrace rule permissions. If a rule does not specify an access list, permissions are implicitly granted.

The trace and tracedby permissions control ptrace(2); read and readby control proc(5) file system access, kcmp(2), futexes (get_robust_list(2)) and perf trace events.

For a ptrace operation to be allowed, the tracing and traced processes need the correct permissions. This means that the tracing process needs the trace permission and the traced process needs the tracedby permission.

Example AppArmor PTrace rules:

# Allow all PTrace access
ptrace,

# Explicitly allow all PTrace access,
ptrace (read, readby, trace, tracedby),

# Explicitly deny use of ptrace(2)
deny ptrace (trace),

# Allow unconfined processes (eg, a debugger) to ptrace us
ptrace (readby, tracedby) peer=unconfined,

# Allow ptrace of a process running under the /usr/bin/foo profile
ptrace (trace) peer=/usr/bin/foo,

34.11 Signal rules

AppArmor supports limiting inter-process signals. AppArmor signal rules are accumulated so that the granted signal permissions are the union of all the listed signal rule permissions. AppArmor signal permissions are implied when a rule does not explicitly state an access list.

The sending and receiving process must both have the correct permissions.

Example signal rules:

# Allow all signal access
signal,

# Explicitly deny sending the HUP and INT signals
deny signal (send) set=(hup, int),

# Allow unconfined processes to send us signals
signal (receive) peer=unconfined,

# Allow sending of signals to a process running under the /usr/bin/foo
# profile
signal (send) peer=/usr/bin/foo,

# Allow checking for PID existence
signal (receive, send) set=("exists"),

# Allow us to signal ourselves using the built-in @{profile_name} variable
signal peer=@{profile_name},

# Allow two real-time signals
signal set=(rtmin+0 rtmin+32),

34.12 Execute modes

Execute modes, also named profile transitions, consist of the following modes:

Px

Discrete profile execute mode

Cx

Discrete local profile execute mode

Ux

Unconfined execute mode

ix

Inherit execute mode

m

Allow PROT_EXEC with mmap(2) calls

34.12.1 Discrete profile execute mode (px)

This mode requires that a discrete security profile is defined for a resource executed at an AppArmor domain transition. If there is no profile defined, the access is denied.

Incompatible with Ux, ux, px, and ix.

34.12.2 Discrete local profile execute mode (cx)

As Px, but instead of searching the global profile set, Cx only searches the local profiles of the current profile. This profile transition provides a way for an application to have alternate profiles for helper applications.

Note
Note: Limitations of the discrete local profile execute mode (cx)

Currently, Cx transitions are limited to top level profiles and cannot be used in hats and children profiles. This restriction will be removed in the future.

Incompatible with Ux, ux, Px, px, cx, and ix.

34.12.3 Unconfined execute mode (ux)

Allows the program to execute the resource without any AppArmor profile applied to the executed resource. This mode is useful when a confined program needs to be able to perform a privileged operation, such as rebooting the machine. By placing the privileged section in another executable and granting unconfined execution rights, it is possible to bypass the mandatory constraints imposed on all confined processes. Allowing a root process to go unconfined means it can change AppArmor policy itself. For more information about what is constrained, see the apparmor(7) man page.

This mode is incompatible with ux, px, Px, and ix.

34.12.4 Unsafe exec modes

Use the lowercase versions of exec modes—px, cx, ux—only in special cases. They do not scrub the environment of variables such as LD_PRELOAD. As a result, the calling domain may have an undue amount of influence over the called resource. Use these modes only if the child absolutely must be run unconfined and LD_PRELOAD must be used. Any profile using such modes provides negligible security. Use at your own risk.

34.12.5 Inherit execute mode (ix)

ix prevents the normal AppArmor domain transition on execve(2) when the profiled program executes the named program. Instead, the executed resource inherits the current profile.

This mode is useful when a confined program needs to call another confined program without gaining the permissions of the target's profile or losing the permissions of the current profile. There is no version to scrub the environment because ix executions do not change privileges.

Incompatible with cx, ux, and px. Implies m.

34.12.6 Allow executable mapping (m)

This mode allows a file to be mapped into memory using mmap(2)'s PROT_EXEC flag. This flag marks the pages executable. It is used on certain architectures to provide non executable data pages, which can complicate exploit attempts. AppArmor uses this mode to limit which files a well-behaved program (or all programs on architectures that enforce non executable memory access controls) may use as libraries, to limit the effect of invalid -L flags given to ld(1) and LD_PRELOAD, LD_LIBRARY_PATH, given to ld.so(8).

34.12.7 Named profile transitions

By default, the px and cx (and their clean exec variants, too) transition to a profile whose name matches the executable name. With named profile transitions, you can specify a profile to be transitioned to. This is useful if multiple binaries need to share a single profile, or if they need to use a different profile than their name would specify. Named profile transitions can be used with cx, Cx, px and Px. Currently there is a limit of twelve named profile transitions per profile.

Named profile transitions use -> to indicate the name of the profile that needs to be transitioned to:

/usr/bin/foo
{
  /bin/** px -> shared_profile,
  ...
  /usr/*bash cx -> local_profile,
  ...
  profile local_profile
  {
    ...
  }
}
Note
Note: Difference between normal and named transitions

When used with globbing, normal transitions provide a one to many relationship—/bin/** px transitions to /bin/ping, /bin/cat, etc, depending on the program being run.

Named transitions provide a many to one relationship—all programs that match the rule regardless of their name transition to the specified profile.

Named profile transitions show up in the log as having the mode Nx. The name of the profile to be changed to is listed in the name2 field.

34.12.8 Fallback modes for profile transitions

The px and cx transitions specify a hard dependency—if the specified profile does not exist, the exec fails. With the inheritance fallback, the execution succeeds but inherits the current profile. To specify inheritance fallback, ix is combined with cx, Cx, px and Px into the modes cix, Cix, pix and Pix.

/path Cix -> profile_name,

or

Cix /path -> profile_name,

where -> profile_name is optional.

The same applies if you add the unconfined ux mode, where the resulting modes are cux, CUx, pux and PUx. These modes allow falling back to unconfined when the specified profile is not found.

/path PUx -> profile_name,

or

PUx /path -> profile_name,

where -> profile_name is optional.

The fallback modes can be used with named profile transitions, too.

34.12.9 Variable settings in execution modes

When choosing one of the Px, Cx or Ux execution modes, take into account that the following environment variables are removed from the environment before the child process inherits it. As a consequence, applications or processes relying on any of these variables do not work anymore if the profile applied to them carries Px, Cx or Ux flags:

  • GCONV_PATH

  • GETCONF_DIR

  • HOSTALIASES

  • LD_AUDIT

  • LD_DEBUG

  • LD_DEBUG_OUTPUT

  • LD_DYNAMIC_WEAK

  • LD_LIBRARY_PATH

  • LD_ORIGIN_PATH

  • LD_PRELOAD

  • LD_PROFILE

  • LD_SHOW_AUXV

  • LD_USE_LOAD_BIAS

  • LOCALDOMAIN

  • LOCPATH

  • MALLOC_TRACE

  • NLSPATH

  • RESOLV_HOST_CONF

  • RES_OPTIONS

  • TMPDIR

  • TZDIR

34.12.10 safe and unsafe keywords

You can use the safe and unsafe keywords for rules instead of using the case modifier of execution modes. For example

/example_rule Px,

is the same as any of the following

safe /example_rule px,
safe /example_rule Px,
safe px /example_rule,
safe Px /example_rule,

and the rule

/example_rule px,

is the same as any of

unsafe /example_rule px,
unsafe /example_rule Px,
unsafe px /example_rule,
unsafe Px /example_rule,

The safe/unsafe keywords are mutually exclusive and can be used in a file rule after the owner keyword, so the order of rule keywords is

[audit] [deny] [owner] [safe|unsafe] file_rule

34.13 Resource limit control

AppArmor can set and control an application's resource limits (rlimits, also known as ulimits). By default, AppArmor does not control an application's rlimits, and it controls those limits specified in the confining profile. For more information about resource limits, refer to the setrlimit(2), ulimit(1), or ulimit(3) man pages.

AppArmor leverages the system's rlimits and as such does not provide an additional auditing that would normally occur. It also cannot raise rlimits set by the system, AppArmor rlimits can only reduce an application's current resource limits.

The values are inherited by the children of a process and remain even if a new profile is transitioned to or the application becomes unconfined. So when an application transitions to a new profile, that profile can further reduce the application's rlimits.

AppArmor's rlimit rules provide mediation of setting an application's hard limits, should it try to raise them. The application cannot raise its hard limits any further than specified in the profile. The mediation of raising hard limits is not inherited as the set value is, so that when the application transitions to a new profile it is free to raise its limits as specified in the profile.

AppArmor's rlimit control does not affect an application's soft limits beyond ensuring that they are less than or equal to the application's hard limits.

AppArmor's hard limit rules have the general form of:

set rlimit RESOURCE <= value,

where RESOURCE and VALUE are to be replaced with the following values:

cpu

CPU time limit in seconds.

fsize, data, stack, core, rss, as, memlock, msgqueue

a number in bytes, or a number with a suffix where the suffix can be K/KB (kilobytes), M/MB (megabytes), G/GB (gigabytes), for example

rlimit data <= 100M,
fsize, nofile, locks, sigpending, nproc, rtprio

a number greater or equal to 0

nice

a value between -20 and 19

Note
Note

The nproc rlimit is handled different than all the other rlimits. Instead of indicating the standard process rlimit, it controls the maximum number of processes that can be running under the profile at any time. When the limit is exceeded, the creation of new processes under the profile fails until the number of currently running processes is reduced.

Note
Note

Currently the tools cannot be used to add rlimit rules to profiles. The only way to add rlimit controls to a profile is to manually edit the profile with a text editor. The tools still work with profiles containing rlimit rules and will not remove them, so it is safe to use the tools to update profiles containing them.

34.14 Auditing rules

AppArmor provides the ability to audit given rules so that when they are matched, an audit message appears in the audit log. To enable audit messages for a given rule, the audit keyword is put in front of the rule:

audit /etc/foo/*        rw,

If it is desirable to audit only a given permission, the rule can be split into two rules. The following example results in audit messages when files are opened for writing, but not when they are opened for reading:

audit /etc/foo/*  w,
/etc/foo/*        r,
Note
Note

Audit messages are not generated for every read or write of a file but only when a file is opened for reading or writing.

Audit control can be combined with owner/other conditional file rules to provide auditing when users access files they own/do not own:

audit owner /home/*/.ssh/**       rw,
audit other /home/*/.ssh/**       r,