Contents
#include
StatementsBuilding AppArmor profiles to confine an application is very 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 Novell 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 are #include
statements grouped by
common application tasks.
Program chunks are #include
statements that contain
chunks of profiles that are specific to program suites.
Capability entries are profile entries for any of the POSIX.1e 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 mediate network access based on the address type and family.
Local variables define shortcuts for paths.
File Access Control Entries specify the set of files an application can access.
rlimit entries set and control an application's resource limits.
For help determining the programs to profile, refer to Section 19.2, “Determining Programs to Immunize”. To start building AppArmor profiles with YaST, proceed to Chapter 22, Building and Managing Profiles with YaST. To build profiles using the AppArmor command line interface, proceed to Chapter 23, Building Profiles from the Command Line.
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> # a comment naming the application to confine /usr/bin/foo { #include <abstractions/base> capability setgid, network inet tcp, link /etc/sysconfig/foo -> /etc/foo.conf, /bin/mount ux, /dev/{,u}random 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, /tmp/foo.pid wr, /tmp/foo.* lrw, /@{HOME}/.foo_file rw, /@{HOME}/.foo_lock kw, owner /shared/foo/** rw, /usr/bin/foobar cx, /bin/** px -> bin_generic, # a comment about foo's local (children)profile for /usr/bin/foobar. profile /usr/bin/foobar { /bin/bash rmix, /bin/cat rmix, /bin/more rmix, /var/log/foobar* rwl, /etc/foobar r, } # foo's hat, bar. ^bar { /lib/ld-*.so* mr, /usr/bin/bar px, /var/spool/* rwl, } }
This loads a file containing variable definitions. | |
The normalized path to the program that is confined. | |
The curly braces ( | |
This directive pulls in components of AppArmor profiles to simplify profiles. | |
Capability entry statements enable each of the 29 POSIX.1e draft capabilities. | |
A directive determining the kind of network access allowed to the application. For details, refer to Section 20.5, “Network Access Control”. | |
A link pair rule specifying the source and the target of a link. See Section 20.7.6, “Link Pair” for more information. | |
The curly braces ( | |
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 | |
This variable expands to a value that can be changed without changing the entire profile. | |
An owner conditional rule, granting read and write permission on files owned by the user. Refer to Section 20.7.7, “Owner Conditional Rules” for more information. | |
This entry defines a transition to the local profile
| |
A named profile transition to the profile bin_generic located in the global scope. See Section 20.8.7, “Named Profile Transitions” for details. | |
The local profile | |
This section references a “hat” subprofile of the application. For more details on AppArmor's ChangeHat feature, refer to Chapter 24, 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 native 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 (see the
chmod
and chown
man
pages) and have /foo/bar w
in its profile.
Attempts to violate Novell AppArmor rules are recorded in
/var/log/audit/audit.log
if the
audit
package is installed
or otherwise in /var/log/messages
. In many cases,
Novell AppArmor rules prevent an attack from working because necessary files are
not accessible and, in all cases, Novell AppArmor confinement restricts the damage
that the attacker can do to the set of files permitted by Novell AppArmor.
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.
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 will be automatically used whenever an unconfined process
executes /usr/bin/foo
.
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 20.8.7, “Named Profile Transitions”) or with the
change_profile
rule (see
Section 20.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.
Local profiles provide a convenient way to provide specialized
confinement for utility programs launched by a confined application.
They are specified just like standard profiles except 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 20.8.2, “Discrete Local Profile Execute Mode (cx)”) or a named
profile transition (see
Section 20.8.7, “Named Profile Transitions”).
AppArmor "hats" are a local profiles with some additional restrictions and
an implicit rule allowing for change_hat
to be used
to transition to them. Refer to Chapter 24, Profiling Your Web Applications Using ChangeHat for a
detailed description.
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 will restrict the opening of new resources, and will even limit
some of the resources opened before the switch. Specifically, memory
resources will still be available while capability and file resources
(as long as 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 (e.g. 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 may not be seen by the kernel and hence not
restricted by the new profile.
Safety of Domain Transitions | |
---|---|
The |
#include
Statements¶
#include
statements are directives that pull in
components of other Novell 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.
By default, AppArmor adds /etc/apparmor.d
to the path in
the #include
statement. AppArmor expects the include files
to be located in /etc/apparmor.d
. Unlike other
profile statements (but similar to C programs),
#include
lines do not end with a comma.
To assist you in profiling your applications, Novell AppArmor provides three
classes of #include
s: abstractions, program chunks and
tunables.
Abstractions are #include
s 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
usually require some of the other files listed in the abstraction file
(depending on the local configuration as well as the specific
requirements of the program). Find abstractions in
/etc/apparmor.d/abstractions
.
The program-chunks directory
(/etc/apparmor.d/program-chunks
) contains some
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.
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
.
Capability statements are simply the word capability
followed by the name of the POSIX.1e capability as defined in the
capabilities(7)
man page.
AppArmor allows mediation of network access based on the address type and family. The following illustrates the network access rule syntax:
network [[<domain>][<type>][<protocol>]]
Supported domains: | |
Supported types: | |
Supported protocols: |
The AppArmor tools support only family and type specification. The AppArmor
module emits only network
in “access denied”
messages. And only these are output by the profile generation tools, both
YaST and command line.
domain
type
The following examples illustrate possible network-related rules to be used in AppArmor profiles. Note that the syntax of the last two are not currently supported by the AppArmor tools.
network, network inet, network inet6, network inet stream, network inet tcp, network tcp,
Allow all networking. No restrictions applied with regards to domain, type, or protocol. | |
Allow general use of IPv4 networking. | |
Allow general use of IPv6 networking. | |
Allow the use of IPv4 TCP networking. | |
Allow the use of IPv4 TCP networking, paraphrasing the rule above. | |
Allow the use of both IPv4 and IPv6 TCP networking. |
AppArmor explicitly distinguishes directory path names from file 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
.
/some/random/example/** r
Give read access to files and directories under
/some/random/example
.
/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 file 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 |
|
Substitutes for the single character
Example: a rule that matches |
|
Substitutes for the single character |
|
Expands to one rule to match
Example: a rule that matches |
|
Substitutes for any character except |
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 have 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/syslog-ng { ... # chrooted applications @{CHROOT_BASE}/var/lib/*/dev/log w, @{CHROOT_BASE}/var/log/** w, ... }
With the current AppArmor tools, variables can only be used when manually editing and maintaining a profile. |
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:
alias /home/ -> /mnt/users/
With the current AppArmor tools, alias rules can only be used when manually
editing and maintaining a profile. Whats more, they are deactivated by
disabled. Enable alias rules by editing
|
File permission access modes consist of combinations of the following modes:
|
Read mode |
|
Write mode (mutually exclusive to |
|
Append mode (mutually exclusive to |
|
File locking mode |
|
Link mode |
|
Link pair rule (cannot be combined with other access modes) |
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.
Allows the program to have write access to the resource. Files must have this permission if they are to be unlinked (removed).
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 just a subset of the
permissions associated with the write mode, the w
and
a
permission flags cannot be used together and are
mutually exclusive.
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.
The link mode mediates access to hard links. When a link is created, the target file must have the same access permissions as the link created (with the exception that the destination does not need link access).
The link mode grants permission to create links to arbitrary files,
provided the link has a subset of the permissions granted by the target
(subset permission test). By specifying origin and destination, the link
pair rule provides greater control over how hard links are created. Link
pair rules by default do not enforce the link subset permission test
that the standard rules link permission requires. To force the rule to
require the test the subset
keyword is used. The
following rules are equivalent:
/link l, link subset /link -> /**,
Currently link pair rules are not supported by YaST and the command line tools. Manually edit your profiles to use them. Updating such profiles using the tools is safe, because the link pair entries will not be touched. |
The file rules can be extended so that they can be conditional upon the
the user being the owner of the file (the fsuid has to match the file's
uid). For this purpose the owner
keyword is prepended
to the rule. Owner conditional rules accumulate just as regular file
rules.
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.
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 resultant permissions will be that of the regular file rule. |
Deny rules can be used to annotate or quiet known rejects. The profile
generating tools will not ask about a known reject treated with a deny
rule. Such a reject will also not show up in the audit logs when denied,
keeping the log files lean. If this is not desired, prepend the deny
entry with the keyword audit
.
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, /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 will not make use of 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 will not be touched.
Execute modes, also named profile transitions, consist of the following modes:
|
Discrete profile execute mode |
|
Discrete local profile execute mode |
|
Unconstrained execute mode |
|
Inherit execute mode |
|
Allow |
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.
Using the Discrete Profile Execute Mode | |
---|---|
|
Incompatible with Ux
, ux
,
Px
, and ix
.
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.
Limitations of the Discrete Local Profile Execute Mode (cx) | |
---|---|
Currently, cx transitions are limited to top level profiles and can not be used in hats and children profiles. This restriction will be removed in the future. |
Incompatible with Ux
, ux
,
Px
, px
, Cx
, and
ix
.
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 unconstrained execution rights, it is possible
to bypass the mandatory constraints imposed on all confined processes.
For more information about what is constrained, see the
apparmor(7)
man page.
Using Unconstrained Execute Mode (ux) | |
---|---|
Use |
This mode is incompatible with Ux
,
px
, Px
, and ix
.
The clean exec modes allow the named program to run in
px
, cx
and ux
mode, but AppArmor invokes the Linux kernel's unsafe_exec
routines to scrub the environment, similar to setuid programs. The clean
exec modes are specified with an uppercase letter:
Px
, Cx
and Ux
.
See the man page of ld.so(8) for some information
about setuid and setgid environment scrubbing.
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
.
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 some 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).
By default, the px
and cx
(and
their clean exec variants, too) transition to a profile who's 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 in conjunction 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 { ... } }
Difference Between Normal and Named Transitions | |
---|---|
When used with globbing, normal transitions provide a “one to
many” relationship— Named transitions provide a “many to one” relationship—all programs that match the rule regardless of their name will transition to the specified profile.
Named profile transitions show up in the log as having the mode
|
The px
and cx
transitions specify
a hard dependency (if the specified profile does not exist, the exec
will fail). With the inheritance fallback, the execution will succeed
but inherit the current profile. To specify inheritance fallback,
ix
is combined with cx
,
Cx
, px
and Px
into the modes cix
, Cix
,
pix
and Pix
. The fallback modes
can be used with named profile transitions, too.
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
AppArmor provides the ability to set and control an application's resource
limits (rlimits, also known as ulimits). By default AppArmor does not control
applications rlimits, and it will only control 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 will be inherited by the children of a process and will 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 has the ability to further reduce the applications rlimits.
AppArmor's rlimit rules will also provide mediation of setting an application's hard limits, should it try to raise them. The application will not be able to 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 once 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 rlimitresource
<=value
,
where resource
and
value
are to be replaced with the following
values:
cpu
currently not supported
fsize
, data
, stack
,
core
, rss
, as
,
memlock
, msgqueue
a number in bytes, or a number with a suffix where the suffix can be K (kilobytes), M (megabytes), G (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
*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 given time. Once the limit is exceeded the creation of new processes under the profile will fail until the number of currently running processes is reduced.
Currently the tools can not 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 will 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. |
AppArmor provides the ability to audit given rules so that when they are
matched an audit message will appear in the audit log. To enable audit
messages for a given rule, the audit
keyword is
prepended to 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 will result in audit messages when files are opened for writing, but not when they are opened for just reading:
audit /etc/foo/* w, /etc/foo/* r,
Audit messages are not generated for every read or write of a file but only when a file is opened for read or write. |
Audit control can be combined with owner conditional file rules to provide auditing when users access files they own (at the moment it is not possible to audit files they don't own):
audit owner /home/*/.ssh/** rw,
Normally AppArmor only restricts existing native Linux controls and does not grant additional privileges. Therefore a program, having been granted write access to a file via its profile, would not be able to actually write to this file as long as the mode bits are set to read only.
The only exception to this strict rule is the set
capability
rule. This provides the ability to give non-root
users administrative privileges, as defined in the
capabilities(7)
man page. Contrary to setting a
program to setuid or using file system capabilities (that apply to single
programs only), the set capability rule allows the user to apply
capabilities to multiple programs running under a specific profile (by
using ix transitions). For security reasons, set capability rules will
not be inherited (once a program leaves the profile, it loses the
elevated privilege).
Use set capabilities Rules with Extreme Caution | |
---|---|
Using the set capabilities rules allows to give processes |
To set a capability in a profile the keyword “set” is prepended to a capability rule. Setting a capability also implicitly adds a capability rule allowing that capability.
set capability cap_chown,
Currently the tools can not 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 will 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. |