Configure AppArmor security profiles for web servers and databases with custom policy enforcement

Intermediate 45 min Apr 03, 2026 34 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up mandatory access control for Nginx, Apache, MySQL, and PostgreSQL using AppArmor security profiles. Learn to create custom policies, debug profile violations, and implement advanced enforcement for production web servers and databases.

Prerequisites

  • Root or sudo access
  • Basic understanding of Linux file permissions
  • Web server or database already installed
  • Command line experience

What this solves

AppArmor provides mandatory access control (MAC) that restricts programs to a limited set of resources, preventing security breaches even if applications are compromised. This tutorial shows you how to configure custom AppArmor profiles for web servers and databases, enforce strict security policies, and debug profile violations in production environments.

Step-by-step installation

Install and enable AppArmor

AppArmor is pre-installed on Ubuntu but requires additional utilities on other distributions. Install the complete AppArmor suite with profile management tools.

sudo apt update
sudo apt install -y apparmor-utils apparmor-profiles apparmor-profiles-extra
sudo systemctl enable apparmor
sudo systemctl start apparmor
sudo dnf install -y apparmor-utils apparmor-profiles apparmor-parser
sudo systemctl enable apparmor
sudo systemctl start apparmor

Verify AppArmor status

Check that AppArmor is running and view the current profile status. This shows which profiles are loaded and their enforcement mode.

sudo apparmor_status

You should see output showing loaded profiles in enforce or complain mode. Enforce mode blocks violations while complain mode only logs them.

Install web servers and databases

Install the services we'll secure with AppArmor profiles. We'll use Nginx, Apache, MySQL, and PostgreSQL as examples.

sudo apt install -y nginx apache2 mysql-server postgresql postgresql-contrib
sudo systemctl enable nginx apache2 mysql postgresql
sudo systemctl start mysql postgresql
sudo dnf install -y nginx httpd mysql-server postgresql postgresql-server
sudo postgresql-setup --initdb
sudo systemctl enable nginx httpd mysqld postgresql
sudo systemctl start mysqld postgresql

Configure Nginx AppArmor profile

Create custom Nginx profile

Create a restrictive AppArmor profile for Nginx that allows only necessary file access and network operations. This profile prevents Nginx from accessing sensitive system files.

#include 

