The Discretionary Access Flaw
Traditional Linux security relies on Discretionary Access Control (DAC)—specifically, the standard read/write/execute (rwx) file permissions assigned to users and groups. If a web server like Apache is running as the user www-data, it has access to any file that the www-data user owns. However, if a hacker exploits a vulnerability in a PHP script hosted by Apache, they gain a shell as www-data. They can immediately traverse the filesystem, read configuration files in /etc/ (if they are world-readable), and launch reverse shells in /tmp/.
To stop this, Linux uses Mandatory Access Control (MAC) systems like AppArmor (the default on Ubuntu and Debian). AppArmor completely ignores standard user permissions. Instead, it places a mathematical straightjacket directly onto the executable program itself. You can configure AppArmor so that the /usr/sbin/apache2 binary is mathematically forbidden from reading anything outside of /var/www/html/, and explicitly forbidden from executing /bin/bash. Even if the hacker gains full root privileges by exploiting a zero-day in Apache, AppArmor will violently terminate the Apache process the millisecond it attempts to open a shell, completely neutralizing the attack.
Step 1: Installing the AppArmor Utilities
While the AppArmor kernel module is active by default in modern Ubuntu, you need the userspace tools to build and manage profiles.
sudo apt-get update
sudo apt-get install apparmor-utils apparmor-profiles
Step 2: Understanding Complain vs. Enforce Mode
Building a Mandatory Access Control profile by hand is incredibly dangerous. If you guess incorrectly and block Apache from reading a required shared library, the web server will instantly crash in production.
AppArmor solves this with a training mode called Complain Mode. When a profile is in Complain mode, AppArmor allows the application to do whatever it wants, but it silently writes a log entry to /var/log/syslog every time the application violates the profile.
Let’s generate a baseline profile for a custom Python application located at /opt/my_app/server.py.
# Generate an empty skeleton profile and immediately put it in Complain mode
sudo aa-autodep /opt/my_app/server.py
sudo aa-complain /opt/my_app/server.py
Step 3: Training the Profile (aa-logprof)
Now that the application is running in Complain mode, you must thoroughly test it. Use the application exactly as a user would: upload files, query the database, trigger error logs. AppArmor is watching every single system call the Python script makes and writing it to the log.
Once you have thoroughly exercised the application, run the profile generator tool:
sudo aa-logprof
This is an interactive tool. It reads the syslog, sees everything the application attempted to do, and asks you if you want to add it to the profile. For example, it might prompt:
“Profile: /opt/my_app/server.py
Path: /etc/ssl/certs/ca-certificates.crt
Mode: r
Allow? (A)llow / (D)eny”
You press A to allow it. Once you finish the interactive prompt, AppArmor saves the completed whitelist to /etc/apparmor.d/opt.my_app.server.py.
Step 4: Enforcing the Profile
Now that the profile perfectly maps the application’s legitimate behavior, you must lock the cage.
sudo aa-enforce /opt/my_app/server.py
The application is now sandboxed. If it attempts to read any file or execute any binary not explicitly approved in the training phase, the Linux kernel will block the system call and return a “Permission Denied” error.
Manually Editing the Profile
You can manually audit the exact rules AppArmor is enforcing by opening the profile in a text editor.
sudo nano /etc/apparmor.d/opt.my_app.server.py
Inside, you will see explicit rules like:
# Allow read access to the config file
/opt/my_app/config.json r,
# Allow write access to the log directory
/var/log/my_app/* w,
# EXPLICIT DENY: Ensure it can never execute a bash shell, even if exploited
deny /bin/bash x,
If you modify the text file manually, you must reload the AppArmor parser for the new rules to take effect in the kernel without restarting the server:
sudo apparmor_parser -r /etc/apparmor.d/opt.my_app.server.py
With AppArmor actively enforcing this profile, the blast radius of a potential zero-day vulnerability in your Python application is reduced to practically zero.