SELinux คืออะไร? ใช้งานอย่างไรไม่ให้ปวดหัว – ทำความเข้าใจ Security ระดับ OS

SELinux คืออะไร? ใช้งานอย่างไรไม่ให้ปวดหัว – ทำความเข้าใจ Security ระดับ OS

คุณเพิ่งติดตั้ง Apache หรือ Nginx เสร็จบน Rocky Linux แล้วทดสอบ curl localhost ก็ผ่าน แต่พอลองเปลี่ยน port หรือย้าย DocumentRoot ไปไว้โฟลเดอร์ใหม่ – เว็บกลับตายสนิท ไม่มี error บน log ที่ชัดเจน ทั้งที่ firewall ก็เปิดแล้ว permission ก็ตั้งแล้ว

ถ้าเจอแบบนี้ ตัวการมักจะเป็น SELinux แทบทุกครั้ง

คำแนะนำแรกที่หลายคนได้ยินจาก Google คือ “ปิด SELinux ซะ” — แต่นั่นคือการแก้ปัญหาแบบผิดวิธี เหมือนถอดสายเบรกออกเพราะรถเบรกบ่อย บทความนี้จะพาคุณทำความเข้าใจ SELinux ตั้งแต่รากจนถึงระดับที่ใช้งานจริงได้โดยไม่ต้องปวดหัวอีกต่อไป


SELinux คืออะไร? — อธิบายให้เข้าใจใน 2 นาที

SELinux (Security-Enhanced Linux) คือระบบ Mandatory Access Control (MAC) ที่ทำงานอยู่ใน Linux Kernel ระดับลึก พัฒนาโดย NSA (สหรัฐอเมริกา) และเปิดเป็น Open Source ตั้งแต่ปี 2000

DAC vs MAC — ต่างกันอย่างไร?

ระบบสิทธิ์แบบเดิมที่เราคุ้นเคยบน Linux เรียกว่า DAC (Discretionary Access Control) ซึ่งควบคุมด้วย chmod / chown / chgrp — ใครเป็นเจ้าของไฟล์ก็จัดการสิทธิ์เองได้

SELinux เพิ่มเลเยอร์ MAC (Mandatory Access Control) ขึ้นมาอีกชั้น หมายความว่า แม้ root ก็ถูกจำกัดได้ หาก Policy ไม่อนุญาต

┌─────────────────────────────────────────────┐
│              Application Request             │
└──────────────────┬──────────────────────────┘
                   │
          ┌────────▼────────┐
          │   DAC Check     │  ← chmod/chown แบบเดิม
          │  (rwxrwxrwx)    │
          └────────┬────────┘
                   │ ผ่าน
          ┌────────▼────────┐
          │  SELinux Check  │  ← MAC ชั้นที่สอง (ใหม่)
          │  (Policy-based) │
          └────────┬────────┘
                   │ ผ่าน
          ┌────────▼────────┐
          │   Kernel / FS   │
          └─────────────────┘

📌 หมายเหตุ: ต้องผ่าน ทั้งสอง ชั้นจึงจะเข้าถึงทรัพยากรได้ ถ้า DAC ผ่านแต่ SELinux ไม่ผ่าน ก็ยังถูกบล็อกอยู่ดี

ทำไม SELinux ถึงสำคัญ

สมมติว่า Apache ของคุณโดน exploit แล้ว attacker สามารถรันโค้ดใน context ของ Apache ได้ หากไม่มี SELinux — attacker อาจอ่านไฟล์ /etc/shadow หรือแก้ไข crontab ได้เลย แต่ถ้ามี SELinux — Apache จะถูกจำกัดให้ทำได้แค่สิ่งที่ Policy อนุญาตสำหรับ web server เท่านั้น ความเสียหายจะถูก contain ไว้ในกรอบแคบ ๆ


Prerequisites

ก่อนเริ่ม ตรวจสอบว่าคุณมีสิ่งเหล่านี้พร้อม

  • เครื่อง Rocky Linux 8 หรือ 9 (ใช้งานได้จริงกับ AlmaLinux / RHEL ด้วย)
  • สิทธิ์ sudo หรือ root
  • พื้นฐาน Linux command line เบื้องต้น
  • Apache หรือ Nginx ติดตั้งไว้แล้ว (ไม่บังคับ แต่ช่วยให้ตามตัวอย่างได้ง่ายขึ้น)

ส่วนที่ 1 — ทำความรู้จัก Mode และ State ของ SELinux

SELinux มี 3 Mode

# ดู mode ปัจจุบัน
sestatus
```
```
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 ความหมาย เหมาะกับ
enforcing บังคับ policy จริง — บล็อกและ log Production
permissive ไม่บล็อก แต่ log ทุก violation ทดสอบ/debug
disabled ปิด SELinux ทั้งหมด ❌ ไม่แนะนำ

เปลี่ยน Mode ชั่วคราว (ไม่ต้อง reboot)

# เปลี่ยนเป็น permissive ชั่วคราว (สำหรับ debug)
sudo setenforce 0
# เปลี่ยนกลับเป็น enforcing
sudo setenforce 1
# ดู mode ปัจจุบันแบบสั้น
getenforce

⚠️ ข้อควรระวัง: setenforce เปลี่ยนเฉพาะ session ปัจจุบัน reboot แล้วจะกลับไปใช้ค่าจาก config file

ตั้งค่า Mode แบบถาวร

# แก้ไขไฟล์ config
sudo nano /etc/selinux/config
# /etc/selinux/config
# ค่าที่แนะนำสำหรับ production server
SELINUX=enforcing
# Policy ที่ใช้ (targeted = ส่วนใหญ่เพียงพอ)
SELINUXTYPE=targeted
```

> 💡 **Tip:** อย่าเปลี่ยนจาก `enforcing` เป็น `disabled` โดยตรง ให้ผ่าน `permissive` ก่อนแล้ว reboot — มิฉะนั้นอาจมีปัญหา filesystem label ที่ต้อง relabel ทั้งระบบ

---

## ส่วนที่ 2 — SELinux Context คืออะไร?

นี่คือหัวใจของ SELinux ที่หลายคนข้ามไป

ทุก file, process, port, และ network socket บน SELinux จะมี **Security Context** หรือ **label** กำกับไว้ รูปแบบคือ:
```
user:role:type:level

ตัวอย่างเช่น

# ดู context ของไฟล์
ls -Z /var/www/html/index.html
```
```
unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html
# ดู context ของ process ที่กำลังรัน
ps auxZ | grep httpd
```
```
system_u:system_r:httpd_t:s0      root  1234 ... /usr/sbin/httpd

ส่วนที่สำคัญที่สุดคือ type (ตรงกลาง) — SELinux ใช้ type ในการตัดสินว่า process ไหนเข้าถึง resource ไหนได้บ้าง

Type ที่พบบ่อยสำหรับ Web Server

Type ใช้กับ
httpd_t Apache/Nginx process
httpd_sys_content_t ไฟล์เว็บที่อ่านได้ (read-only)
httpd_sys_rw_content_t ไฟล์เว็บที่เขียนได้
httpd_log_t ไฟล์ log ของ web server
httpd_config_t ไฟล์ config ของ web server

ส่วนที่ 3 — Lab มือ: แก้ปัญหา SELinux จริง

 Lab 1: Apache ไม่สามารถอ่านไฟล์จาก DocumentRoot ใหม่

สถานการณ์: ย้าย DocumentRoot จาก /var/www/html ไปที่ /web/mysite

# สร้างโฟลเดอร์และไฟล์ทดสอบ
sudo mkdir -p /web/mysite
echo "<h1>Hello SELinux</h1>" | sudo tee /web/mysite/index.html

# ตั้งค่า Apache ให้ชี้ไปที่โฟลเดอร์ใหม่
sudo nano /etc/httpd/conf/httpd.conf

แก้ไข DocumentRoot

# /etc/httpd/conf/httpd.conf
DocumentRoot "/web/mysite"
<Directory "/web/mysite">
    AllowOverride None
    Require all granted
</Directory>
# Restart Apache
sudo systemctl restart httpd

# ทดสอบ — จะได้ 403 Forbidden
curl -I localhost

วิเคราะห์ปัญหา

# ดู audit log เพื่อหา SELinux denial
sudo ausearch -m avc -ts recent
```
```
type=AVC msg=audit(...): avc:  denied  { getattr } for
  pid=1234 comm="httpd" path="/web/mysite/index.html"
  scontext=system_u:system_r:httpd_t:s0
  tcontext=unconfined_u:object_r:default_t:s0
  tclass=file permissive=0

