Hardening Ubuntu: Using AppArmor to Sandbox Applications

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Why Standard Linux Permissions Fall Short

Standard Linux security uses Discretionary Access Control (DAC). This system relies on users, groups, and basic permissions (rwx). If a web server process runs as www-data, it can access any file owned by that user. This creates a massive blind spot. If an attacker exploits a vulnerability in your PHP code, they inherit those permissions. They can immediately scrape your configuration files, read source code, or exfiltrate sensitive data from temporary directories.

A few years ago, a midnight SSH brute-force attack on my personal server forced me to rethink my strategy. I realized that blocking the front door isn’t enough. You must ensure that even if an intruder gets inside, they are locked in a small, empty room. This is Mandatory Access Control (MAC). On Ubuntu, AppArmor is the primary tool for this task.

AppArmor defines exactly what a program can touch. Can it read /etc/passwd? Can it write to /tmp? Can it open a network socket? If an action isn’t explicitly permitted in the AppArmor profile, the kernel blocks it. This happens even if the user running the process has full permissions to perform that action.

Understanding AppArmor Profiles and Modes

AppArmor uses profiles, which are plain-text files stored in /etc/apparmor.d/. Unlike SELinux, which uses complex labels, AppArmor is path-based. A profile for Nginx, for instance, lives at /etc/apparmor.d/usr.sbin.nginx.

Before you modify any settings, you must understand the two primary modes:

  • Enforce: The kernel strictly applies the policy. Any unauthorized attempt is blocked and logged.
  • Complain: The policy is not enforced. The application runs without restrictions, but AppArmor logs every action that would have been blocked. This mode is vital for testing new profiles without crashing production services.

Checking Your Current Security Status

Most Ubuntu 22.04 or 24.04 installations come with AppArmor enabled by default. Usually, you will find 30 to 50 profiles already active. Check your status with this command:

sudo aa-status

This provides a snapshot of loaded profiles and identifies which processes are currently restricted.

Hands-on: Securing a Custom Python Application

Let’s move beyond theoretical system services. We will secure a simple Python script to see the mechanics in action. Imagine a script that only needs to read a config file and append to a log.

1. Install the Toolkit

Ubuntu includes the core engine, but the profile generation tools are in an extra package. Install these to make your life easier:

sudo apt update
sudo apt install apparmor-utils apparmor-profiles

2. Create the Target Application

Save this script to /usr/local/bin/myapp.py:

#!/usr/bin/python3
# A script that reads a specific config and writes to a log

with open("/data/config.txt", "r") as f:
    print(f.read())

with open("/var/log/myapp.log", "a") as log:
    log.write("Access recorded\n")

Set the permissions: sudo chmod +x /usr/local/bin/myapp.py.

3. Generate the Profile Automatically

The aa-genprof utility streamlines the creation process. It places the profile in complain mode and monitors the application’s behavior.

sudo aa-genprof /usr/local/bin/myapp.py

While the tool is running, open a second terminal and execute your script: /usr/local/bin/myapp.py. Then, return to the first terminal and press S to scan the logs. The utility will ask if you want to allow or deny the actions it detected. For this script, permit access to the Python interpreter, the config file, and the log file.

4. Fine-Tuning the Profile

Sometimes the automated tool is too broad. You can manually edit the file at /etc/apparmor.d/usr.local.bin.myapp.py. A clean profile looks like this:

#include <tunables/global>

/usr/local/bin/myapp.py {
  #include <abstractions/base>
  #include <abstractions/python>

  /usr/bin/python3.[0-9]* ix,
  /data/config.txt r,
  /var/log/myapp.log w,
}

Here is what those flags mean:

  • r: Read access.
  • w: Write access.
  • ix: Execute the file and inherit the current security profile.

Management and Troubleshooting

Avoid moving a profile straight to enforce mode. I recommend staying in complain mode for at least 24 hours. This helps you catch edge cases like weekly cron jobs or log rotation scripts that might trigger a false positive.

Toggling Modes

To switch to complain mode for testing:

sudo aa-complain /etc/apparmor.d/usr.local.bin.myapp.py

Once you are certain the application works correctly, lock it down:

sudo aa-enforce /etc/apparmor.d/usr.local.bin.myapp.py

Interpreting Logs

AppArmor blocks happen at the kernel level. The application might just say “Permission Denied,” even if the file permissions look correct. To find the real cause, check the system logs:

sudo tail -f /var/log/syslog | grep -i apparmor

Look for DENIED messages. If you see a legitimate action being blocked, run aa-logprof. It will read these logs and prompt you to update your profile rules automatically.

Best Practices for Production

Managing security policies doesn’t have to be a chore. Follow these three rules:

  1. Leverage Abstractions: Use the #include <abstractions/base> directive. These are pre-made snippets for common system libraries. They prevent your profiles from becoming hundreds of lines long.
  2. Focus on Exposure: You don’t need to sandbox every binary. Prioritize internet-facing services like Nginx, your mail server, or custom APIs. These are the most likely entry points for an attacker.
  3. Backup Before Updating: Before running aa-logprof, copy your existing profile. Automated tools occasionally suggest rules that are too permissive, and having a backup makes it easy to revert.

Final Thoughts

AppArmor provides a robust defense-in-depth strategy. By moving beyond simple file permissions and sandboxing your applications, you limit the damage an attacker can do. It takes time to refine these profiles, but the security is worth it. Knowing your web server is physically unable to touch your backup folder provides immense peace of mind.

Share: