In this lesson, you will learn how to design AppArmor profiles that permit multiple users to execute the same shared application while ensuring their personal data remains strictly isolated.
Balancing Shared Execution with Private Data
In a multi-user environment, you often have a single binary—such as a data processing script or a customized text editor—installed in a global location like /usr/local/bin/. While the executable is shared, the data it touches must be private to each user. If you simply write a broad rule allowing access to /home/*, you create a security hole where any user could potentially read another’s sensitive files through the application.
To solve this, we leverage AppArmor variables combined with path-based restrictions. Instead of hardcoding paths, we define a variable (e.g., @{HOME}) to dynamically scope access to the user currently running the process.
Implementing User Isolation
When building the profile, we grant read/write access to the specific locations where the application needs to store state, but we restrict it to the current user's home directory.
Consider a shared tool that processes logs in a ~/work/ directory:
profile shared-processor /usr/local/bin/processor {
# Allow execution of shared libraries and binaries
/usr/local/bin/processor mr,
/usr/lib/** mr,
# Use the @{HOME} variable to enforce isolation
owner @{HOME}/work/ rw,
owner @{HOME}/work/** rw,
# Deny access to other users' directories
deny /home/*/** w,
}
Why this works
- The
ownerkeyword: This is the most critical component. It tells the kernel to verify that the file being accessed is owned by the user ID (UID) of the process currently running the profile. Even if the application attempts to open/home/bob/work/data.txtwhile running asalice, the kernel will block the access because the UIDs do not match. - Explicit Deny: By adding a
denyrule for the broader/home/*tree, we create a "fail-safe" layer that prevents the application from accidentally escaping its intended user-scope if the profile logic is modified improperly.
Key Takeaway: Use the owner keyword in conjunction with AppArmor variables like @{HOME} to enforce a strict boundary that prevents shared applications from accessing data outside the current user's filesystem scope.