In this lesson, you will learn how to restrict which programs a confined application is permitted to execute, ensuring that a compromised process cannot spawn unauthorized binaries to escalate privileges or bypass security controls.
The Concept of Execution Transitions
By default, when a confined process (like a web server) executes another binary, the new process typically inherits the security attributes of the parent. However, in a robust security model, we often want to strictly define the "execution domain" for these child processes. This prevents an attacker from tricking your application into running system tools, shells, or malicious scripts.
In AppArmor, we control these transitions by explicitly defining allowed execution rules within the profile. When you restrict transitions, you are effectively creating a "sandbox" that dictates the exact boundaries of what the application is allowed to launch.
Defining Allowed Transitions
If you have an application that only needs to run a specific log-rotate script, you should not leave its execution permissions open-ended. Instead of allowing general execution, you define exactly which binary is permissible:
/usr/bin/my-app {
# Deny all by default, then allow specific paths
/usr/local/bin/log-helper ix,
# Deny execution of everything else
deny /** x,
}
In the example above, the ix (inherit execution) modifier allows the program to run /usr/local/bin/log-helper while maintaining the current security profile. By adding the deny /** x rule, you explicitly block the execution of any other binary on the system, even if the user or the process attempts to call /bin/bash or other common attack vectors.
Why This Matters
If an attacker exploits a vulnerability in my-app to gain code execution, they will typically try to launch a shell to interact with the system. Without defined domain transitions, they might succeed. By enforcing strict execution rules, the kernel rejects the execution attempt of any unauthorized binary, triggering an audit event and stopping the compromise in its tracks. This is a primary layer of defense against lateral movement and privilege escalation.
Key Takeaway: Always use explicit allow-lists for execution paths and combine them with a global deny rule to ensure your application can only launch the specific binaries required for its legitimate function.