In this lesson, you will learn how to use AppArmor profile abstractions to modularize your security policies and reduce redundancy when managing common tasks like DNS resolution or system logging.
The Problem of Repetition
When writing AppArmor profiles, you often find yourself repeating the same sets of permissions across multiple applications. For example, almost every network-aware service needs to read /etc/resolv.conf to perform DNS lookups, and many services need access to the logging socket at /dev/log. If you hardcode these rules into every individual profile, updating the location of a socket or changing a permission becomes a maintenance nightmare.
Leveraging Abstractions
AppArmor provides abstractions to solve this. These are essentially "include files" located in /etc/apparmor.d/abstractions/ that contain pre-defined sets of rules. Instead of manually writing access rules for every system task, you simply include the relevant abstraction file in your custom profile.
By using the #include directive, you modularize your policy. The main profile remains clean and focused solely on the unique requirements of your application, while the shared dependencies are offloaded to these managed abstractions.
Practical Implementation
To allow a custom profile to perform standard DNS resolution, you do not need to list every system file required for glibc name services. You can simply add an include line:
profile my-custom-app /usr/bin/my-app {
# Include common rules for DNS lookups
#include <abstractions/nameservice>
# Include common rules for system logging
#include <abstractions/base>
# Application-specific rules
/var/log/my-app.log rw,
/etc/my-app/config.conf r,
}
When the AppArmor policy is loaded, the kernel interprets the #include directive by pulling in the contents of the specified abstraction file. This keeps your security posture consistent; if the underlying requirements for "nameservice" change (perhaps due to an OS update), you only need to update the abstraction file rather than auditing every application profile on the system.
This modular approach ensures that your security policies are easier to read, maintain, and audit for potential leaks.
Key Takeaway: Use #include to load modular abstractions like <abstractions/nameservice> or <abstractions/base> to simplify your profile maintenance and ensure consistent security permissions across common system tasks.