วิธีตั้งค่า firewalld บน Rocky Linux และ AlmaLinux
เรียนรู้วิธีเปิดพอร์ต SSH และเว็บเซิร์ฟเวอร์บน Rocky หรือ AlmaLinux ด้วย firewalld พร้อมวิธีใช้งานแฟล็ก --permanent เพื่อให้กฎคงอยู่หลังรีบูตและทำความเข้าใจระบบโซน
firewalld คืออะไร และเหตุใด Rocky และ AlmaLinux จึงติดตั้งมาให้
firewalld คือตัวจัดการไฟร์วอลล์ที่ติดตั้งมาเป็นค่าเริ่มต้นบน Rocky Linux, AlmaLinux และระบบปฏิบัติการอื่นที่สร้างใหม่จาก Red Hat Enterprise Linux (RHEL) ทั้งสองดิสทริบิวชันได้รับค่าเริ่มต้นนี้มาโดยไม่ได้เป็นผู้เลือกเอง ซึ่งจะเข้าใจได้ง่ายขึ้นเมื่อคุณทราบถึง ที่มาของการที่ Rocky และ AlmaLinux สร้างระบบขึ้นใหม่จากงานของ Red Hat หลังจากที่ CentOS เปลี่ยนทิศทาง ตัวมันเองไม่ได้ทำหน้าที่ตรวจสอบแพ็กเก็ตโดยตรง แต่จะเก็บรักษาการตั้งค่าไว้และแปลงการตั้งค่าเหล่านั้นให้เป็นกฎของ nftables โดยมีคำสั่ง firewall-cmd ที่ใช้แก้ไขค่าได้ในขณะที่เซิร์ฟเวอร์ยังคงทำงานอยู่ เนื้อหาในคู่มือนี้ไม่มีส่วนใดที่แตกต่างกันระหว่างทั้งสองดิสทริบิวชัน เนื่องจาก สิ่งที่แยก Rocky ออกจาก AlmaLinux จริงๆ คือคำมั่นสัญญาเรื่องความเข้ากันได้และช่วงของ CPU ที่ยังรองรับ ไม่ใช่ตัวไฟร์วอลล์
หากคุณทราบแล้วว่า ufw ทำงานอย่างไรบน Ubuntu VPS คุณก็จะเข้าใจหน้าที่ของมัน firewalld เพิ่มแนวคิดสองประการที่ ufw ไม่มี ประการแรกคือโซน (zones): ซึ่งคือนโยบายที่มีชื่อเรียกสำหรับจัดกลุ่มแพ็กเก็ต ประการที่สองคือการแยกกฎที่ใช้งานจริง (live rules) ออกจากกฎที่บันทึกไว้ (saved rules) ซึ่งควบคุมด้วยแฟล็ก --permanent และเป็นสาเหตุที่ทำให้เกิดความสับสนมากที่สุดในการใช้งานเครื่องมือนี้
ทุกคำสั่งด้านล่างนี้เป็นคำสั่งที่คุณต้องรันบนเซิร์ฟเวอร์ของคุณเอง โปรดทดสอบการเปลี่ยนแปลงทุกครั้งจากเครื่องที่สอง เนื่องจากกฎที่ดูเหมือนถูกต้องเมื่อตรวจสอบจากภายในเครื่องอาจยังคงผิดพลาดเมื่อเชื่อมต่อจากอินเทอร์เน็ต
เปิดใช้งาน SSH ก่อนดำเนินการอื่นใด
การติดตั้ง Rocky และ AlmaLinux ส่วนใหญ่จะมี firewalld ติดตั้งและทำงานอยู่แล้ว และการตั้งค่าเริ่มต้นก็อนุญาตให้ใช้งาน SSH ได้ แต่ image แบบ minimal ในระบบคลาวด์บางตัวอาจไม่มีมาให้ ดังนั้นควรตรวจสอบให้แน่ใจแทนการคาดเดา
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --statefirewall-cmd --state จะแสดงผล running หาก service หยุดทำงาน การเรียกใช้ firewall-cmd อื่นๆ ทั้งหมดจะตอบกลับเป็น FirewallD is not running และจบการทำงานด้วยสถานะ non-zero นี่คือสิ่งแรกที่ควรตรวจสอบเมื่อคำสั่งดูเหมือนไม่ทำงานเลย
ตอนนี้ให้ตรวจสอบสิ่งที่อนุญาตอยู่ในปัจจุบัน
sudo firewall-cmd --list-allผลลัพธ์จริงจะมีบรรทัดอื่นๆ เพิ่มเติมอีกเล็กน้อย แต่บรรทัดที่มีความสำคัญมีดังนี้:
public (active)
target: default
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:
rich rules:ssh ในบรรทัด services: คือเหตุผลที่ session ของคุณยังคงทำงานได้ หากบรรทัดนี้หายไป ให้เพิ่มเข้าไปก่อนที่คุณจะแก้ไขสิ่งอื่นใด เพราะการเริ่ม firewall โดยไม่มีกฎ SSH จะทำให้ session ของคุณหลุดและไม่สามารถกลับเข้ามาได้อีก
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reloadtarget: default หมายความว่า packet ที่ไม่ตรงกับกฎใดๆ จะถูกปฏิเสธด้วยการตอบกลับแบบ ICMP (internet control message protocol) host-prohibited ดังนั้น client ที่พยายามเชื่อมต่อพอร์ตที่ปิดอยู่จะได้รับข้อความ No route to host ทันที การตั้งค่า target เป็น DROP จะทำให้เซิร์ฟเวอร์เงียบไปแทน และเครื่องมือสแกนจะรอจนกว่าจะหมดเวลา (timeout)
sudo firewall-cmd --permanent --zone=public --set-target=DROP
sudo firewall-cmd --reloadโปรดทราบถึงผลกระทบก่อนดำเนินการดังกล่าว: DROP จะทำให้เซิร์ฟเวอร์ไม่ตอบสนองต่อ ping ด้วยเช่นกัน ซึ่งจะส่งผลให้ระบบตรวจสอบสถานะ (monitoring) ของคุณเงียบหายไปด้วย
ทำไมกฎของฉันถึงหายไป? การใช้ flag --permanent
firewalld เก็บการตั้งค่าไว้สองชุดพร้อมกัน การตั้งค่าแบบ runtime คือสิ่งที่ kernel บังคับใช้ในขณะนี้ ส่วนการตั้งค่าแบบ permanent คือสิ่งที่อยู่ใน /etc/firewalld/zones/public.xml และจะกลับมาใช้งานอีกครั้งหลังจาก reload หรือ reboot
คำสั่งที่ไม่มี --permanent จะเปลี่ยนเฉพาะค่า runtime เท่านั้น กฎจะมีผลทันทีแต่จะหายไปเมื่อมีการ reload หรือบูตเครื่องใหม่ ส่วนคำสั่งที่มี --permanent จะเขียนลงไฟล์แต่ไม่มีผลกับสิ่งที่กำลังทำงานอยู่ ดังนั้นพอร์ตจะยังคงปิดอยู่จนกว่าคุณจะสั่ง reload พฤติกรรมทั้งสองอย่างไม่ใช่บั๊ก แต่สร้างความประหลาดใจให้ผู้ใช้เพราะคำสั่งจะแสดงผล success เหมือนกันทั้งสองกรณี
ให้เขียนคำสั่งคู่กันเสมอ
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reloadคุณสามารถตรวจสอบการตั้งค่าทั้งสองชุดได้ ซึ่งเป็นวิธีที่เร็วที่สุดในการดูว่าคุณทำพลาดในส่วนใด
sudo firewall-cmd --list-services
sudo firewall-cmd --permanent --list-servicesคำสั่งแรกจะแสดงชุดกฎที่ใช้งานจริง ส่วนคำสั่งที่สองจะแสดงชุดกฎที่บันทึกไว้ หากชุดกฎที่ใช้งานจริงมี service ที่ชุดที่บันทึกไว้ไม่มี กฎนั้นจะหายไปเมื่อมีการ reload ครั้งถัดไป หากชุดที่บันทึกไว้มี service ที่ชุดที่ใช้งานจริงไม่มี แสดงว่าคุณลืมสั่ง reload ส่วน sudo firewall-cmd --runtime-to-permanent จะคัดลอกทุกอย่างจาก runtime ไปยังไฟล์ที่บันทึกไว้ ซึ่งมีประโยชน์หลังจากที่คุณทดลองตั้งค่ามาสักพัก
--reload จะคงสถานะการติดตามการเชื่อมต่อไว้ ทำให้ session SSH ของคุณไม่หลุด ส่วน --complete-reload จะโหลด kernel module ใหม่ด้วยและทำให้สถานะดังกล่าวหายไป ซึ่งมักจะตัดการเชื่อมต่อทุกอย่างรวมถึง session ของคุณด้วย ให้ใช้การ reload แบบปกติแทน
มีกลไกป้องกันความผิดพลาดที่ติดตั้งมาให้ในตัว กฎแบบ runtime สามารถหมดอายุได้ด้วยตัวเอง
sudo firewall-cmd --add-service=http --timeout=5mกฎดังกล่าวจะลบตัวเองออกหลังจากผ่านไป 5 นาที ไม่สามารถใช้ร่วมกับ --permanent ได้ และนั่นคือจุดประสงค์ของมัน คือใช้สำหรับการทดสอบการเปลี่ยนแปลงที่คุณยังไม่แน่ใจ กลไกป้องกันแบบเดิมนั้นดีกว่า ให้เปิด session SSH สำรองไว้อีกหนึ่งช่องขณะแก้ไขกฎ และอย่าปิด session นั้นจนกว่าจะล็อกอินใหม่แล้วพิสูจน์ได้ว่ากฎใหม่ทำงานได้ถูกต้อง
โซน และเหตุผลที่โซนเริ่มต้นมีความสำคัญที่สุดบน VPS
โซนคือชุดสิทธิ์ที่มีการกำหนดระดับความน่าเชื่อถือไว้ firewalld จะจัดประเภทแพ็กเก็ตขาเข้าทุกรายการลงในโซนใดโซนหนึ่งเพียงโซนเดียวเท่านั้น โดยจะตรวจสอบที่อยู่ต้นทางของแพ็กเก็ตเทียบกับรายการ sources: ของแต่ละโซนก่อน หากไม่มีรายการใดตรงกัน ระบบจะใช้โซนที่ผูกไว้กับอินเทอร์เฟซขาเข้า หากอินเทอร์เฟซไม่ได้ผูกไว้กับโซนใดเลย แพ็กเก็ตจะถูกส่งไปยังโซนเริ่มต้น
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zonesบน VPS ที่มีอินเทอร์เฟซเครือข่ายเพียงรายการเดียว คำตอบแรกมักจะเป็น public เสมอ และนั่นคือโซนเดียวที่คุณจำเป็นต้องใช้งาน คำสั่ง firewall-cmd ที่ไม่มีอาร์กิวเมนต์ --zone= จะทำงานกับโซนเริ่มต้น ซึ่งเป็นเหตุผลว่าทำไมคำสั่งสั้นๆ ทุกคำสั่งในคู่มือนี้จึงทำงานได้โดยไม่ต้องระบุชื่อโซน
ข้อผิดพลาดที่ทำให้เสียเวลาไปทั้งบ่ายมีดังนี้ หากอินเทอร์เฟซถูกผูกไว้กับโซนอื่น กฎที่คุณสร้างจะไปอยู่ใน public ในขณะที่ทราฟฟิกถูกจัดการโดยโซนอื่น ทำให้สิ่งที่คุณเพิ่มเข้าไปไม่มีผลใดๆ และไม่มีการแจ้งเตือนใดๆ ทั้งสิ้น คำสั่ง --get-active-zones จะแสดงการผูกอินเทอร์เฟซไว้:
public
interfaces: eth0หากอินเทอร์เฟซปรากฏภายใต้ชื่อโซนอื่น ให้เขียนกฎของคุณในโซนนั้นโดยใช้ --zone= หรือย้ายอินเทอร์เฟซไปที่โซนที่ถูกต้อง
sudo firewall-cmd --permanent --zone=public --change-interface=eth0
sudo firewall-cmd --reloadNetworkManager ทำหน้าที่จัดการอินเทอร์เฟซบน Rocky และ AlmaLinux และจะกำหนดโซนใหม่เมื่อการเชื่อมต่อเริ่มทำงาน คุณควรตั้งค่าในส่วนนี้ด้วยเพื่อให้การรีบูตไม่ทำให้การตั้งค่าของคุณหายไป ให้ใช้ชื่อการเชื่อมต่อจากคำสั่งแรก เนื่องจากชื่อการเชื่อมต่อมักจะไม่เหมือนกับชื่ออุปกรณ์
sudo nmcli connection show
sudo nmcli connection modify "System eth0" connection.zone publicการจับคู่ด้วยที่อยู่ต้นทาง (source matching) มีลำดับความสำคัญสูงกว่าการจับคู่ด้วยอินเทอร์เฟซ ซึ่งเป็นวิธีที่ทำให้ที่อยู่หนึ่งได้รับนโยบายที่แตกต่างออกไป โซน trusted ที่มีมาให้ในตัวจะยอมรับทุกอย่าง
sudo firewall-cmd --permanent --zone=trusted --add-source=203.0.113.10/32
sudo firewall-cmd --reloadโปรดใช้ความระมัดระวังกับโซนดังกล่าว เพราะจะเป็นการเปิดพอร์ตทุกพอร์ตบนเซิร์ฟเวอร์ให้กับที่อยู่นั้น รวมถึงฐานข้อมูลที่คุณคิดว่าเป็นส่วนตัวด้วย ให้ใช้ rich rule แทนเมื่อคุณต้องการเปิดเพียงพอร์ตเดียว ไม่ใช่เปิดให้ทั้งโฮสต์
firewalld service คืออะไร
service คือชุดของพอร์ตที่ถูกจัดกลุ่มไว้ภายใต้ชื่อหนึ่ง โดยเก็บอยู่ในรูปแบบไฟล์ XML ตัวอย่างเช่น --add-service=https จะเปิดพอร์ต 443/tcp เนื่องจาก /usr/lib/firewalld/services/https.xml เป็นตัวกำหนดว่า https หมายถึงอะไร
sudo firewall-cmd --get-services
sudo firewall-cmd --info-service=https--info-service จะแสดงรายการพอร์ตที่ซ่อนอยู่ภายใต้ชื่อนั้น:
https
ports: 443/tcpควรใช้ชื่อ service เมื่อมีชื่อนั้นกำหนดไว้แล้ว เพราะจะทำให้อ่านคำสั่งใน --list-all ได้เข้าใจง่ายขึ้นเมื่อกลับมาดูในอีก 6 เดือนข้างหน้า นอกจากนี้แพ็กเกจอย่าง Cockpit ยังติดตั้งไฟล์ service ของตนเองมาให้ด้วย สำหรับสิ่งที่ไม่มีการกำหนดนิยามไว้ ให้ใช้ --add-port แทน
ข้อควรระวัง: service ที่ชื่อ ssh หมายถึงพอร์ต 22/tcp เท่านั้น หากคุณย้าย SSH ไปยังพอร์ตอื่นในระหว่างขั้นตอน การเพิ่มความปลอดภัยในการเข้าถึง SSH บนเซิร์ฟเวอร์ คำสั่ง --add-service=ssh จะไม่เปิดพอร์ตที่คุณใช้งานจริง
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reloadในการติดตั้ง RHEL ใหม่ จะมีการล็อกชั้นที่สองไว้เสมอ นั่นคือ SELinux (Security-Enhanced Linux) ซึ่งจะทำหน้าที่กำกับป้ายกำกับ (label) ให้กับหมายเลขพอร์ต และ sshd จะไม่ได้รับอนุญาตให้ bind เข้ากับพอร์ตที่อยู่นอกเหนือจากป้ายกำกับที่กำหนดไว้ ส่งผลให้ service ปฏิเสธที่จะเริ่มทำงานและใน log จะแสดงข้อความ error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. คุณต้องทำการติดป้ายกำกับพอร์ตก่อน
sudo dnf install -y policycoreutils-python-utils
sudo semanage port -a -t ssh_port_t -p tcp 2222ฉันจะตรวจสอบได้อย่างไรว่าพอร์ตใดเปิดอยู่บ้างในขณะนี้
sudo firewall-cmd --list-all
sudo firewall-cmd --list-rich-rules
sudo nft list table inet firewalld | head -n 40คำสั่งสองรายการแรกเป็นการรายงานสิ่งที่ firewalld รับทราบ ส่วนคำสั่งที่สามเป็นการอ่านกฎที่ kernel ใช้งานจริงในตารางที่ firewalld ดูแลอยู่ ซึ่งข้อมูลทั้งสามควรจะตรงกัน
ข้อมูลเหล่านี้ยังไม่ใช่ข้อพิสูจน์ที่ชัดเจน ให้ทำการทดสอบจากเครื่องอื่นแทน:
nc -zv 203.0.113.20 443ห้ามรันการทดสอบบนตัวเซิร์ฟเวอร์เอง เนื่องจาก firewalld จะยอมรับทุกการเชื่อมต่อที่เข้ามาทาง loopback interface ดังนั้น curl http://localhost:8080 จะสำเร็จเสมอไม่ว่ากฎที่คุณตั้งไว้จะเป็นอย่างไร การทดสอบนั้นบอกเพียงว่าบริการยังทำงานอยู่ แต่ไม่ได้ให้ข้อมูลใดๆ เกี่ยวกับสถานะของ firewall เลย
การอนุญาตพอร์ตสำหรับเว็บ
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-servicesคำสั่งสุดท้ายควรแสดงรายการ http https เพิ่มเข้ามาพร้อมกับรายการเดิมที่มีอยู่ หากเว็บไซต์ยังคงไม่ตอบสนอง ปัญหาอาจไม่ได้อยู่ที่ไฟร์วอลล์ กฎของไฟร์วอลล์เพียงแค่อนุญาตให้แพ็กเก็ตผ่านเข้ามาได้ แต่ยังจำเป็นต้องมีโพรเซสที่คอยรับฟัง (listening) อยู่ด้วย
sudo ss -tlnpซ็อกเก็ตที่แสดงเป็น 0.0.0.0:443 หรือ *:443 จะยอมรับการเชื่อมต่อจากทุกแอดเดรส ส่วนซ็อกเก็ตที่แสดงเป็น 127.0.0.1:443 จะตอบสนองเฉพาะบน loopback เท่านั้น ซึ่งไม่มีกฎไฟร์วอลล์ใดที่จะทำให้เข้าถึงจากภายนอกได้ พอร์ตและซ็อกเก็ตที่เปิดรับฟังบน Linux ได้อธิบายความแตกต่างนี้ไว้ในรายละเอียดเพิ่มเติม
ฉันจะปิดพอร์ตอีกครั้งได้อย่างไร?
sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --reloadกฎ --permanent ยังคงใช้ได้ในกรณีนี้ และส่งผลกระทบมากกว่าเมื่อดำเนินการในทิศทางนี้ หากคุณลบ service ออกจาก runtime เท่านั้น พอร์ตจะดูเหมือนถูกปิด แต่เมื่อมีการ reload หรือ reboot ใหม่ พอร์ตจะถูกเปิดขึ้นอีกครั้งตามไฟล์ที่บันทึกไว้ นี่คือช่องโหว่ที่คุณอาจไม่ทันสังเกต เพราะการตรวจสอบที่คุณทำไปนั้นผ่าน
การลบสิ่งที่ไม่มีอยู่จริงจะแสดงผล Warning: NOT_ENABLED: http และยังคง exit ด้วยสถานะ 0 การเพิ่มสิ่งที่ซ้ำกันจะแสดงผล Warning: ALREADY_ENABLED: http ทั้งสองกรณีนี้ปลอดภัย แต่หากสะกดชื่อผิดจะเป็นอีกเรื่องหนึ่ง: Error: INVALID_SERVICE หมายความว่า firewalld ไม่มีนิยามของชื่อนั้นและไม่มีการเปลี่ยนแปลงใดๆ เกิดขึ้นเลย
หาก --list-all ของคุณแสดง cockpit และคุณไม่ได้ใช้งาน Cockpit web console บนพอร์ต 9090 ให้ลบออกเสีย พอร์ตที่เปิดอยู่ทุกพอร์ตคือ service ที่คุณต้องคอยดูแลให้เป็นเวอร์ชันล่าสุดอยู่เสมอ และสำหรับพอร์ตที่คุณตัดสินใจเปิดไว้ dnf-automatic สามารถติดตั้งการอัปเดตความปลอดภัยตามเวลาที่กำหนดได้ เพื่อให้งานนี้ไม่ต้องขึ้นอยู่กับการที่คุณต้องคอยจำเอง อย่างไรก็ตาม การติดตั้ง patch ไม่เหมือนกับการรัน patch นั้น และ needs-restarting จะแสดงให้เห็นว่ามี service ใดบ้างที่ยังคงเรียกใช้ library เวอร์ชันเก่าอยู่ หลังจากที่การอัปเดตเหล่านั้นเสร็จสิ้นลง
การจำกัดพอร์ตให้รับเฉพาะจากแหล่งที่มาเดียว
Rich rules เป็นรูปแบบคำสั่งแบบยาวสำหรับกรณีที่ชื่อ service ปกติไม่สามารถระบุเงื่อนไขที่ต้องการได้ การจำกัด SSH ให้รับเฉพาะจาก IP ของสำนักงานต้องใช้คำสั่ง 2 ชุด และคำสั่งที่สองมักเป็นสิ่งที่ผู้ใช้งานลืมเสมอ
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reloadZone คือชุดของสิทธิ์ ไม่ใช่รายการลำดับเลขที่จะหยุดทำงานเมื่อพบเงื่อนไขแรก Rich rule จะเพิ่มการอนุญาต (accept) สำหรับหนึ่งที่อยู่ IP เท่านั้น แต่มันไม่ได้ปฏิเสธ (deny) ใครเลย ในขณะที่ ssh ยังคงอยู่ในบรรทัด services: อินเทอร์เน็ตทั้งหมดยังคงเข้าถึงพอร์ต 22 ได้ และ rich rule จะไม่ส่งผลลัพธ์ใดๆ ที่คุณสามารถวัดผลได้ คุณต้องลบรายการที่อนุญาตแบบกว้างออก มิฉะนั้นรายการที่จำกัดไว้ก็จะเป็นเพียงแค่การตกแต่งเท่านั้น
สำหรับพอร์ตที่ไม่มีชื่อ service ให้ระบุเป็นหมายเลขพอร์ตแทน
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port port="5432" protocol="tcp" accept'หากต้องการทิ้ง (drop) ทราฟฟิกจากเครือข่ายที่ก่อกวนและต้องการบันทึกข้อมูลไว้ ให้วาง element log ไว้ก่อน action ซึ่งเป็นลำดับที่ภาษาของ rich rule กำหนดไว้
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" log prefix="fw-drop " level="info" limit value="3/m" drop'
sudo firewall-cmd --reload
sudo journalctl -k -g fw-dropค่า limit จะช่วยป้องกันไม่ให้แพ็กเก็ตจำนวนมหาศาลเข้ามาเติมเต็มจน log เต็ม ก่อนที่คุณจะล็อก SSH ให้รับเฉพาะที่อยู่เดียว ต้องมั่นใจว่าที่อยู่ IP นั้นมีความเสถียร การเชื่อมต่ออินเทอร์เน็ตตามบ้านที่มี IP เปลี่ยนแปลงตลอดเวลาจะทำให้คุณเข้าใช้งานไม่ได้ในวันที่ IP เปลี่ยน ดังนั้นควรทดสอบและตรวจสอบให้แน่ใจว่าคุณสามารถเข้าถึงคอนโซลของผู้ให้บริการได้ก่อนเป็นอันดับแรก
คำสั่ง ufw และคำสั่งที่เทียบเท่าใน firewall-cmd
งานเดียวกันแต่ใช้เครื่องมือต่างกัน ทุกบรรทัด --permanent จำเป็นต้องมี sudo firewall-cmd --reload ตามหลัง ซึ่งเป็นสิ่งเดียวที่รายการลักษณะนี้ไม่สามารถแสดงให้คุณเห็นได้
sudo ufw enableกลายเป็นsudo systemctl enable --now firewalldsudo ufw disableกลายเป็นsudo systemctl disable --now firewalldsudo ufw status verboseกลายเป็นsudo firewall-cmd --list-allsudo ufw allow OpenSSHกลายเป็นsudo firewall-cmd --permanent --add-service=sshsudo ufw allow 443/tcpกลายเป็นsudo firewall-cmd --permanent --add-port=443/tcpsudo ufw delete allow 443/tcpกลายเป็นsudo firewall-cmd --permanent --remove-port=443/tcpsudo ufw allow from 203.0.113.10 to any port 22กลายเป็น rich rule ตามที่แสดงไว้ด้านบนsudo ufw reloadกลายเป็นsudo firewall-cmd --reloadsudo ufw default deny incomingคือลักษณะการทำงานของโซนpublicอยู่แล้ว และ--set-target=DROPคือเวอร์ชันที่ทำงานแบบเงียบของโซนดังกล่าวsudo ufw logging onกลายเป็นsudo firewall-cmd --set-log-denied=all
มีข้อแตกต่างประการหนึ่งที่ควรระบุให้ชัดเจน ufw เก็บรายการกฎแบบมีหมายเลขกำกับและคุณสามารถแทรกกฎไว้ที่ตำแหน่งที่ 1 ได้ แต่ firewalld ไม่มีหมายเลขกฎ ดังนั้นคำสั่ง "วางกฎนี้ไว้ลำดับแรก" จึงไม่มีความหมายในที่นี้ เมื่อรายการของ firewalld สองรายการดูเหมือนจะขัดแย้งกัน กฎที่อนุญาตแบบกว้างจะถือว่าชนะ เนื่องจากไม่มีกฎใดในชุดที่สั่งปฏิเสธ คุณต้องลบรายการที่กว้างเกินไปออกด้วยตนเอง
เหตุใดคอนเทนเนอร์ Docker ของฉันจึงเข้าถึงได้ทั้งที่ไฟร์วอลล์ดูเหมือนปิดอยู่?
เนื่องจากพอร์ตของคอนเทนเนอร์ที่ถูก publish ไม่เคยผ่านส่วนของไฟร์วอลล์ที่โซนของคุณควบคุมอยู่ docker run -d -p 8080:80 nginx สั่งให้ Docker เขียนกฎ NAT (network address translation) และกฎการส่งต่อ (forwarding) ของตนเอง แพ็กเก็ตที่เข้ามายังพอร์ต 8080 จะถูกเขียนใหม่และส่งต่อไปยังคอนเทนเนอร์ ดังนั้นแพ็กเก็ตจึงถูกส่งต่อ (forwarded) แทนที่จะถูกส่งมอบ (delivered) ให้กับโฮสต์ บรรทัด services: และ ports: ในโซนของคุณควบคุมเฉพาะแพ็กเก็ตที่ส่งมอบให้กับโฮสต์เท่านั้น กฎของ Docker ควบคุมเส้นทางการส่งต่อและยอมรับการเชื่อมต่อเหล่านั้น
ผลลัพธ์คือเซิร์ฟเวอร์ที่ sudo firewall-cmd --list-all ไม่แสดงพอร์ต 8080 แต่ nc -zv 203.0.113.20 8080 จากเครื่องอื่นกลับเชื่อมต่อได้ ให้ตรวจสอบสิ่งที่ Docker ติดตั้งไว้ดังนี้:
sudo iptables -t nat -L DOCKER -nวิธีแก้ไขอยู่ที่การใช้ flag สำหรับการ publish ให้ผูกพอร์ตไว้กับ loopback และวาง reverse proxy ไว้ด้านหน้าแทน
docker run -d -p 127.0.0.1:8080:80 nginxตอนนี้คอนเทนเนอร์จะตอบสนองต่อ curl http://127.0.0.1:8080 บนเซิร์ฟเวอร์เท่านั้นและไม่ตอบสนองต่อการเชื่อมต่อจากภายนอก ผู้ใช้ Ubuntu มักประสบปัญหาเดียวกัน ซึ่งอธิบายไว้ใน เหตุใดคอนเทนเนอร์ Docker จึง publish พอร์ตข้าม ufw ไปโดยตรง สำหรับ Podman แบบ rootful ซึ่งมาพร้อมกับ repository พื้นฐานของ Rocky และ AlmaLinux นั้นจะ publish พอร์ตด้วยวิธี NAT แบบเดียวกัน ดังนั้นควรทดสอบจากเครื่องอื่นแทนการเชื่อรายการในโซน ความซ้อนทับกันนี้เป็นเหตุผลว่าทำไม การติดตั้ง Docker Engine บน distribution เหล่านี้ จึงต้องมีขั้นตอนเพิ่มเติมที่คู่มือของ Ubuntu ไม่ได้กล่าวถึง โดยเริ่มจากการที่ Podman ครอบครองคำสั่ง docker ไปแล้ว
การทำให้ระบบทำงานได้หลังรีบูต และข้อผิดพลาดที่คุณอาจพบ
sudo systemctl is-enabled firewalld
sudo systemctl status firewalldenabled และ active (running) คือสิ่งที่คุณต้องการ ไฟร์วอลล์ที่กำลังทำงานอยู่แต่ไม่ได้เปิดใช้งาน (enabled) จะปกป้องคุณได้จนกว่าจะรีบูตครั้งแรก การตรวจสอบนี้ควรอยู่ในรายการที่คุณต้องทำใน สิบนาทีแรกบน VPS ใหม่ ควบคู่ไปกับการตั้งค่า SSH keys และการอัปเดตระบบ
คำสั่ง nftables ดิบและ firewalld ไม่สามารถใช้งานร่วมกันได้ firewalld เป็นเจ้าของตารางที่ชื่อว่า inet firewalld หาก sudo nft flush ruleset ลบตารางดังกล่าว เซิร์ฟเวอร์จะเปิดรับการเชื่อมต่อทุกอย่าง และ firewall-cmd --list-all จะยังคงแสดงการตั้งค่าที่คุณต้องการอยู่ เพราะ firewalld รายงานสิ่งที่มันเข้าใจ ไม่ใช่สิ่งที่ kernel ถือครองอยู่จริง sudo firewall-cmd --reload จะทำการติดตั้งกฎใหม่ทั้งหมด ควรเขียนกฎด้วย firewall-cmd เพื่อให้กฎเหล่านั้นกลับมาทำงานใหม่หลังจากโหลดการตั้งค่าใหม่
การใช้โปรแกรมจัดการไฟร์วอลล์สองตัวบนเซิร์ฟเวอร์เดียว การติดตั้ง ufw หรือ iptables-services ควบคู่ไปกับ firewalld จะทำให้คุณมีโปรแกรมสองตัวที่เขียนกฎโดยไม่รับรู้ถึงกันและกัน และตัวที่จะมีผลคือตัวที่เริ่มทำงานหลังสุด ให้เลือกใช้เพียงตัวเดียว สำหรับ Rocky และ AlmaLinux นั้น firewalld คือตัวที่ได้รับการสนับสนุนจาก distribution
ไฟร์วอลล์ของผู้ให้บริการที่อยู่หน้าเซิร์ฟเวอร์ แผงควบคุม VPS หลายแห่งมีไฟร์วอลล์เครือข่ายแยกต่างหาก หาก --list-all แสดงว่าพอร์ตเปิดอยู่แต่การเชื่อมต่อจากภายนอกยังคงล้มเหลว ให้ตรวจสอบที่แผงควบคุมก่อนที่จะแก้ไขสิ่งใดบนเซิร์ฟเวอร์ ในทางกลับกัน กฎที่เปิดไว้บนแผงควบคุมจะไม่มีผลหาก firewalld ปฏิเสธแพ็กเก็ตนั้น
การรัน firewall-cmd โดยไม่ใช้ sudo ทุกการเปลี่ยนแปลงจำเป็นต้องใช้สิทธิ์ root หากไม่มีสิทธิ์ดังกล่าว คำขอจะถูกปฏิเสธโดยการตรวจสอบสิทธิ์และไม่มีการแก้ไขใดๆ เกิดขึ้น ซึ่งดูเผินๆ อาจเข้าใจผิดว่าคำสั่งถูกละเลย
คำสั่งหกรายการครอบคลุมการใช้งานส่วนใหญ่: --list-all เพื่ออ่านสถานะ, --permanent --add-service หรือ --add-port เพื่อเปิดพอร์ต, --permanent --remove-service เพื่อปิดพอร์ต, --reload เพื่อนำไฟล์ที่บันทึกไว้มาใช้งาน และ --runtime-to-permanent หลังจากทดลองตั้งค่าเสร็จสิ้น โซนที่ใช้คือ public, แฟล็กที่ใช้คือ --permanent และการตรวจสอบที่ถูกต้องที่สุดคือการตรวจสอบจากเครื่องอื่นเท่านั้น
FAQ
ทำไมกฎ firewalld ของฉันถึงหายไปหลังจากรีบูตเครื่อง?
กฎดังกล่าวถูกเพิ่มเข้าไปใน configuration ขณะรันไทม์เท่านั้น sudo firewall-cmd --add-service=http จะมีผลทันทีแต่จะถูกลบทิ้งเมื่อมีการโหลดใหม่หรือรีบูตเครื่อง เนื่องจาก configuration ที่บันทึกไว้ใน /etc/firewalld/zones/public.xml ไม่ได้รับการแก้ไข ให้เพิ่ม --permanent แล้วรัน sudo firewall-cmd --reload หากต้องการเก็บกฎที่คุณเพิ่มด้วยตนเองไปแล้ว ให้รัน sudo firewall-cmd --runtime-to-permanent ซึ่งจะเป็นการคัดลอกกฎที่ใช้งานอยู่ลงในไฟล์ที่บันทึกไว้
ทำไมไม่มีอะไรเปลี่ยนแปลงหลังจากฉันเพิ่มกฎด้วย --permanent?
เพราะ --permanent จะเขียนไฟล์ลงดิสก์แต่ไม่ได้แก้ไข firewall ที่กำลังทำงานอยู่ พอร์ตจะยังคงปิดอยู่จนกว่า sudo firewall-cmd --reload จะโหลด configuration ที่บันทึกไว้เข้าสู่ kernel ให้เปรียบเทียบ sudo firewall-cmd --list-services กับ sudo firewall-cmd --permanent --list-services หากรายการที่บันทึกไว้มีรายการที่รายการปัจจุบันไม่มี แสดงว่าคุณลืมรันคำสั่งโหลดใหม่
ฉันควรใช้ --add-service หรือ --add-port?
ให้ใช้ --add-service เมื่อมีชื่อบริการที่ตรงกับสิ่งที่คุณรันอยู่ เพราะเป็นการระบุความต้องการที่ชัดเจน และ sudo firewall-cmd --info-service=https จะแสดงให้เห็นว่าชื่อบริการนั้นครอบคลุมพอร์ตใดบ้าง ให้ใช้ --add-port เมื่อไม่มีคำจำกัดความของบริการนั้น หรือเมื่อบริการรันอยู่บนพอร์ตที่ไม่เป็นมาตรฐาน บริการ ssh หมายถึงพอร์ต 22/tcp เท่านั้น ดังนั้นหากย้าย SSH ไปยังพอร์ต 2222 จำเป็นต้องใช้ --add-port=2222/tcp ร่วมกับการกำหนด SELinux label ให้กับพอร์ตนั้นด้วย
ทำไม Docker container ของฉันถึงเข้าถึงได้ทั้งที่ firewall-cmd แสดงว่าพอร์ตปิดอยู่?
พอร์ตที่ถูก publish จะถูกเขียนทับด้วยกฎ NAT ของ Docker เองและส่งต่อไปยัง container ดังนั้นแพ็กเก็ตจะไม่ถูกส่งมาที่โฮสต์โดยตรง และรายการ service หรือพอร์ตของ zone จะครอบคลุมเฉพาะแพ็กเก็ตที่ส่งมาถึงโฮสต์เท่านั้น container จึงตอบสนองจากอินเทอร์เน็ตได้ในขณะที่ --list-all ไม่แสดงข้อมูลใดๆ ให้ publish ไปยัง loopback แทนด้วย docker run -d -p 127.0.0.1:8080:80 nginx และใช้ reverse proxy วางไว้ด้านหน้าแทน
ฉันสามารถติดตั้ง ufw บน Rocky Linux แทน firewalld ได้หรือไม่?
การมี firewall manager สองตัวบนเซิร์ฟเวอร์เดียวกันจะทำให้เกิดการเขียนกฎโดยที่อีกตัวไม่รับรู้ และกฎชุดใดจะคงอยู่ขึ้นอยู่กับว่าบริการใดเริ่มทำงานทีหลัง firewalld เป็นเครื่องมือที่รองรับบน Rocky Linux และ AlmaLinux โดยมีการติดตั้งมาให้แล้ว และมันทำงานบน backend เดียวกันกับที่ ufw ใช้คือ nftables ให้เรียนรู้เรื่อง default zone และแฟล็ก --permanent เพียงครั้งเดียว คุณก็จะเข้าใจการใช้งานเครื่องมือนี้ทั้งหมด