Securing Your Server: More Than Just Firewalls and SSH
Those of us in system administration are no strangers to server hardening. We usually start with the basics: configuring the firewall with ufw, changing the SSH port, disabling password-based login, or installing Fail2Ban. But that’s just the outer perimeter of defense. What happens if an old WordPress plugin on your server has an RCE (Remote Code Execution) vulnerability? The attacker is already inside. What will they do next?
I once had an unforgettable midnight experience. My personal server was being hit by an SSH brute-force bot with thousands of requests per minute. Luckily, I caught it in time and didn’t suffer any serious losses. Since then, I’ve always been obsessed with securing from the inside out. Once an attacker gets inside, even with a limited user account like www-data, they won’t sit still. Their goal is privilege escalation to gain root access. This is where the real battle begins.
So, how do you prevent a compromised process from “acting up”? This is where Mandatory Access Control (MAC) mechanisms come into play.
DAC vs. MAC: Two Approaches to Restricting Processes
On Linux, there are two main philosophies for controlling a program’s access rights:
- Discretionary Access Control (DAC): This is the familiar
rwx(read, write, execute) permission system for users, groups, and others. The problem with it is that it’s “discretionary.” If a program runs with the permissions of theapp_user, it inherits all the rights thatapp_userpossesses. When the program is hacked, the attacker has those same rights. - Mandatory Access Control (MAC): This is the “mandatory” mechanism. The kernel enforces a predefined security policy to decide what a process is allowed to do. Importantly, even if the process is running as root, it is still restricted by this policy.
The Two ‘Giants’ of the MAC World: AppArmor and SELinux
When it comes to MAC on Linux, the two most prominent names are SELinux and AppArmor.
- SELinux (Security-Enhanced Linux): Developed by the NSA, it is extremely powerful, detailed, and… complex. It applies “labeling” to every object on the system (files, processes, ports) and defines rules for interaction between these labels. SELinux is the standard on Red Hat-family distros (CentOS, Fedora, RHEL).
- AppArmor (Application Armor): More approachable, it operates based on file paths. Instead of labeling, you define a “profile” for each application, listing the paths it is allowed or denied access to. AppArmor is the default choice on Debian-family distros (Ubuntu, Debian).
AppArmor or SELinux? Which One Is Easier to Manage?
This is a classic question. If you ask a security expert, they might say SELinux is superior due to its extremely granular control. But for DevOps or SysAdmins managing dozens of servers, “ease of management” and “ease of debugging” are top priorities.
Why is AppArmor so popular?
- Easy to learn, easy to write policies: AppArmor’s profile syntax is very intuitive. You just list paths and permissions like
r,w,x. - Built into Ubuntu: You don’t need to install anything extra. Most core services already have profiles.
- Excellent supporting tools: Utilities like
aa-genprofandaa-logprofmake creating new profiles much simpler. - Easy to diagnose errors: Because profiles are path-based, you can easily figure out why an application’s access is being denied.
What are the disadvantages of AppArmor?
- Less granular than SELinux. For example, it can’t set permissions based on context or a file’s dynamic security label.
In reality, the meme “How to fix SELinux errors? -> setenforce 0” (which means turning it off) exists for a reason. SELinux is notoriously difficult to debug and can cause unforeseen errors. In contrast, AppArmor strikes an excellent balance between security and usability. It’s powerful enough to block most common privilege escalation attacks. More importantly, it won’t make you pull your hair out every time you deploy a new application.
Step-by-Step Guide to Configuring AppArmor on Ubuntu
Enough theory, let’s get practical. I’ll guide you through creating an AppArmor profile from scratch. For demonstration, let’s create a profile for Nginx (even though Nginx already has a pre-built profile in practice).
Step 1: Check AppArmor Status
First, let’s see how AppArmor is running on your server.
sudo aa-status
This command will list the loaded profiles. You’ll see which profiles are in enforce mode (enforces and blocks violations) and which are in complain mode (only logs violations, does not block).
Step 2: Understand Enforce and Complain Modes
- Enforce mode: The official operational mode. Any action that violates the policy will be blocked and recorded in the system log.
- Complain mode: The “learning” and debugging mode. AppArmor will not block any actions. Instead, it just logs all violations. This mode is extremely useful when you are building a profile for a new application.
To switch a profile to complain mode (e.g., for Nginx):
sudo aa-complain /usr/sbin/nginx
And to re-enable enforce mode:
sudo aa-enforce /usr/sbin/nginx
Step 3: Hands-on: Automatically Generate a Profile with `aa-genprof`
This is the most interesting part. AppArmor can “learn” an application’s behavior to automatically generate a basic profile.
1. Put the application in Complain mode: Ensure AppArmor doesn’t block Nginx while we are “teaching” it.
sudo aa-complain /usr/sbin/nginx
2. Run the profile generator: Open a terminal and run the following command. It will start monitoring system logs.
sudo aa-genprof /usr/sbin/nginx
3. Interact with the application: Open another terminal. Now, you need to use Nginx so it performs the necessary actions. The more you interact with it, the more accurate the generated profile will be.
# Restart the service
sudo systemctl restart nginx
# Access the website
curl http://localhost
# Try accessing a non-existent page to generate an error log
curl http://localhost/non-existent-page
4. Scan logs and create rules: Go back to the terminal running aa-genprof and press the S key (Scan) for it to scan the logs and find the actions that Nginx performed.
The tool will ask you about each action one by one. For example:
Profile: /usr/sbin/nginx
Path: /var/log/nginx/access.log
Mode: w
(A)llow / (D)eny / (I)gnore / (N)ew / (G)lob / Glob with (E)xt / (S)kip / (F)inish
Here, it’s asking if you want to allow Nginx to write (w) to the access.log file. This is a valid action, so you press A (Allow). Carefully consider each request. If you’re not sure, you can press I (Ignore) to skip it. After reviewing all the actions, press S (Save) to save the new rules and F` (Finish) to complete the process.</p>
<h3>Step 4: Read and Manually Refine the Profile</h3>
<p>The profile you just created will be saved at <code>/etc/apparmor.d/usr.sbin.nginx. Let’s open this file to see its contents. It will look something like this:
#include <tunables/global>
/usr/sbin/nginx flags=(complain) {
#include <abstractions/base>
#include <abstractions/nginx>
capability dac_override,
capability net_bind_service,
capability setgid,
capability setuid,
network tcp,
/etc/nginx/nginx.conf r,
/var/log/nginx/access.log w,
/var/log/nginx/error.log w,
/var/www/html/** r,
}
The syntax is quite readable:
r: allow read.w: allow write.x: allow execute.*: wildcard for a part of a filename.**: wildcard for everything in a subdirectory.
You can absolutely edit this file by hand to add/remove permissions, then simply reload the profile.
Step 5: Activate and Monitor
Once you are satisfied with the profile, switch it to enforce mode:
sudo aa-enforce /usr/sbin/nginx
If you have just manually edited the profile file, use the following command to reload it:
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
How do you know if any actions are being blocked? Monitor the system logs in real-time:
sudo journalctl -f | grep 'apparmor="DENIED"'
If you see a DENIED line related to Nginx, it means your profile is missing a required permission. You’ll need to use aa-logprof to update it or edit the profile file manually, then reload.
Conclusion: An Often-Overlooked Layer of Protection
AppArmor is an extremely valuable but often overlooked layer of protection. It’s not a silver bullet for all security problems. However, it creates a solid barrier against the most common privilege escalation techniques. Once you get used to it, creating profiles for your custom-coded applications (Python, Node.js, Go) becomes incredibly fast.
Take an afternoon to learn and “lock down” your critical services with AppArmor. You’ll sleep much better at night. It is the second line of defense, the silent savior when the outer perimeter is breached.
