Guidelines
Common structure¶
AppArmor profiles can be written without any specific guidelines. However, when you work with over 1500 profiles, you need a common structure among all the profiles.
The logic behind it is that if a rule is present in a profile, it should only be in one place, making it easier to review profiles.
For example, if a program needs to run executable binaries then the rules allowing it can only be in a specific rule block (just after the @{exec_path} mr, rule). It is therefore easy to ensure some profile features such as:
- A profile has access to a given resource
- A profile enforces a strict write xor execute (W^X) policy.
It also improves compatibility and makes personalization easier thanks to the use of more variables.
Why does it matter?
The structure presented below is more important than it looks. Without this it would be easy to miss some rules when reviewing profiles, making the whole system more opaque and less secure.
Guidelines¶
In order to ensure a common structure across the profiles, all new profile must follow the guidelines presented here.
The rules in the profile should be written in blocks of same rule type. Each block should be sorted in the profile as follows:
| Order | Name | Example |
|---|---|---|
| 1 | include |
|
| 2 | set rlimit |
|
| 3 | userns |
|
| 4 | capability |
|
| 5 | network |
|
| 6 | mount |
|
| 7 | remount |
|
| 8 | umount |
|
| 9 | pivot_root |
|
| 10 | change_profile |
|
| 11 | mqueue |
|
| 12 | signal |
|
| 13 | ptrace |
|
| 14 | unix |
|
| 15 | io_uring |
|
| 16 | dbus |
|
| 17 | file |
|
| 18 | local include |
This rule order is taken from AppArmor with minor changes as we tend to:
- Divide the file block into multiple subcategories
- Put the block with the longer rules (
files,dbus) after the other blocks
File sub-blocks¶
The file block is usually the longest block in a profile. It should be written in sub-blocks of identical file types. The file sub blocks must be sorted as follows.
Dbus block¶
Dbus rules must be written in a dedicated block just before the file block. The dbus block must be sorted as follows:
- The system bus should be sorted before the session bus
- The bind rules should be sorted after send & receive rules
For DBus, try to determine peer's label when possible. E.g.:
dbus send bus=session path=/org/freedesktop/DBus
interface=org.freedesktop.DBus
member={RequestName,ReleaseName}
peer=(name=org.freedesktop.DBus, label="@{p_dbus_session}"),
If there is no predictable label it can be omitted.
Profile rules¶
Variables¶
-
Always use the apparmor.d variables. Example:
/usr/libor/usr/binbecome@{bin}or@{lib}/usr/sbinor/sbinbecome@{bin}.
Sort¶
- In a rule block, all rules must be alphabetically sorted.
Sub-profiles¶
- Sub-profiles should come at the end of a profile.
Similar purpose¶
- When some rules share similar purposes, they may be sorted together. E.g.:
Limit the use of deny¶
-
The use of
denyshould be limited to the minimum:
Comments¶
- Ensure you only have useful comments. E.g.: Does not help, and if generalized it would add a lot of complexity to any profiles.
Clarity over cleverness¶
- Always prefer clarity to cleverness. E.g., if a rule is more explicit but longer, prefer it over a shorter but less explicit one.