📌 อ่าน log: httpd_t (Apache) ถูก deny ไม่ให้เข้าถึง default_t (label เริ่มต้น) — เพราะ Apache ต้องการ httpd_sys_content_t

แก้ปัญหา — ด้วยการตั้ง Context ที่ถูกต้อง

# ดู context ปัจจุบัน
ls -dZ /web/mysite

# เปลี่ยน type ให้ถูกต้อง
sudo semanage fcontext -a -t httpd_sys_content_t "/web/mysite(/.*)?"
# Apply context ที่ตั้งไว้
sudo restorecon -Rv /web/mysite
# ตรวจสอบว่า context เปลี่ยนแล้ว
ls -Z /web/mysite/index.html
# unconfined_u:object_r:httpd_sys_content_t:s0  /web/mysite/index.html
# ทดสอบใหม่ — ควรได้ 200 OK
curl -I localhost

ผลลัพธ์ที่คาดหวัง: HTTP/1.1 200 OK โดยที่ SELinux ยังคง enforcing อยู่


 Lab 2: เปิด Port ที่ไม่ใช่ค่าเริ่มต้นให้ Apache

สถานการณ์: ต้องการให้ Apache ฟังที่ port 8888 แทน 80

# เพิ่ม port ใน Apache config
sudo nano /etc/httpd/conf/httpd.conf
# เพิ่มบรรทัด: Listen 8888
sudo systemctl restart httpd
# อาจ fail เพราะ SELinux ไม่อนุญาต port 8888

ตรวจสอบ port ที่ Apache อนุญาต

# ดู port ทั้งหมดที่ httpd_t อนุญาต
sudo semanage port -l | grep http
```
```
http_port_t    tcp    80, 81, 443, 488, 8008, 8009, 8443, 9000

เพิ่ม port ใหม่

# เพิ่ม port 8888 เข้า policy
sudo semanage port -a -t http_port_t -p tcp 8888
# ยืนยัน
sudo semanage port -l | grep http_port_t

# Restart Apache
sudo systemctl restart httpd

✅ ตอนนี้ Apache สามารถ listen port 8888 ได้แล้ว โดย SELinux ยัง enforcing อยู่


ส่วนที่ 4 — Boolean: ปุ่มเปิด/ปิด Feature ของ SELinux

SELinux มีสิ่งที่เรียกว่า Boolean — ตัวแปร on/off ที่ช่วยให้เปิด feature บางอย่างได้โดยไม่ต้องเขียน policy ใหม่เอง

# ดู boolean ทั้งหมดของ httpd
sudo getsebool -a | grep httpd
```
```
httpd_can_connect_ftp --> off
httpd_can_network_connect --> off      ← สำคัญมาก!
httpd_can_network_connect_db --> off
httpd_can_sendmail --> off
httpd_enable_cgi --> on
httpd_use_nfs --> off
...

Boolean ที่ใช้บ่อย

# อนุญาตให้ Apache เชื่อมต่อ database (เช่น PHP → MySQL)
sudo setsebool -P httpd_can_network_connect_db on

# อนุญาตให้ Apache เชื่อมต่อ network ทั่วไป (reverse proxy, API call)
sudo setsebool -P httpd_can_network_connect on

# อนุญาตให้ Apache ส่งอีเมล
sudo setsebool -P httpd_can_sendmail on

💡 Tip: flag -P หมายถึง persistent — บันทึกถาวรแม้ reboot หากไม่ใส่ -P จะเปลี่ยนแค่ session ปัจจุบัน


ส่วนที่ 5 — audit2allow: เครื่องมือช่วยวิเคราะห์ Denial

เมื่อ SELinux บล็อกอะไรบางอย่างและยังไม่รู้ว่าต้องเปิดอะไร เครื่องมือ audit2allow จะช่วยวิเคราะห์และแนะนำ policy ให้

# ติดตั้ง (ถ้ายังไม่มี)
sudo dnf install -y policycoreutils-python-utils

# วิเคราะห์ denial ล่าสุด และแนะนำ policy
sudo ausearch -m avc -ts recent | audit2allow -a

