AppArmor: Profile Configuration
Introduction
AppArmor (Application Armor) is a Linux kernel security module that provides mandatory access control (MAC) through per-program profiles, offering an effective and user-friendly alternative to SELinux. Unlike traditional discretionary access control (DAC), AppArmor confines programs to a limited set of resources, significantly reducing the potential damage from security vulnerabilities or compromised applications.
Originally developed by Immunix and later acquired by Novell, AppArmor has become the default security framework for Ubuntu, SUSE Linux Enterprise, and several other distributions. Its profile-based approach makes it easier to configure and maintain compared to SELinux, while still providing robust application confinement and system protection.
This comprehensive guide covers everything you need to know about AppArmor profile configuration, from understanding basic concepts to creating custom profiles for your applications. Whether you're securing a web server, database, or custom application, this guide provides the knowledge and practical techniques needed to effectively implement AppArmor in production environments.
Understanding AppArmor and Security Context
What Is AppArmor?
AppArmor is a Mandatory Access Control (MAC) system implemented as a Linux Security Module (LSM) in the kernel. It restricts programs based on defined profiles that specify which files, capabilities, network access, and other resources each program can access.
How AppArmor Works
AppArmor operates using several key mechanisms:
1. Path-Based Security
Unlike SELinux which uses labels, AppArmor uses file paths to define access controls:
/usr/bin/application {
/etc/application/config r,
/var/log/application/** w,
/home/*/.application/ rw,
}
2. Profiles
Each confined application has a profile that defines:
- File access permissions (read, write, execute, append)
- Network access (TCP, UDP, specific protocols)
- Capability permissions (Linux capabilities like CAP_NET_ADMIN)
- Resource limits
- Child process execution
3. Operational Modes
AppArmor profiles can operate in two modes:
- Enforce mode: Policy is enforced, violations are blocked and logged
- Complain mode: Policy violations are logged but allowed (useful for profile development)
4. Include Directives
Profiles can include common abstractions:
#include <abstractions/base>
#include <abstractions/nameservice>
Why AppArmor Matters
AppArmor provides significant security benefits:
- Application containment: Limits damage from compromised applications
- Zero-day protection: Reduces exploit effectiveness even without patches
- Easy configuration: Path-based policies are more intuitive than label-based systems
- Low overhead: Minimal performance impact
- Complain mode: Allows safe profile development in production
- Compatibility: Works with standard Linux applications without modification
AppArmor vs. SELinux
| Feature | AppArmor | SELinux |
|---|---|---|
| Access Control | Path-based | Label-based |
| Configuration Complexity | Easier | More complex |
| Default on | Ubuntu, SUSE | RHEL, CentOS, Fedora |
| Learning Curve | Moderate | Steep |
| Flexibility | Good | Excellent |
| Policy Development | Simpler | More powerful but complex |
Common Use Cases
- Confining web servers (Apache, Nginx)
- Securing database servers (MySQL, PostgreSQL)
- Protecting network services (OpenSSH, DNS, mail servers)
- Isolating containerized applications
- Restricting custom applications
- Compliance with security frameworks
Prerequisites
Before configuring AppArmor profiles, ensure you have:
System Requirements
- Operating System: Ubuntu, Debian, SUSE Linux Enterprise, openSUSE, or other AppArmor-enabled distribution
- Kernel: Linux kernel 2.6.36 or later with AppArmor support
- Root Access: Administrative privileges required
- Disk Space: Adequate space for logs and profile files
Required Knowledge
- Basic Linux system administration
- Understanding of file permissions and paths
- Command-line proficiency
- Familiarity with system services
- Basic understanding of Linux security concepts
Software Requirements
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra
SUSE/openSUSE:
sudo zypper install apparmor-parser apparmor-profiles apparmor-utils
Verification:
sudo aa-status
Verify AppArmor is Active
Check AppArmor is loaded:
sudo systemctl status apparmor
Check kernel module:
sudo cat /sys/module/apparmor/parameters/enabled
Should return Y
View AppArmor filesystem:
sudo ls /sys/kernel/security/apparmor/
Step-by-Step AppArmor Profile Configuration
Step 1: Check AppArmor Status
View detailed AppArmor status:
sudo aa-status
Expected output:
apparmor module is loaded.
50 profiles are loaded.
40 profiles are in enforce mode.
/sbin/dhclient
/usr/bin/man
/usr/lib/NetworkManager/nm-dhcp-client.action
...
10 profiles are in complain mode.
/usr/bin/firefox
/usr/sbin/mysqld
...
5 processes have profiles defined.
5 processes are in enforce mode.
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
Check if a specific profile is loaded:
sudo aa-status | grep nginx
View profile mode:
sudo aa-status --pretty-print
Step 2: Understand Profile Locations
System profile directories:
/etc/apparmor.d/: Main directory for profiles/etc/apparmor.d/abstractions/: Reusable policy components/etc/apparmor.d/tunables/: Variable definitions/etc/apparmor.d/disable/: Disabled profiles/etc/apparmor.d/local/: Local customizations
View available profiles:
ls -l /etc/apparmor.d/
View profile abstractions:
ls -l /etc/apparmor.d/abstractions/
Step 3: Read and Understand an Existing Profile
View an example profile (Nginx):
sudo cat /etc/apparmor.d/usr.sbin.nginx
Example profile structure:
#include <tunables/global>
/usr/sbin/nginx {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/openssl>
capability dac_override,
capability net_bind_service,
capability setgid,
capability setuid,
/usr/sbin/nginx mr,
/etc/nginx/** r,
/etc/ssl/openssl.cnf r,
/var/log/nginx/* w,
/var/www/html/** r,
/run/nginx.pid rw,
# Local customizations
#include <local/usr.sbin.nginx>
}
Profile components explained:
#include <tunables/global>: Global variables#include <abstractions/...>: Common policy componentscapability: Linux capabilities the program needs- File paths with permissions:
r: readw: writem: memory mapx: executeix: inherit execute (inherit parent profile)px: profile execute (use child's profile)ux: unconfined execute (no profile)
Step 4: Generate a Profile Automatically
Use aa-genprof to create a new profile:
sudo aa-genprof /usr/bin/myapp
This launches an interactive profile generation tool:
- Start the application in another terminal
- Exercise all functionality of the application
- Return to aa-genprof terminal and press 'S' to scan logs
- Review suggested rules and accept/reject each
- Save the profile when complete
The tool will prompt for decisions:
Profile: /usr/bin/myapp
Execute: /etc/myapp/script.sh
(I)nherit / (P)rofile / (U)nconfined / (X) ix On / (D)eny / Abo(r)t / (F)inish
- (I)nherit: Use parent profile
- (P)rofile: Use separate profile
- (U)nconfined: No restriction
- (D)eny: Block access
Step 5: Create a Profile Manually
Create a basic profile for a custom application:
sudo nano /etc/apparmor.d/usr.local.bin.myapp
Basic profile template:
#include <tunables/global>
/usr/local/bin/myapp {
#include <abstractions/base>
#include <abstractions/nameservice>
# Executable
/usr/local/bin/myapp mr,
# Configuration files
/etc/myapp/** r,
/etc/myapp/config rw,
# Data directories
/var/lib/myapp/** rw,
# Log files
/var/log/myapp/* w,
/var/log/myapp/** rw,
# Temporary files
/tmp/myapp-* rw,
/run/myapp.pid rw,
# Capabilities
capability dac_override,
capability net_bind_service,
# Network access
network inet stream,
network inet dgram,
# Child processes
/usr/bin/bash ix,
/usr/bin/cat rix,
# Local customizations
#include <local/usr.local.bin.myapp>
}
Save and load the profile:
sudo apparmor_parser -r /etc/apparmor.d/usr.local.bin.myapp
Step 6: Switch Profiles Between Enforce and Complain Modes
Put a profile in complain mode:
sudo aa-complain /usr/sbin/nginx
Put a profile in enforce mode:
sudo aa-enforce /usr/sbin/nginx
Put all profiles in complain mode:
sudo aa-complain /etc/apparmor.d/*
Verify mode change:
sudo aa-status
Step 7: Test and Refine Profiles
Put profile in complain mode for testing:
sudo aa-complain /usr/local/bin/myapp
Monitor denials in real-time:
sudo tail -f /var/log/syslog | grep DENIED
Or on systems using journald:
sudo journalctl -f | grep DENIED
Exercise the application:
Test all functionality to generate comprehensive logs.
Use aa-logprof to update profile:
sudo aa-logprof
This tool:
- Analyzes logs for AppArmor denials
- Suggests additions to profiles
- Allows you to approve/reject each suggestion
- Updates profiles automatically
After refinement, switch to enforce mode:
sudo aa-enforce /usr/local/bin/myapp
Step 8: Disable or Remove Profiles
Disable a profile (move to disabled directory):
sudo aa-disable /usr/sbin/nginx
This creates a symlink in /etc/apparmor.d/disable/
Re-enable a disabled profile:
sudo aa-enforce /usr/sbin/nginx
Remove a profile completely:
sudo rm /etc/apparmor.d/usr.sbin.nginx
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.nginx
Unload a profile from kernel:
sudo aa-remove-unknown
Step 9: Create Local Customizations
Instead of modifying main profiles, use local includes:
sudo nano /etc/apparmor.d/local/usr.sbin.nginx
Add custom rules:
# Allow access to custom web directory
/srv/www/** r,
# Allow PHP-FPM socket
/run/php/php8.1-fpm.sock rw,
# Custom log location
/custom/logs/nginx/* w,
Reload the profile:
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
Local customizations survive package updates.
Advanced AppArmor Hardening Tips
1. Use Abstractions Effectively
Common useful abstractions:
#include <abstractions/base> # Essential system files
#include <abstractions/nameservice> # DNS, NSS lookups
#include <abstractions/ssl_certs> # SSL certificate access
#include <abstractions/openssl> # OpenSSL libraries
#include <abstractions/mysql> # MySQL client
#include <abstractions/php> # PHP execution
#include <abstractions/apache2-common> # Apache common files
View abstraction contents:
cat /etc/apparmor.d/abstractions/base
2. Implement Network Restrictions
Restrict network access precisely:
# Allow only IPv4 TCP
network inet stream,
# Allow only IPv6 UDP
network inet6 dgram,
# Allow Unix domain sockets
network unix stream,
# Deny all other network
deny network,
Protocol-specific restrictions:
network inet stream tcp,
network inet dgram udp,
network netlink raw,
3. Use Profile Variables (Tunables)
Define variables in /etc/apparmor.d/tunables/myapp:
@{MYAPP_HOME}=/opt/myapp
@{MYAPP_CONFIG}=/etc/myapp
@{MYAPP_DATA}=/var/lib/myapp
Use in profiles:
#include <tunables/myapp>
/usr/local/bin/myapp {
@{MYAPP_CONFIG}/** r,
@{MYAPP_DATA}/** rw,
@{MYAPP_HOME}/bin/* rix,
}
4. Implement Child Profile Transitions
Define child profile for scripts:
/usr/local/bin/myapp {
# Main application rules
/usr/local/bin/myapp mr,
# Execute helper script with separate profile
/usr/local/bin/helper.sh px -> myapp_helper,
}
profile myapp_helper {
#include <abstractions/base>
#include <abstractions/bash>
/usr/local/bin/helper.sh r,
/tmp/helper-* rw,
}
5. Use Conditional Rules
Platform-specific rules:
#include <tunables/global>
/usr/bin/myapp {
#include <abstractions/base>
# Common rules
/usr/bin/myapp mr,
# Conditional include based on distribution
#include if exists <local/usr.bin.myapp>
}
6. Implement Deny Rules
Explicitly deny sensitive paths:
/usr/local/bin/myapp {
#include <abstractions/base>
# Normal access
/etc/myapp/** r,
# Explicitly deny sensitive files
deny /etc/shadow r,
deny /etc/gshadow r,
deny /etc/ssh/ssh_host_* r,
# Deny write to config
deny /etc/myapp/secure.conf w,
}
7. Implement File Locking
Allow specific file operations:
/var/lib/myapp/database.db rwk,
Permissions:
r: readw: writek: lockl: link
8. Profile Stacking for Containers
Create container-aware profiles:
profile docker-default flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
network,
capability,
file,
umount,
deny @{PROC}/* w,
deny @{PROC}/{[^1]*,1/[^s]*,1/s[^y]*} w,
}
Verification and Testing
Verify Profile Syntax
Test profile syntax before loading:
sudo apparmor_parser -p /etc/apparmor.d/usr.local.bin.myapp
Dry-run profile loading:
sudo apparmor_parser -n -r /etc/apparmor.d/usr.local.bin.myapp
Test Profile Enforcement
Start application and check its confinement:
ps auxZ | grep myapp
Should show profile name instead of unconfined.
Check process context:
cat /proc/$(pidof myapp)/attr/current
Monitor Profile Violations
Real-time monitoring:
sudo tail -f /var/log/syslog | grep apparmor
Or with journald:
sudo journalctl -f | grep apparmor
Search for specific denials:
sudo grep DENIED /var/log/syslog | grep myapp
Analyze denials with aa-notify:
sudo aa-notify -p -f /var/log/syslog
Verify Profile Updates
After making changes, verify reload:
sudo apparmor_parser -r /etc/apparmor.d/usr.local.bin.myapp
sudo aa-status | grep myapp
Check profile cache:
ls -l /var/cache/apparmor/
Test Complete Application Functionality
Testing checklist:
- Start application and verify it runs
- Test all major features
- Check for denied operations in logs
- Verify child processes work correctly
- Test network connectivity if required
- Verify file access permissions
- Check capability requirements
Troubleshooting Common Issues
Issue 1: Profile Syntax Error
Symptoms: Parser errors when loading profile
Solutions:
-
Check syntax:
sudo apparmor_parser -p /etc/apparmor.d/usr.bin.myapp -
Common syntax issues:
- Missing comma after permissions
- Unclosed braces
- Invalid permission characters
- Incorrect path format
-
Example correction:
# Wrong /etc/myapp/config rw # Correct /etc/myapp/config rw,
Issue 2: Application Won't Start
Symptoms: Application fails to launch with AppArmor loaded
Solutions:
-
Check denials immediately:
sudo tail -20 /var/log/syslog | grep DENIED -
Put profile in complain mode:
sudo aa-complain /usr/bin/myapp -
Start application and monitor:
sudo journalctl -f | grep apparmor -
Update profile with aa-logprof:
sudo aa-logprof -
Return to enforce mode after fixes:
sudo aa-enforce /usr/bin/myapp
Issue 3: Permission Denied Errors
Symptoms: Application runs but fails specific operations
Solutions:
-
Identify missing permission:
sudo grep "myapp" /var/log/syslog | grep DENIED | tail -5 -
Add to local profile:
sudo nano /etc/apparmor.d/local/usr.bin.myapp -
Add the denied path/capability:
/path/to/denied/file rw, -
Reload profile:
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.myapp
Issue 4: Profile Not Loading
Symptoms: Profile exists but not showing in aa-status
Solutions:
-
Manually load profile:
sudo apparmor_parser -a /etc/apparmor.d/usr.bin.myapp -
Check for errors:
sudo systemctl status apparmor -
Verify profile isn't disabled:
ls /etc/apparmor.d/disable/ -
Restart AppArmor service:
sudo systemctl restart apparmor
Issue 5: Child Process Failures
Symptoms: Main application works, but child processes fail
Solutions:
-
Check for child process denials:
sudo grep DENIED /var/log/syslog | grep exec -
Add appropriate execution rule:
# Inherit parent profile /usr/bin/child ix, # Use child's profile /usr/bin/child px, # Unconfined execution (less secure) /usr/bin/child ux, -
Create separate child profile if needed:
profile myapp_child { #include <abstractions/base> /usr/bin/child mr, }
Issue 6: Network Access Denied
Symptoms: Application cannot connect to network
Solutions:
-
Check for network denials:
sudo grep DENIED /var/log/syslog | grep network -
Add network permissions:
network inet stream, network inet6 stream, -
For specific protocols:
network inet stream tcp, network inet dgram udp,
Issue 7: Capability Denials
Symptoms: Operations requiring Linux capabilities fail
Solutions:
-
Identify required capability:
sudo grep "capability" /var/log/syslog | grep DENIED -
Add to profile:
capability net_bind_service, capability sys_admin, -
Common capabilities:
dac_override: Bypass file permissionsnet_bind_service: Bind to ports < 1024setuid/setgid: Change user/groupsys_admin: Various admin operations
Best Practices for AppArmor Management
1. Profile Development Workflow
- Start in complain mode during development
- Exercise all functionality to capture complete access patterns
- Review and refine using aa-logprof
- Test thoroughly in complain mode
- Enable enforce mode after validation
- Monitor production for unexpected denials
- Iterate as needed when adding features
2. Profile Organization
- Use abstractions for common patterns
- Create local includes for customizations
- Use variables for paths that may change
- Document unusual rules with comments
- Version control profile files
- Organize by application or service type
3. Security Hardening
- Principle of least privilege: Grant minimum necessary access
- Explicit denials: Deny access to sensitive system files
- Avoid unconfined execution: Use ix or px instead of ux
- Restrict capabilities: Only grant required capabilities
- Network restrictions: Limit network access by protocol/type
- Regular audits: Review profiles quarterly
4. Operational Practices
- Monitor regularly: Set up log monitoring for denials
- Test updates: Test profile changes in non-production first
- Backup profiles: Maintain backups before modifications
- Documentation: Document why specific rules exist
- Gradual deployment: Roll out new profiles incrementally
5. Performance Considerations
- Profile caching: AppArmor caches compiled profiles for performance
- Avoid excessive wildcards: More specific rules perform better
- Use abstractions: Shared abstractions are cached
- Monitor overhead: Generally minimal but monitor in high-performance environments
6. Compliance and Auditing
- Profile inventory: Maintain list of all custom profiles
- Change management: Track all profile modifications
- Regular reviews: Audit profiles for unnecessary permissions
- Compliance mapping: Document how profiles meet security requirements
- Incident response: Include AppArmor logs in security investigations
7. Container Security
- Default container profile: Use docker-default or custom profiles
- Kubernetes integration: Use AppArmor annotations
- Profile per container: Create specific profiles for container applications
- Test in development: Verify profiles in development environments
Conclusion
AppArmor provides powerful, path-based mandatory access control that enhances Linux security through application confinement. Its intuitive profile syntax and complain mode make it more accessible than alternatives like SELinux, while still providing robust protection against compromised applications and security vulnerabilities.
Key takeaways:
- Path-based approach: Easier to understand and configure than label-based systems
- Complain mode: Invaluable for safe profile development and testing
- Profile flexibility: Supports varying levels of restriction per application
- Local customizations: Preserve customizations across updates
- Performance: Minimal overhead with significant security benefits
- Comprehensive tooling: aa-genprof, aa-logprof, and aa-status simplify management
By following the practices outlined in this guide, you implement effective application confinement that significantly reduces your attack surface. AppArmor is particularly valuable for:
- Confining network-facing services
- Protecting against zero-day exploits
- Implementing defense-in-depth security
- Meeting compliance requirements
- Securing containerized applications
Remember that AppArmor is most effective when profiles are:
- Developed with complete understanding of application behavior
- Tested thoroughly in complain mode before enforcement
- Regularly reviewed and updated as applications evolve
- Monitored for denials that may indicate attacks or misconfigurations
Start with system-provided profiles for common applications, customize them to your needs using local includes, and gradually extend AppArmor protection to custom applications. With proper implementation and maintenance, AppArmor becomes an invaluable component of your security strategy, providing strong application isolation with manageable administrative overhead.


