ลดความยุ่งยากในการใช้คลาวด์ พูดคุยกับเจ้าหน้าที่

Home ใช้งาน SELinux บน AlmaLinux และ Rocky Linux โดยไม่ต้องปิด

ใช้งาน SELinux บน AlmaLinux และ Rocky Linux โดยไม่ต้องปิด

SELinux (Security-Enhanced Linux) เปิดใช้งานมาตั้งแต่ติดตั้งบน AlmaLinux และ Rocky Linux เป็นชั้นป้องกันที่ทำงานเพิ่มจากสิทธิ์ไฟล์แบบปกติ แม้โปรแกรมจะรันเป็นผู้ใช้ที่อ่านไฟล์ได้ตาม chmod แต่ถ้านโยบายของ SELinux ไม่อนุญาต การเข้าถึงนั้นก็จะถูกปฏิเสธ ประโยชน์ที่ชัดเจนคือ ถ้าเว็บไซต์ถูกเจาะ ผู้โจมตีที่ได้สิทธิ์ของ Nginx หรือ Apache จะยังถูกจำกัดให้อยู่ในขอบเขตที่เว็บเซิร์ฟเวอร์ควรทำได้เท่านั้น

คำแนะนำในอินเทอร์เน็ตจำนวนมากบอกให้ "ปิด SELinux" ทันทีที่เจอ Permission denied บทความนี้แสดงวิธีที่ถูกต้องกว่า คือหาว่า SELinux ปฏิเสธอะไร แล้วแก้เฉพาะจุดด้วยเครื่องมือมาตรฐานสามตัว ได้แก่ File Context, Boolean และ Port Label ซึ่งครอบคลุมปัญหาที่พบบนเว็บเซิร์ฟเวอร์เกือบทั้งหมด

สิ่งที่ต้องเตรียม

  • AlmaLinux หรือ Rocky Linux 8 หรือ 9 และผู้ใช้ที่มีสิทธิ์ sudo
  • ติดตั้งเครื่องมือจัดการ SELinux:
sudo dnf install -y policycoreutils-python-utils setroubleshoot-server

policycoreutils-python-utils ให้คำสั่ง semanage และ audit2why ส่วน setroubleshoot-server ให้คำสั่ง sealert ที่อธิบายสาเหตุเป็นภาษาคนอ่านเข้าใจ

ขั้นตอนที่ 1: ตรวจสถานะ SELinux

getenforce
sestatus
โหมดความหมาย
Enforcingบังคับใช้นโยบาย ปฏิเสธและบันทึก Log (ค่าที่ควรใช้)
Permissiveไม่ปฏิเสธ แต่บันทึก Log ว่าจะปฏิเสธอะไร ใช้ตรวจหาปัญหาชั่วคราว
Disabledปิดทั้งหมด ไม่มีการบันทึก

ขั้นตอนที่ 2: เข้าใจ Label

SELinux ตัดสินจาก Label (Context) ที่ติดอยู่กับทุกไฟล์และทุกโปรเซส ดูได้ด้วยตัวเลือก -Z:

ls -Z /var/www/html
ps -eZ | grep -E "nginx|httpd"

ตัวอย่างผลลัพธ์:

unconfined_u:object_r:httpd_sys_content_t:s0 index.html
system_u:system_r:httpd_t:s0    1320 ?  00:00:00 nginx

ส่วนที่สำคัญคือส่วนที่สาม เรียกว่า Type โปรเซส Nginx และ Apache รันใน Type httpd_t และนโยบายอนุญาตให้ httpd_t อ่านไฟล์ที่เป็น httpd_sys_content_t ได้ ถ้าไฟล์เว็บมี Type อื่น เช่น user_home_t หรือ default_t เว็บเซิร์ฟเวอร์จะอ่านไม่ได้ แม้สิทธิ์ chmod จะเปิดเต็มที่ก็ตาม

Type ที่ใช้บ่อยกับเว็บ:

Typeใช้กับ
httpd_sys_content_tไฟล์เว็บที่อ่านอย่างเดียว
httpd_sys_rw_content_tโฟลเดอร์ที่เว็บต้องเขียน เช่น wp-content/uploads, storage ของ Laravel
httpd_log_tไฟล์ Log ของเว็บเซิร์ฟเวอร์

ขั้นตอนที่ 3: หาว่า SELinux ปฏิเสธอะไร

เมื่อเว็บขึ้น 403 หรือ 502 และ Log ของแอปแจ้ง Permission denied ทั้งที่สิทธิ์ไฟล์ถูกต้อง ให้ดูบันทึกการปฏิเสธ (AVC Denial):

sudo ausearch -m AVC,USER_AVC -ts recent

ตัวอย่างบรรทัดที่พบ:

type=AVC msg=audit(...): avc:  denied  { read } for  pid=1320 comm="nginx"
  name="index.html" scontext=system_u:system_r:httpd_t:s0
  tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

อ่านได้ว่า โปรเซส nginx (Type httpd_t) ถูกปฏิเสธการ read ไฟล์ที่มี Type user_home_t ให้ส่งผลนี้เข้า audit2why หรือใช้ sealert เพื่อดูคำแนะนำ:

sudo ausearch -m AVC -ts recent | audit2why
sudo sealert -a /var/log/audit/audit.log

sealert มักบอกคำสั่งที่ควรใช้แก้มาให้ด้วย แต่ให้อ่านและเข้าใจก่อนทำตาม

ขั้นตอนที่ 4: แก้ปัญหาไฟล์ด้วย File Context

สาเหตุที่พบบ่อยที่สุดคือ ใช้ mv ย้ายไฟล์ จาก Home Directory ไปไว้ที่โฟลเดอร์เว็บ mv จะพา Label เดิม (user_home_t) ติดไปด้วย ส่วน cp จะให้ Label ตามโฟลเดอร์ปลายทาง วิธีแก้คือรีเซ็ต Label ให้ตรงกับนโยบาย:

sudo restorecon -Rv /var/www/example.com

ผลลัพธ์จะแสดงบรรทัด Relabeled ... from ... to ... สำหรับไฟล์ที่ถูกแก้

ถ้าเว็บอยู่นอกโฟลเดอร์มาตรฐาน เช่น /srv/www หรือ /data/sites ต้องเพิ่มกฎถาวรก่อน แล้วค่อย restorecon:

sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"
sudo restorecon -Rv /srv/www

โฟลเดอร์ที่เว็บต้องเขียนได้ ให้กำหนดเฉพาะโฟลเดอร์นั้น ไม่ใช่ทั้งเว็บ:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/srv/www/example.com/wp-content/uploads(/.*)?"
sudo restorecon -Rv /srv/www/example.com/wp-content/uploads

ดูกฎที่คุณเพิ่มไว้ทั้งหมดด้วย sudo semanage fcontext -l -C อย่าใช้ chcon เป็นวิธีถาวร เพราะค่าจาก chcon จะหายเมื่อมีการ Relabel ทั้งระบบ

ขั้นตอนที่ 5: เปิดความสามารถด้วย Boolean

Boolean คือสวิตช์เปิดปิดพฤติกรรมที่นโยบายเตรียมไว้ให้ ดูรายการของเว็บเซิร์ฟเวอร์ได้ด้วย:

getsebool -a | grep httpd

ที่ใช้บ่อย:

Booleanเปิดเมื่อ
httpd_can_network_connectเว็บต้องเชื่อมต่อออกไปยังเครือข่าย เช่น Nginx เป็น Reverse Proxy ให้แอป Node.js ที่พอร์ต 3000 หรือ PHP เรียก API ภายนอก
httpd_can_network_connect_dbเว็บต้องต่อฐานข้อมูลผ่าน TCP (เช่น ฐานข้อมูลอยู่อีกเครื่อง)
httpd_can_sendmailPHP ต้องส่งอีเมลผ่าน sendmail ในเครื่อง
httpd_enable_homedirsต้องให้บริการไฟล์จาก Home Directory ของผู้ใช้

เปิดแบบถาวรด้วยตัวเลือก -P (ไม่มี -P ค่าจะหายเมื่อรีบูต):

sudo setsebool -P httpd_can_network_connect on

อาการที่บ่งบอกว่าต้องเปิด Boolean ตัวแรก คือ Nginx แจ้ง 502 Bad Gateway และ Error Log มีข้อความ connect() to 127.0.0.1:3000 failed (13: Permission denied) ซึ่งพบบ่อยในการตั้ง Node.js กับ Nginx Reverse Proxy

ขั้นตอนที่ 6: อนุญาตพอร์ตที่ไม่ใช่ค่ามาตรฐาน

SELinux กำหนดว่าแต่ละ Service Bind พอร์ตใดได้บ้าง ถ้าคุณเปลี่ยน SSH ไปพอร์ต 2222 หรือให้ Nginx ฟังที่พอร์ตที่ไม่อยู่ในรายการ Service จะเริ่มไม่ได้ ตรวจรายการก่อน:

sudo semanage port -l | grep -E "^(ssh_port_t|http_port_t)"

เพิ่มพอร์ตที่ต้องการ:

sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo semanage port -a -t http_port_t -p tcp 8090

ถ้าคำสั่งแจ้งว่าพอร์ตนั้นมี Type อื่นกำหนดไว้แล้ว ให้ใช้ -m แทน -a เพื่อแก้ไข การเปลี่ยนพอร์ต SSH ต้องทำขั้นตอนนี้ก่อนรีสตาร์ต SSH เสมอ และต้องเปิด SSH Session เดิมค้างไว้ แล้วทดสอบเข้าด้วยหน้าต่างใหม่ก่อนปิด ดูขั้นตอนเต็มที่ วิธีเปลี่ยน SSH Port บน Linux Server

ตรวจสอบผลลัพธ์

  • getenforce ยังเป็น Enforcing
  • ทดสอบเว็บหรือ Service ที่มีปัญหาอีกครั้ง แล้วรัน sudo ausearch -m AVC -ts recent ต้องไม่มีบรรทัดปฏิเสธใหม่ (อาจแสดง <no matches>)
  • ls -Z บนโฟลเดอร์เว็บแสดง Type ตามที่ตั้งไว้

ใช้ Permissive เพื่อวินิจฉัยเท่านั้น

ถ้าต้องการยืนยันว่าปัญหามาจาก SELinux จริงหรือไม่ ให้สลับเป็น Permissive ชั่วคราว ทดสอบ แล้วสลับกลับทันที:

sudo setenforce 0
# ทดสอบเว็บ
sudo setenforce 1

วิธีที่แคบกว่าคือทำให้เฉพาะ Domain ของเว็บเซิร์ฟเวอร์เป็น Permissive ขณะที่ส่วนอื่นยัง Enforcing:

sudo semanage permissive -a httpd_t
# หลังตรวจเสร็จ
sudo semanage permissive -d httpd_t

ปัญหาที่พบบ่อย

ใช้ audit2allow สร้าง Module แล้วปัญหาหาย แต่ไม่รู้ว่าอนุญาตอะไรไป

audit2allow -M สร้างกฎจาก Log ทุกบรรทัดที่ได้รับ ซึ่งอาจอนุญาตกว้างเกินไป ใช้ได้เมื่อไม่มี Boolean หรือ File Context ที่ตรงกับกรณีของคุณแล้วเท่านั้น และต้องอ่านไฟล์ .te ที่สร้างขึ้นก่อนติดตั้ง

เคยปิด SELinux แล้วเปิดกลับ ระบบบูตแล้วมีปัญหาเต็มไปหมด

ขณะปิด ไฟล์ที่สร้างใหม่จะไม่มี Label ต้อง Relabel ทั้งระบบก่อนเปิดใช้งาน โดยตั้ง SELINUX=permissive ใน /etc/selinux/config แล้วรัน sudo touch /.autorelabel และรีบูต (ใช้เวลานานตามจำนวนไฟล์) เมื่อบูตเสร็จและไม่มี Denial สำคัญ จึงเปลี่ยนเป็น enforcing บน AlmaLinux/Rocky 9 การตั้ง SELINUX=disabled ในไฟล์นี้ไม่ได้ปิดทั้งหมดอีกต่อไป ต้องใช้ Kernel Parameter selinux=0 ซึ่งเป็นอีกเหตุผลที่ไม่ควรปิด

ไม่เห็น AVC ใน Log ทั้งที่สงสัยว่าเป็น SELinux

บางกฎถูกตั้งให้ปฏิเสธแบบไม่บันทึก (dontaudit) ปิดชั่วคราวด้วย sudo semodule -DB ทดสอบซ้ำ แล้วเปิดกลับด้วย sudo semodule -B

หากตรวจ Log แล้วยังหาทางแก้ที่ไม่ต้องปิด SELinux ไม่ได้ ติดต่อทีมซัพพอร์ต THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

ผู้ให้บริการคลาวด์ไทย
เพื่อธุรกิจของคนไทย

"มุ่งมั่น" และ "มั่นคง"
พร้อมรับมือทุกการเติบโต
Trust Cloud
คลาว์ที่ปลอดภัย
คือรากฐานที่มั่นคง
cloud security