# ดูเฉพาะ denial ของ httpd
sudo ausearch -m avc -ts recent -c httpd | audit2allow
```
```
#============= httpd_t ==============
allow httpd_t user_home_dir_t:file { read getattr open };
```
⚠️ **ข้อควรระวัง:** `audit2allow` แนะนำ policy ที่อนุญาตพอดีกับที่ถูก deny แต่ไม่ได้แปลว่าถูกต้องเสมอ ควรทำความเข้าใจก่อน apply จริง อย่า copy-paste โดยไม่คิด

---

## ส่วนที่ 6 — เวิร์กโฟลว์แก้ปัญหา SELinux แบบมืออาชีพ

เมื่อเจอปัญหา ให้ทำตามขั้นตอนนี้เป็นลำดับ:
```
1. ตรวจสอบว่า SELinux เป็นต้นเหตุจริงหรือเปล่า
   → setenforce 0  แล้วทดสอบซ้ำ
   → ถ้าหายปัญหา = SELinux เป็นต้นเหตุ

2. อ่าน audit log เพื่อเข้าใจปัญหา
   → sudo ausearch -m avc -ts recent
   → sudo journalctl -t setroubleshoot (ถ้า setroubleshoot ติดตั้งไว้)
3. เลือกวิธีแก้ที่เหมาะสม:
   ├── ปัญหา file context → semanage fcontext + restorecon
   ├── ปัญหา port → semanage port -a
   ├── ปัญหา feature → setsebool -P
   └── ปัญหาซับซ้อน → audit2allow (ระวัง!)
4. setenforce 1 กลับ แล้วทดสอบซ้ำ

ทดสอบและ Verify

ตรวจสอบว่า SELinux ทำงานถูกต้อง

# 1. ดูสถานะรวม
sestatus

# 2. ดู context ของ file สำคัญ
ls -Z /var/www/html/
ls -Z /etc/httpd/conf/

# 3. ดู boolean ที่เปิดไว้
sudo getsebool -a | grep -i "on$" | grep httpd

# 4. ดู port ที่ผูกไว้กับ http
sudo semanage port -l | grep http

# 5. ดูว่ามี denial ค้างอยู่ไหม
sudo ausearch -m avc -ts today | wc -l
# ถ้าได้ 0 = สะอาดดี

คำสั่งสรุป (Cheat Sheet)

# ─── ดูสถานะ ───
sestatus                          # สถานะรวม
getenforce                        # mode ปัจจุบัน
# ─── เปลี่ยน Mode ───
sudo setenforce 0                 # permissive (ชั่วคราว)
sudo setenforce 1                 # enforcing (ชั่วคราว)
# ─── จัดการ Context ───
ls -Z <path>                      # ดู context ของไฟล์/โฟลเดอร์
ps auxZ | grep <process>          # ดู context ของ process
sudo semanage fcontext -a -t <type> "<path>(/.*)?"  # ตั้ง context
sudo restorecon -Rv <path>        # Apply context
# ─── จัดการ Port ───
sudo semanage port -l | grep <keyword>   # ดู port
sudo semanage port -a -t <type> -p tcp <port>  # เพิ่ม port
# ─── จัดการ Boolean ───
sudo getsebool -a | grep <keyword>       # ดู boolean
sudo setsebool -P <boolean> on|off       # ตั้งค่า boolean
# ─── Debug ───
sudo ausearch -m avc -ts recent          # audit log ล่าสุด
sudo ausearch -m avc -ts recent | audit2allow  # วิเคราะห์ + แนะนำ

สรุป

SELinux ไม่ได้น่ากลัวอย่างที่คิด มันเป็นเหมือน “ยาม” ที่เข้มงวดซึ่งทำงานให้เราตลอดเวลา แทนที่จะปิดยามทิ้ง เราควรเรียนรู้วิธีคุยกับเขา ด้วยเครื่องมือที่ได้เรียนในบทความนี้ คุณสามารถ

  • วินิจฉัย ปัญหาจาก audit log ได้เอง
  • แก้ปัญหา ได้ 3 วิธีหลัก (fcontext, port, boolean) โดยไม่ต้องปิด SELinux
  • รักษา security ของเซิร์ฟเวอร์ไว้ได้ในระดับ production


Write by SysAdmin Knowledge
https://www.sysadmin.in.th
March 25, 2026