SELinux (Security-Enhanced Linux) เป็น Mandatory Access Control (MAC) System ที่สร้างโดย NSA และรวมเข้ากับ Linux Kernel ต่างจาก Traditional Unix Permission (DAC) ที่ใช้ Owner/Group/Other, SELinux กำหนด Policy ว่า Process ไหนสามารถเข้าถึง File, Port, หรือ Resource ใดได้ แม้แต่ root ก็ถูกจำกัดโดย SELinux Policy ทำให้ความเสียหายจาก Service ที่ถูก Exploit ลดลงอย่างมาก
บทความนี้อธิบาย SELinux Mode, Context, Boolean, Policy Type, การแก้ปัญหา AVC Denial, และการตั้งค่า Custom Policy สำหรับ Application ที่ทำงานบน RHEL/Rocky/AlmaLinux ซึ่งเป็น Distribution ที่ใช้ SELinux เป็น Default
SELinux Mode และการตรวจสอบสถานะ
# ตรวจสอบ SELinux Status
getenforce
# Enforcing → SELinux เปิดและบังคับใช้ Policy
# Permissive → SELinux เปิดแต่ไม่บังคับ แค่ Log
# Disabled → SELinux ปิดอยู่
sestatus
# ตัวอย่าง Output:
# SELinux status: enabled
# SELinuxfs mount: /sys/fs/selinux
# SELinux mount point: /sys/fs/selinux
# Loaded policy name: targeted
# Current mode: enforcing
# Mode from config file: enforcing
# Policy MLS status: enabled
# Policy deny_unknown status: allowed
# Memory protection checking: actual (secure)
# Max kernel policy version: 33
# เปลี่ยน Mode ชั่วคราว (ไม่ต้อง Reboot)
setenforce 0 # เปลี่ยนเป็น Permissive
setenforce 1 # เปลี่ยนเป็น Enforcing
# เปลี่ยน Mode ถาวร
sudo nano /etc/selinux/config
# แก้ไข:
SELINUX=enforcing # enforcing | permissive | disabled
SELINUXTYPE=targeted # targeted | mls
# ถ้าเปลี่ยนจาก disabled เป็น enforcing ต้อง Relabel ก่อน
# สร้างไฟล์นี้เพื่อให้ Relabel เมื่อ Reboot
sudo touch /.autorelabel
sudo reboot
SELinux Context
ทุก File, Process, และ Port ใน SELinux มี Context ในรูปแบบ user:role:type:level โดย Type เป็นส่วนที่สำคัญที่สุดในการกำหนดสิทธิ์การเข้าถึง
# ดู SELinux Context ของ File
ls -laZ /var/www/html/
# -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html
# รูปแบบ: user:role:type:level
ls -laZ /etc/shadow
# ----------. root root system_u:object_r:shadow_t:s0 /etc/shadow
# ดู Context ของ Process
ps -eZ | grep httpd
# system_u:system_r:httpd_t:s0 ... httpd
# ดู Context ของ Port
semanage port -l | grep http
# http_port_t tcp 80, 443, 488, 8008, 8009, 8443
# ดู Context ปัจจุบันของ Process ที่รันอยู่
id -Z
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
# เปลี่ยน Context ของ File
# chcon — เปลี่ยนชั่วคราว (หาย เมื่อ Relabel)
sudo chcon -t httpd_sys_content_t /var/www/html/myfile.html
# chcon แบบ Recursive
sudo chcon -R -t httpd_sys_content_t /var/www/mysite/
# semanage fcontext — เปลี่ยนถาวร (บันทึกใน Policy)
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/mysite(/.*)?"
# Apply Context ที่ตั้งค่าไว้
sudo restorecon -Rv /var/www/mysite/
# Restore Context กลับเป็น Default ตาม Policy
sudo restorecon -v /var/www/html/myfile.html
# ดู Context ที่ถูกกำหนดไว้ใน Policy
sudo semanage fcontext -l | grep /var/www
SELinux Boolean
Boolean คือ Switch ที่ปรับเปลี่ยน Policy ได้โดยไม่ต้องเขียน Custom Policy ใหม่ เหมาะสำหรับเปิด/ปิดฟีเจอร์ที่ Policy ไม่อนุญาตเป็นค่าเริ่มต้น
# ดู Boolean ทั้งหมด
getsebool -a
# ค้นหา Boolean ที่เกี่ยวข้องกับ httpd
getsebool -a | grep httpd
# Boolean ที่ใช้บ่อยสำหรับ Web Server:
# httpd_can_network_connect → อนุญาต httpd เชื่อมต่อ Network (proxy, API call)
# httpd_can_network_connect_db → อนุญาต httpd เชื่อมต่อ Database
# httpd_execmem → อนุญาต httpd ใช้ Executable Memory
# httpd_read_user_content → อนุญาต httpd อ่านไฟล์ใน Home Directory
# httpd_enable_homedirs → อนุญาต httpd เข้าถึง Home Directories
# httpd_use_nfs → อนุญาต httpd ใช้ NFS
# เปิด Boolean ชั่วคราว (หายเมื่อ Reboot)
sudo setsebool httpd_can_network_connect on
# เปิด Boolean ถาวร (-P flag)
sudo setsebool -P httpd_can_network_connect on
# ตรวจสอบ
getsebool httpd_can_network_connect
# Boolean ที่ใช้บ่อยสำหรับ Service อื่น ๆ
# FTP:
getsebool -a | grep ftp
# ftpd_anon_write → อนุญาต FTP Anonymous Write
# ftpd_home_dir → อนุญาต FTP เข้าถึง Home Directories
# allow_ftpd_full_access → Full Access สำหรับ FTP
# SSH/SFTP:
getsebool -a | grep ssh
# ssh_keysign → อนุญาต ssh-keysign
# Samba:
getsebool -a | grep samba
# samba_export_all_rw → อนุญาต Samba อ่านเขียนทุก Directory
# NFS:
getsebool -a | grep nfs
# use_nfs_home_dirs → อนุญาต NFS Home Directories
วิเคราะห์และแก้ไข AVC Denial
เมื่อ SELinux Block การกระทำใด ๆ จะบันทึกใน Audit Log เป็น AVC (Access Vector Cache) Denial การอ่านและวิเคราะห์ Log เหล่านี้เป็นทักษะสำคัญในการ Troubleshoot SELinux
# ดู AVC Denial ล่าสุด
sudo ausearch -m AVC -ts recent
sudo ausearch -m AVC -ts today
# ดูผ่าน journalctl
sudo journalctl -t setroubleshoot
# ดูด้วย sealert (ต้องติดตั้ง setroubleshoot-server)
sudo dnf install setroubleshoot-server -y
sudo sealert -a /var/log/audit/audit.log
# ตัวอย่าง AVC Denial:
# type=AVC msg=audit(1713200000.000:100): avc: denied { name_connect }
# for pid=12345 comm="httpd" dest=3306
# scontext=system_u:system_r:httpd_t:s0
# tcontext=system_u:object_r:mysqld_port_t:s0
# tclass=tcp_socket permissive=0
#
# อ่านว่า: httpd (httpd_t) ถูก Block ไม่ให้เชื่อมต่อ Port 3306 (mysqld_port_t)
# audit2why — อธิบายสาเหตุของ AVC Denial
sudo ausearch -m AVC -ts recent | audit2why
# ตัวอย่าง Output:
# Was caused by:
# The boolean httpd_can_network_connect_db was set incorrectly.
# Description:
# Allow httpd to connect to database.
# Allow access by executing:
# # setsebool -P httpd_can_network_connect_db 1
# audit2allow — สร้าง Policy Module จาก AVC Denial
sudo ausearch -m AVC -ts recent | audit2allow -M myapp-policy
sudo semodule -i myapp-policy.pp
# ตรวจสอบ Policy Module ที่โหลดอยู่
sudo semodule -l
sudo semodule -l | grep myapp
SELinux Port Management
# ดู Port ที่ SELinux รู้จัก
sudo semanage port -l
sudo semanage port -l | grep ssh
sudo semanage port -l | grep http
# เพิ่ม Port ให้ Service
# เช่น SSH ใช้ Port 2222
sudo semanage port -a -t ssh_port_t -p tcp 2222
# เพิ่ม Port ให้ httpd ใช้ Port 8080
sudo semanage port -a -t http_port_t -p tcp 8080
# แก้ไข Port Type
sudo semanage port -m -t http_port_t -p tcp 8443
# ลบ Port
sudo semanage port -d -t http_port_t -p tcp 8080
# ตรวจสอบว่า Port ถูกเพิ่มแล้ว
sudo semanage port -l | grep 2222
ตัวอย่างการแก้ปัญหา SELinux จริง
# ปัญหาที่พบบ่อย #1: Nginx/Apache ไม่สามารถ Connect Database ได้
# AVC: httpd denied name_connect to mysqld_port_t (3306)
sudo setsebool -P httpd_can_network_connect_db on
# หรือ
sudo setsebool -P httpd_can_network_connect on # กว้างกว่า
# ปัญหาที่พบบ่อย #2: Web Server อ่านไฟล์จาก Directory ที่ไม่ใช่ /var/www ไม่ได้
# AVC: httpd denied read on /data/myapp/
sudo semanage fcontext -a -t httpd_sys_content_t "/data/myapp(/.*)?"
sudo restorecon -Rv /data/myapp/
# ปัญหาที่พบบ่อย #3: Web App เขียนไฟล์ไม่ได้ (เช่น Upload)
# ต้องใช้ Type ที่อนุญาต Write: httpd_sys_rw_content_t
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/uploads(/.*)?"
sudo restorecon -Rv /var/www/html/uploads/
# ปัญหาที่พบบ่อย #4: App ใช้ Port ที่ SELinux ไม่รู้จัก
sudo semanage port -a -t http_port_t -p tcp 3000
# ปัญหาที่พบบ่อย #5: Cron Job หรือ Script ถูก Block
# ตรวจสอบ Context ของ Script และแก้ไข
ls -laZ /etc/cron.d/myscript
sudo restorecon -v /etc/cron.d/myscript
สร้าง Custom SELinux Policy Module
# วิธีที่ง่ายที่สุด: ใช้ audit2allow จาก AVC Log
# 1. เปลี่ยน SELinux เป็น Permissive ชั่วคราว
sudo setenforce 0
# 2. รัน Application เพื่อให้ Log AVC ที่ควรอนุญาต
# (ทดสอบ Functionality ทั้งหมดของ App)
# 3. สร้าง Policy Module จาก Log
sudo ausearch -m AVC -ts recent | audit2allow -M myapp
# สร้าง myapp.te และ myapp.pp
# 4. ดู Policy ที่จะสร้าง
cat myapp.te
# 5. ติดตั้ง Policy Module
sudo semodule -i myapp.pp
# 6. กลับเป็น Enforcing
sudo setenforce 1
# 7. ทดสอบอีกครั้ง
sudo ausearch -m AVC -ts recent | wc -l # ควรได้ 0 หรือน้อยมาก
# เขียน Policy Module แบบ Manual (ขั้นสูง)
# /tmp/myapp.te
cat > /tmp/myapp.te <<'EOF'
module myapp 1.0;
require {
type httpd_t;
type var_t;
class file { read open getattr };
}
# อนุญาตให้ httpd_t อ่านไฟล์ใน var_t
allow httpd_t var_t:file { read open getattr };
EOF
# Compile Policy
checkmodule -M -m -o /tmp/myapp.mod /tmp/myapp.te
semodule_package -o /tmp/myapp.pp -m /tmp/myapp.mod
# ติดตั้ง
sudo semodule -i /tmp/myapp.pp
# ถอนการติดตั้ง Policy Module
sudo semodule -r myapp
SELinux บน Docker และ Container
# Docker ใน RHEL/Rocky ทำงานร่วมกับ SELinux ได้
# ต้องใช้ :z หรือ :Z สำหรับ Volume Mount
# :z = ให้ Container หลายตัวใช้ Volume ร่วมกัน (Shared Label)
docker run -v /data/myapp:/app:z nginx
# :Z = Private Label สำหรับ Container เดียว
docker run -v /data/myapp:/app:Z nginx
# ตรวจสอบ Context หลัง Mount
ls -laZ /data/myapp/
# ควรเห็น container_file_t หรือ svirt_sandbox_file_t
# ถ้าไม่ใช้ Label อาจต้องใช้ --security-opt
docker run --security-opt label=disable nginx # ปิด SELinux สำหรับ Container นี้
# Podman (Rootless) ทำงานกับ SELinux ได้ดีกว่า
podman run -v /data/myapp:/app:Z nginx
Script ตรวจสอบ SELinux Health
#!/bin/bash
# /usr/local/bin/selinux-check.sh
echo "=== SELinux Health Check ==="
echo ""
# Mode
MODE=$(getenforce)
echo "SELinux Mode: ${MODE}"
if [ "$MODE" = "Enforcing" ]; then
echo "✅ SELinux is Enforcing"
elif [ "$MODE" = "Permissive" ]; then
echo "⚠️ SELinux is Permissive (not enforcing)"
else
echo "❌ SELinux is Disabled"
fi
# Policy
POLICY=$(sestatus | grep "Loaded policy name" | awk '{print $NF}')
echo "Policy: ${POLICY}"
# Recent AVC
echo ""
echo "=== Recent AVC Denials (last 24h) ==="
AVC_COUNT=$(ausearch -m AVC -ts today 2>/dev/null | grep -c "type=AVC" || echo 0)
echo "AVC Denials today: ${AVC_COUNT}"
if [ "$AVC_COUNT" -gt 0 ]; then
echo "Top denied processes:"
ausearch -m AVC -ts today 2>/dev/null | grep "comm=" | \
sed 's/.*comm="\([^"]*\)".*/\1/' | sort | uniq -c | sort -rn | head -5
fi
# Permissive Domains
echo ""
echo "=== Permissive Domains ==="
semanage permissive -l 2>/dev/null
echo "---"
echo ""
echo "Check /var/log/audit/audit.log for full AVC details"
สรุป
SELinux เป็น Security Layer เพิ่มเติมจาก Unix Permission ที่จำกัดสิทธิ์ของ Process แม้จะเป็น root ควรคง SELinux Mode เป็น Enforcing เสมอ ไม่ใช่ปิดเมื่อมีปัญหา การแก้ปัญหาที่ถูกต้องคือใช้ audit2why เพื่อวิเคราะห์ AVC Denial และแก้ไขด้วย Boolean, Context (semanage fcontext + restorecon), หรือ Port Management (semanage port) แทนที่จะ Disable SELinux สร้าง Custom Policy Module ด้วย audit2allow เมื่อไม่มี Boolean ที่ตรงกับความต้องการ
แนะนำบริการ DE
SELinux เป็น Default บน RHEL/Rocky Linux ซึ่งต้องการ Root Access ในการจัดการ Policy และ Context Cloud VPS ของ DE รองรับทั้ง Ubuntu และ Rocky Linux พร้อม Root Access เต็มรูปแบบ สามารถตั้งค่า SELinux Policy, เพิ่ม Custom Module และ Tune Boolean ได้ตามต้องการของ Application
สำหรับผู้ที่ต้องการโฮสต์เว็บไซต์โดยไม่ต้องดูแล Security Policy เอง Cloud Hosting ของ DE มี Security Infrastructure ที่พร้อมใช้งาน โดยไม่ต้องจัดการ MAC Policy ระดับ Kernel

