AppArmor Setup บน Linux — Profile, Mode, และการสร้าง Custom Security Policy

AppArmor (Application Armor) เป็นระบบ Mandatory Access Control (MAC) ที่ใช้ Profile-based Security ควบคุมว่า Process แต่ละตัวสามารถเข้าถึงไฟล์, Network, และ Capability ใดได้บ้าง แตกต่างจาก SELinux ที่ใช้ Security Context บน Inode, AppArmor ใช้ Path-based โดยอ้างอิงชื่อ Path ของไฟล์ ทำให้เขียนและอ่าน Policy ง่ายกว่ามาก

บทความนี้อธิบายการติดตั้งและตั้งค่า AppArmor บน Ubuntu/Debian ตั้งแต่การตรวจสอบ Status, การใช้ Mode (Enforce/Complain/Disabled), การสร้างและแก้ไข Profile, การ Debug ด้วย aa-logprof, จนถึงการเขียน Custom Profile สำหรับ Application จริง

ตรวจสอบ AppArmor Status

# ตรวจสอบว่า AppArmor ทำงานอยู่หรือไม่
sudo apparmor_status
# หรือใช้ aa-status (เหมือนกัน)
sudo aa-status

# ตัวอย่าง Output:
# apparmor module is loaded.
# 35 profiles are loaded.
# 35 profiles are in enforce mode.
#    /usr/bin/evince
#    /usr/sbin/mysqld
#    ...
# 0 profiles are in complain mode.
# 0 processes have profiles defined.

# ตรวจสอบว่า Module โหลดอยู่ใน Kernel
cat /sys/module/apparmor/parameters/enabled
# Y = เปิดใช้งาน

# ดู AppArmor ใน systemd
systemctl status apparmor
# ติดตั้ง AppArmor บน Ubuntu/Debian (มักติดตั้งมาแล้ว)
sudo apt install apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra

# เปิดใช้ AppArmor ถ้าถูก Disable
sudo systemctl enable --now apparmor

# ตรวจสอบว่า Kernel Boot Parameter มี apparmor=1
grep -i apparmor /proc/cmdline
# BOOT_IMAGE=... apparmor=1 security=apparmor

AppArmor Modes — Enforce, Complain, Disabled

AppArmor มี 3 Mode สำหรับแต่ละ Profile โดยสามารถตั้งค่าแยกกันได้ในแต่ละ Application

# ดู Mode ของทุก Profile
sudo aa-status | grep -A 100 "profiles are in enforce mode"

# เปลี่ยน Profile เป็น Complain Mode (Log เท่านั้น ไม่ Block)
sudo aa-complain /usr/sbin/nginx
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx   # ระบุ Profile File ได้เช่นกัน

# เปลี่ยน Profile เป็น Enforce Mode (Block จริง)
sudo aa-enforce /usr/sbin/nginx

# Disable Profile (ปิดทั้งหมด ไม่ apply)
sudo aa-disable /usr/sbin/nginx

# ดู Mode ของ Profile เฉพาะตัว
sudo aa-status | grep nginx
# โหลด Profile ใหม่หลังแก้ไข
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx

# โหลด Profile ใหม่ทั้งหมด
sudo systemctl reload apparmor

# โหลด Profile จากไฟล์เฉพาะ
sudo apparmor_parser -a /etc/apparmor.d/usr.sbin.nginx

# ลบ Profile ออกจาก Kernel (ไม่ลบไฟล์)
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.nginx

โครงสร้าง AppArmor Profile

Profile อยู่ที่ /etc/apparmor.d/ ชื่อไฟล์ใช้ Path ของ Binary โดยแทน / ด้วย . เช่น /usr/sbin/nginxusr.sbin.nginx

# โครงสร้าง Profile พื้นฐาน
# /etc/apparmor.d/usr.sbin.nginx

#include <tunables/global>

/usr/sbin/nginx {
  #include <abstractions/base>
  #include <abstractions/nameservice>

  # Capability
  capability net_bind_service,
  capability setuid,
  capability setgid,

  # File Access
  /usr/sbin/nginx mr,                    # mr = map + read (binary)
  /etc/nginx/** r,                       # r = read (config files)
  /var/log/nginx/ rw,
  /var/log/nginx/** rw,                  # rw = read + write (log)
  /var/www/html/ r,
  /var/www/html/** r,
  /run/nginx.pid rw,
  /tmp/ rw,
  /tmp/** rw,

  # Network
  network inet tcp,
  network inet6 tcp,

  # Deny explicit
  deny /etc/shadow r,
  deny /root/** rw,
}
# Permission Modes ใน AppArmor Profile
# r  = read
# w  = write
# a  = append
# x  = execute
# m  = mmap (executable mapping)
# k  = lock
# l  = link
# ix = inherit execute (ใช้ Profile ของ Parent)
# Px = execute ด้วย Profile ของ Binary นั้น (must exist)
# Ux = execute โดยไม่มี Profile (unconfined)
# cx = execute ด้วย Child Profile ที่ระบุใน Profile

# ตัวอย่างการใช้ Execute Modes
/bin/bash ix,          # bash inherit ใช้ Profile ของ nginx
/usr/bin/perl Px,      # perl ใช้ Profile ของ perl เอง
/usr/bin/python3 Ux,   # python ทำงานแบบ unconfined (ไม่ปลอดภัย ใช้เฉพาะจำเป็น)

สร้าง Profile อัตโนมัติด้วย aa-genprof

aa-genprof เป็นเครื่องมือที่ช่วยสร้าง Profile แบบ Interactive โดยสังเกต Behavior ของ Program แล้วสร้าง Rule ให้อัตโนมัติ

# สร้าง Profile สำหรับ Program ใหม่ (ต้องรันเป็น root)
sudo aa-genprof /path/to/program

# aa-genprof จะ:
# 1. สร้าง Profile ว่างและตั้งเป็น Complain Mode
# 2. รอให้คุณรัน Program ในอีก Terminal
# 3. กด S เพื่อ Scan Log และแสดง Rule ที่แนะนำ
# 4. กด F เพื่อ Finish และ Save Profile

# ขั้นตอนปฏิบัติ:
# Terminal 1: sudo aa-genprof /usr/local/bin/myapp
# Terminal 2: /usr/local/bin/myapp --test-run

# ในระหว่าง aa-genprof จะถามว่าแต่ละ Access ให้ทำอะไร:
# (A)llow   = อนุญาตและเพิ่มใน Profile
# (D)eny    = ปฏิเสธและเพิ่ม deny rule
# (I)gnore  = ข้ามไป
# (G)lob    = ใช้ Glob Pattern แทน Path เฉพาะ
# (N)ew     = พิมพ์ Path ใหม่เอง

Debug และปรับแต่ง Profile ด้วย aa-logprof

เมื่อ Application ทำงานในโหมด Complain, AppArmor จะบันทึก Denied Access ไปยัง Log โดยไม่ Block ใช้ aa-logprof อ่าน Log และเพิ่ม Rule เข้า Profile

# ดู AppArmor Log ใน syslog/audit
sudo grep "apparmor" /var/log/syslog | tail -20
sudo grep "apparmor" /var/log/kern.log | tail -20

# ตัวอย่าง Log Entry:
# kernel: audit: type=1400 audit(1234567890.123:456): apparmor="ALLOWED"
#   operation="open" profile="/usr/sbin/nginx" name="/etc/ssl/private/server.key"
#   pid=1234 comm="nginx" requested_mask="r" denied_mask="r" fsuid=33 ouid=0

# หรือดูผ่าน journalctl
sudo journalctl -k | grep apparmor | tail -20

# รัน aa-logprof เพื่ออ่าน Log และแนะนำ Rule
sudo aa-logprof

# aa-logprof อ่าน /var/log/syslog (และ /var/log/audit/audit.log ถ้ามี)
# แสดง Access ที่ถูก Log และถามว่าจะ Allow หรือ Deny

# ระบุ Log File เฉพาะ
sudo aa-logprof -f /var/log/syslog
# ตัวอย่าง Output ของ aa-logprof และการตอบ:
# Profile: /usr/sbin/nginx
# Path:    /etc/ssl/private/server.key
# Old Mode: (none)
# New Mode: r
#
# Severit: 2
#
# (A)llow / (D)eny / (I)gnore / (G)lob / ...
# => กด A เพื่ออนุญาต

# หลังจากตอบทุก Entry แล้ว:
# Save Changes? [Y/n]
# => กด Y เพื่อบันทึก Profile

# โหลด Profile ที่แก้ไขแล้ว
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx

ตัวอย่าง Profile จริง — Nginx Web Server

# /etc/apparmor.d/usr.sbin.nginx
# Profile สำหรับ Nginx บน Ubuntu

#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,

  # Binary
  /usr/sbin/nginx mr,

  # Config
  /etc/nginx/ r,
  /etc/nginx/** r,

  # Logs
  /var/log/nginx/ rw,
  /var/log/nginx/** rw,

  # Web Root
  /var/www/ r,
  /var/www/** r,

  # SSL Certificates
  /etc/ssl/certs/ r,
  /etc/ssl/certs/** r,
  /etc/letsencrypt/live/ r,
  /etc/letsencrypt/live/** r,
  /etc/letsencrypt/archive/ r,
  /etc/letsencrypt/archive/** r,

  # PID, Cache, Temp
  /run/nginx.pid rw,
  /var/cache/nginx/ rw,
  /var/cache/nginx/** rw,
  /tmp/ rw,
  /tmp/nginx* rw,

  # Unix Sockets (สำหรับ PHP-FPM)
  /run/php/ r,
  /run/php/*.sock rw,

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

  # Proc (สำหรับ Worker Process)
  /proc/*/cpuinfo r,
  /proc/*/meminfo r,

  # Deny
  deny /etc/shadow r,
  deny /root/ rw,
  deny /home/**/.ssh/ rw,
}

ตัวอย่าง Profile — MySQL/MariaDB

# /etc/apparmor.d/usr.sbin.mysqld
# Profile สำหรับ MySQL/MariaDB

#include <tunables/global>

/usr/sbin/mysqld {
  #include <abstractions/base>
  #include <abstractions/nameservice>
  #include <abstractions/user-tmp>

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

  # Binary
  /usr/sbin/mysqld mr,

  # Config
  /etc/mysql/ r,
  /etc/mysql/** r,

  # Data Directory
  /var/lib/mysql/ rw,
  /var/lib/mysql/** rw,

  # Logs
  /var/log/mysql/ rw,
  /var/log/mysql/** rw,

  # PID, Socket
  /run/mysqld/ rw,
  /run/mysqld/mysqld.pid rw,
  /run/mysqld/mysqld.sock rw,
  /run/mysqld/mysqld.sock.lock rw,

  # Tmp
  /tmp/ rw,
  /tmp/** rw,

  # Deny access outside DB directory
  deny /etc/passwd w,
  deny /etc/shadow rw,
}

AppArmor Abstractions

Abstractions คือ Profile Fragment ที่ใช้ซ้ำได้ เก็บอยู่ที่ /etc/apparmor.d/abstractions/ ช่วยลดความซ้ำซ้อนในการเขียน Profile

# Abstractions ที่ใช้บ่อย
#include <abstractions/base>         # libc, locale, tz, dns
#include <abstractions/nameservice>  # DNS, /etc/hosts, NSS
#include <abstractions/openssl>      # libssl, /etc/ssl/openssl.cnf
#include <abstractions/user-tmp>     # /tmp, /var/tmp
#include <abstractions/python>       # Python libs และ modules
#include <abstractions/perl>         # Perl libs
#include <abstractions/php>          # PHP libs (Ubuntu)
#include <abstractions/apache2-common>  # Apache + modules

# ดู Abstractions ที่มีทั้งหมด
ls /etc/apparmor.d/abstractions/

# ดูเนื้อหา Abstraction
cat /etc/apparmor.d/abstractions/base

AppArmor สำหรับ Docker Container

# Docker ใช้ AppArmor Profile ชื่อ docker-default โดย Default
# ตรวจสอบ Profile ที่ Container ใช้
docker inspect --format='{{.AppArmorProfile}}' <container-id>

# รัน Container ด้วย Profile เฉพาะ
docker run --security-opt apparmor=docker-default nginx

# รัน Container โดยไม่มี AppArmor (ไม่แนะนำ)
docker run --security-opt apparmor=unconfined nginx

# ดู Profile docker-default
cat /etc/apparmor.d/docker

# สร้าง Custom Profile สำหรับ Docker Container
# /etc/apparmor.d/docker-nginx-strict
#include <tunables/global>
profile docker-nginx-strict flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>
  network inet tcp,
  network inet6 tcp,
  /etc/nginx/** r,
  /var/www/html/** r,
  /var/log/nginx/** rw,
  deny /proc/sysrq-trigger rw,
  deny /proc/sys/kernel/** rw,
}

# โหลด Profile
sudo apparmor_parser -r /etc/apparmor.d/docker-nginx-strict

# รัน Container ด้วย Custom Profile
docker run --security-opt apparmor=docker-nginx-strict nginx

Tunable Variables

# Tunables คือ Variable ที่ใช้แทนค่าซ้ำในหลาย Profile
# เก็บที่ /etc/apparmor.d/tunables/

# ดู Tunable ที่สำคัญ
cat /etc/apparmor.d/tunables/global
# @{PROC}=/proc/
# @{sys}=/sys/
# @{HOME}=@{HOMEDIRS}/*/ /root/
# @{HOMEDIRS}=/home/

# ใช้งานใน Profile
/usr/sbin/nginx {
  @{PROC}/*/cpuinfo r,   # แทน /proc/*/cpuinfo r
  /var/www/** r,
}

# สร้าง Tunable เอง
# /etc/apparmor.d/tunables/webroot
# @{WEBROOT}=/var/www/html/ /srv/www/

Script ตรวจสอบ AppArmor

#!/bin/bash
# /usr/local/bin/apparmor-check.sh
# ตรวจสอบสถานะ AppArmor และ Profile

echo "=== AppArmor Status ==="
if ! command -v apparmor_status &>/dev/null; then
    echo "AppArmor tools not installed"
    exit 1
fi

if ! sudo apparmor_status --enabled 2>/dev/null; then
    echo "❌ AppArmor is NOT enabled"
    exit 1
fi

echo "✅ AppArmor is enabled"
echo ""

# สรุป Profile
ENFORCE=$(sudo aa-status 2>/dev/null | grep "profiles are in enforce" | awk '{print $1}')
COMPLAIN=$(sudo aa-status 2>/dev/null | grep "profiles are in complain" | awk '{print $1}')

echo "Profiles in Enforce mode: ${ENFORCE:-0}"
echo "Profiles in Complain mode: ${COMPLAIN:-0}"
echo ""

# แสดง Profile ที่ Complain (ควรตรวจสอบและเปลี่ยนเป็น Enforce)
if [ "${COMPLAIN:-0}" -gt 0 ]; then
    echo "⚠️  Profiles in Complain mode (consider enforcing):"
    sudo aa-status 2>/dev/null | grep -A "${COMPLAIN}" "profiles are in complain" | tail -"${COMPLAIN}"
fi
echo ""

# ตรวจหา Recent Denials
echo "=== Recent AppArmor Denials (last 24h) ==="
sudo journalctl -k --since "24 hours ago" 2>/dev/null \
    | grep 'apparmor="DENIED"' \
    | awk '{print $1, $2, $NF}' \
    | sort | uniq -c | sort -rn | head -20

echo ""
echo "=== Done ==="

สรุป

AppArmor ใช้ Path-based Profile ควบคุม Access ของ Process แต่ละตัว โดยมี 3 Mode คือ Enforce (บังคับ), Complain (Log อย่างเดียว) และ Disabled วิธีสร้าง Profile ที่มีประสิทธิภาพที่สุดคือเริ่มจาก Complain Mode แล้วใช้ aa-logprof ในการอ่าน Log และเพิ่ม Rule ทีละส่วนจนครบ หรือใช้ aa-genprof สร้าง Profile แบบ Interactive การใช้ Abstractions ช่วยลดการเขียน Rule ซ้ำสำหรับ Library และ System Resource ที่ใช้บ่อย AppArmor เหมาะสำหรับระบบ Ubuntu/Debian และสามารถใช้ร่วมกับ Docker ได้โดยกำหนด --security-opt apparmor=<profile>

แนะนำบริการ DE

การตั้งค่า AppArmor Profile สำหรับ Application บน Server ต้องการสิทธิ์ Root และการเข้าถึง Kernel Module โดยตรง Cloud VPS ของ DE ให้ Root Access เต็มรูปแบบ สามารถติดตั้ง apparmor-utils, สร้าง Custom Profile, และตั้งค่า MAC Security สำหรับทุก Service บน Server ได้อย่างอิสระ

สำหรับผู้ที่ต้องการโฮสต์เว็บไซต์โดยมีระบบความปลอดภัยพร้อมใช้ Cloud Hosting ของ DE มี Security Infrastructure ที่ตั้งค่าไว้แล้ว เหมาะสำหรับผู้ที่ต้องการความปลอดภัยระดับ Enterprise โดยไม่ต้องจัดการ AppArmor เอง