/usr/sbin/nginx {
  #include 
  #include 
  #include 

  capability dac_override,
  capability setuid,
  capability setgid,
  capability net_bind_service,

  /usr/sbin/nginx mr,
  /etc/nginx/** r,
  /var/log/nginx/** w,
  /var/lib/nginx/** rw,
  /run/nginx.pid rw,
  /var/cache/nginx/** rw,

  # Web content directories
  /var/www/** r,
  /usr/share/nginx/** r,

  # SSL certificates
  /etc/ssl/certs/** r,
  /etc/ssl/private/** r,
  /etc/letsencrypt/** r,

  # Network access
  network inet stream,
  network inet6 stream,

  # Process management
  /usr/sbin/nginx Px,
  
  # Deny dangerous operations
  audit deny /etc/passwd r,
  audit deny /etc/shadow r,
  audit deny /root/** rwx,
  audit deny /home/** rwx,
}

Load and test Nginx profile

Load the profile in complain mode first to test for violations, then switch to enforce mode. Complain mode logs violations without blocking them.

sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
sudo aa-complain /usr/sbin/nginx
sudo systemctl restart nginx
sudo systemctl status nginx

Monitor and adjust Nginx profile

Test Nginx functionality and check for AppArmor denials. Use logprof to automatically update the profile based on logged violations.

curl http://localhost
sudo dmesg | grep -i apparmor
sudo aa-logprof /usr/sbin/nginx

Review suggested changes carefully. Only allow access that Nginx actually needs for your specific configuration.

Enable Nginx profile enforcement

Once testing is complete, switch to enforce mode to actively block policy violations.

sudo aa-enforce /usr/sbin/nginx
sudo systemctl restart nginx

Configure Apache AppArmor profile

Create Apache profile

Apache requires different permissions than Nginx, especially for module loading and CGI execution. This profile accommodates Apache's modular architecture.

#include 

/usr/sbin/apache2 {
  #include 
  #include 
  #include 
  #include 

  capability dac_override,
  capability setuid,
  capability setgid,
  capability net_bind_service,
  capability sys_tty_config,

  /usr/sbin/apache2 mr,
  /etc/apache2/** r,
  /var/log/apache2/** w,
  /var/lib/apache2/** rw,
  /run/apache2/** rw,
  /var/cache/apache2/** rw,

  # Web content
  /var/www/** r,
  /usr/share/apache2/** r,

  # SSL certificates
  /etc/ssl/certs/** r,
  /etc/ssl/private/** r,
  /etc/letsencrypt/** r,

  # Module support
  /usr/lib/apache2/modules/** mr,
  /etc/mime.types r,

  # PHP support (if needed)
  /usr/bin/php* Px,
  /etc/php/** r,

  # Network access
  network inet stream,
  network inet6 stream,
  network unix stream,

  # Security denials
  audit deny /etc/passwd r,
  audit deny /etc/shadow r,
  audit deny /root/** rwx,
}

Load Apache profile

Load the Apache profile and test in complain mode before enforcing.

sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.apache2
sudo aa-complain /usr/sbin/apache2
sudo systemctl restart apache2
sudo systemctl status apache2

Configure MySQL AppArmor profile

Create MySQL security profile

MySQL requires access to data directories, socket files, and configuration. This profile restricts MySQL to only necessary system resources.

#include 

/usr/sbin/mysqld {
  #include 
  #include 
  #include 
  #include 

  capability dac_override,
  capability setuid,
  capability setgid,
  capability sys_resource,
  capability net_bind_service,

  /usr/sbin/mysqld mr,
  /etc/mysql/** r,
  /var/lib/mysql/** rwk,
  /var/log/mysql/** rw,
  /run/mysqld/** rw,
  /tmp/** rw,

  # Configuration files
  /etc/hosts.allow r,
  /etc/hosts.deny r,

  # SSL certificates for MySQL
  /etc/ssl/certs/** r,
  /etc/mysql/ssl/** r,

  # Network access
  network inet stream,
  network inet6 stream,
  network unix stream,

  # Memory management
  owner /proc/*/status r,
  /sys/devices/system/node/*/meminfo r,

  # Security restrictions
  audit deny /etc/passwd r,
  audit deny /etc/shadow r,
  audit deny /root/** rwx,
  audit deny /home/** rwx,
  deny /bin/** x,
  deny /usr/bin/** x,
}

Test MySQL profile

Load the MySQL profile and verify database functionality. Check for any access violations in the logs.

sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
sudo aa-complain /usr/sbin/mysqld
sudo systemctl restart mysql
sudo mysql -u root -e "SELECT version();"
sudo journalctl -u mysql | tail -20

Configure PostgreSQL AppArmor profile

Create PostgreSQL profile

PostgreSQL has a more complex process model with background workers. This profile accounts for PostgreSQL's architecture while maintaining security.

#include 

/usr/lib/postgresql/*/bin/postgres {
  #include 
  #include 
  #include 
  #include 

  capability dac_override,
  capability setuid,
  capability setgid,
  capability sys_resource,
  capability ipc_lock,

  /usr/lib/postgresql/*/bin/postgres mr,
  /etc/postgresql/** r,
  /var/lib/postgresql/** rwk,
  /var/log/postgresql/** rw,
  /run/postgresql/** rw,
  /tmp/** rw,

  # Shared memory and IPC
  owner /dev/shm/PostgreSQL.* rw,
  /proc/*/stat r,
  /proc/*/statm r,

  # SSL support
  /etc/ssl/certs/** r,
  /etc/ssl/private/** r,

  # Network access
  network inet stream,
  network inet6 stream,
  network unix stream,

  # Process management
  /usr/lib/postgresql/*/bin/postgres Px,

  # Extensions and libraries
  /usr/lib/postgresql/*/lib/** mr,
  /usr/share/postgresql/** r,

  # Security denials
  audit deny /etc/passwd r,
  audit deny /etc/shadow r,
  audit deny /root/** rwx,
  deny /bin/sh x,
  deny /usr/bin/** x,
}

Load PostgreSQL profile

Apply the PostgreSQL profile and test database connectivity. PostgreSQL profiles may need adjustment based on your version and extensions.

sudo apparmor_parser -r /etc/apparmor.d/usr.lib.postgresql.*.bin.postgres
sudo aa-complain '/usr/lib/postgresql/*/bin/postgres'
sudo systemctl restart postgresql
sudo -u postgres psql -c "SELECT version();"
sudo journalctl -u postgresql | tail -20

Advanced policy enforcement and monitoring

Enable strict enforcement

Switch all profiles to enforce mode for production security. This blocks any access not explicitly allowed in the profiles.

sudo aa-enforce /usr/sbin/nginx
sudo aa-enforce /usr/sbin/apache2
sudo aa-enforce /usr/sbin/mysqld
sudo aa-enforce '/usr/lib/postgresql/*/bin/postgres'

Configure AppArmor notifications

Set up automatic monitoring for AppArmor violations. This script checks logs and sends alerts when profiles are violated.

#!/bin/bash

AppArmor monitoring script

LOGFILE="/var/log/apparmor-violations.log" TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

Check for new AppArmor denials

DENIALS=$(dmesg | grep -i "apparmor.*denied" | tail -10) if [ ! -z "$DENIALS" ]; then echo "[$TIMESTAMP] AppArmor violations detected:" >> $LOGFILE echo "$DENIALS" >> $LOGFILE # Optional: Send email alert # echo "$DENIALS" | mail -s "AppArmor Violation Alert" admin@example.com fi

Clear old dmesg entries

sudo dmesg -c > /dev/null
sudo chmod +x /usr/local/bin/apparmor-monitor

Create monitoring systemd timer

Set up automated monitoring that runs every 5 minutes to catch violations quickly.

[Unit]
Description=AppArmor Violation Monitor
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/apparmor-monitor
User=root
[Unit]
Description=Run AppArmor Monitor every 5 minutes
Requires=apparmor-monitor.service

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now apparmor-monitor.timer

Create profile backup script

Backup your custom profiles to prevent loss during system updates. This ensures your security policies persist.

#!/bin/bash

Backup custom AppArmor profiles

BACKUP_DIR="/etc/apparmor.d/backups" DATE=$(date +%Y%m%d-%H%M%S) sudo mkdir -p $BACKUP_DIR

Backup custom profiles

sudo tar -czf $BACKUP_DIR/apparmor-profiles-$DATE.tar.gz /etc/apparmor.d/usr.* echo "AppArmor profiles backed up to $BACKUP_DIR/apparmor-profiles-$DATE.tar.gz"

Keep only last 10 backups

sudo find $BACKUP_DIR -name "apparmor-profiles-*.tar.gz" -type f | sort -r | tail -n +11 | xargs -r sudo rm
sudo chmod +x /usr/local/bin/backup-apparmor-profiles
sudo /usr/local/bin/backup-apparmor-profiles

Profile debugging and troubleshooting

Debug profile violations

When services fail after enabling AppArmor, use these debugging techniques to identify missing permissions.

# Check recent AppArmor denials
sudo dmesg | grep -i "apparmor.*denied" | tail -20

Monitor real-time violations

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

Use aa-decode to understand denial messages

sudo dmesg | grep -i "apparmor.*denied" | aa-decode

Generate profiles automatically

Use aa-genprof to create initial profiles by monitoring application behavior. This creates a baseline that you can then restrict further.

# Generate profile for a new application
sudo aa-genprof /path/to/application

Follow the prompts and test your application

Choose 'S' to save the profile when done

Test profile changes safely

Always test profile changes in complain mode before enforcing. This prevents service outages from overly restrictive policies.

# Switch to complain mode for testing
sudo aa-complain /usr/sbin/nginx

Test your application thoroughly

Check logs for violations

If everything works, enable enforcement

sudo aa-enforce /usr/sbin/nginx

Verify your setup

# Check AppArmor status
sudo apparmor_status

Verify all profiles are in enforce mode

sudo aa-status | grep enforce

Test web servers

curl -I http://localhost curl -I http://localhost:8080

Test databases

sudo mysql -u root -e "SELECT 1;" sudo -u postgres psql -c "SELECT version();"

Check for recent violations

sudo dmesg | grep -i "apparmor.*denied" | tail -5

Verify monitoring is active

sudo systemctl status apparmor-monitor.timer
Note: If you see AppArmor denials in dmesg, review your profiles. Some applications may need additional permissions based on your specific configuration.

Common issues

SymptomCauseFix
Service won't start after profileMissing required permissionsSwitch to complain mode, test, use aa-logprof to add permissions
Web server 403 errorsProfile blocking web content accessAdd /var/www/** r or your document root to profile
Database connection failuresSocket file access deniedAdd /run/mysqld/ rw or /run/postgresql/ rw to profile
SSL certificate errorsCertificate directory not accessibleAdd /etc/ssl/ r and /etc/letsencrypt/ r to profile
Profile syntax errorsInvalid AppArmor syntaxUse sudo apparmor_parser -Q /etc/apparmor.d/profile to check syntax
Profiles not loading on bootAppArmor service not enabledRun sudo systemctl enable apparmor and reboot
Warning: Never disable AppArmor to fix service issues. Always debug the profile and add only the minimum required permissions. Disabling AppArmor removes an important security layer.

Next steps

Automated install script

Run this to automate the entire setup

#apparmor #mandatory-access-control #security-profiles #nginx-security #mysql-security #policy-enforcement

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer