Break/Fix: chạy trên server được, từ máy khác thì không
Tạo sự cố trong phạm vi lab của bài, thu bằng chứng, sửa rồi kiểm tra.
Tình huống
Dịch vụ kdc-catalog nghe trên port 8070, mọi interface. Trên server, curl http://127.0.0.1:8070/ trả kết quả bình thường. Từ máy của bạn thì bị từ chối. Ticket cũ ghi "đã mở port 8070 trên firewall".
Nhiệm vụ: cho phép truy cập 8070/tcp từ bên ngoài, bền vững sau reload và reboot, không tắt firewall, và viết RCA.
Vì sao cần học
"Đã mở port rồi mà" là câu bạn sẽ nghe rất nhiều. Port có thể được mở ở zone không gắn với interface, chỉ ở runtime, hoặc bị một rule khác chặn. Phải đọc cấu hình đang áp dụng cho đúng interface, không phải cấu hình người khác kể lại.
Mục tiêu
- Phân biệt "dịch vụ không nghe" (kiểm tra trên server) với "bị chặn ở mạng" (kiểm tra từ ngoài).
- Tìm zone đang gắn với interface và đọc toàn bộ rule của zone đó.
- Hiểu thứ tự xử lý: rich rule
reject/dropthắng port được mở.
Điều kiện tiên quyết
- Bài Firewall với firewalld: zone, service và port.
Tạo sự cố
Đọc kỹ script, lưu vào ~/break-firewall.sh trên VM, chạy sudo bash ~/break-firewall.sh.
#!/usr/bin/env bash
# KDC Break/Fix: firewall block. Chỉ chạy trên VM lab có /etc/kdc-lab.
set -euo pipefail
[[ $EUID -eq 0 ]] || { echo "Chạy bằng sudo." >&2; exit 1; }
grep -qs '^KDC-LAB' /etc/kdc-lab || { echo "Không thấy marker /etc/kdc-lab; script chỉ chạy trên máy lab." >&2; exit 1; }
firewall-cmd --state >/dev/null 2>&1 || { echo "firewalld chưa chạy; làm bài 'Firewall với firewalld' trước." >&2; exit 1; }
firewall-cmd --query-service=ssh >/dev/null || { echo "SSH chưa được phép trên firewall; dừng để tránh mất kết nối." >&2; exit 1; }
read -r -p "Script sẽ tạo kdc-catalog và thêm rule firewall sai. Tiếp tục? [y/N] " answer
[[ $answer == y ]] || exit 1
id -u kdc-catalog &>/dev/null || useradd --system --no-create-home --shell /usr/sbin/nologin kdc-catalog
install -d -m 755 /srv/kdc-catalog
echo '<h1>KDC-CATALOG</h1>' > /srv/kdc-catalog/index.html
cat > /etc/systemd/system/kdc-catalog.service <<'UNIT'
[Unit]
Description=KDC catalog
[Service]
User=kdc-catalog
ExecStart=/usr/bin/python3 -m http.server 8070 --directory /srv/kdc-catalog
Restart=on-failure
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now kdc-catalog.service
zone=$(firewall-cmd --get-default-zone)
firewall-cmd --permanent --zone=internal --add-port=8070/tcp
firewall-cmd --permanent --zone="$zone" --add-rich-rule='rule family="ipv4" port port="8070" protocol="tcp" reject'
firewall-cmd --reload
echo "Đã tạo sự cố. Từ máy của bạn: curl -m 5 http://<ip-của-vm>:8070/"
Giải thích lệnh và tham số
zone=$(firewall-cmd --get-default-zone) lấy tên zone mặc định vào biến. --zone=internal chọn zone cụ thể cho lần gọi; không có --zone thì dùng zone mặc định. --query-service=ssh hỏi rule SSH có được phép không; mã thoát được script dùng để quyết định dừng. --add-rich-rule='...' nhận cả chuỗi rule nhờ nháy đơn; reject trả phản hồi từ chối, drop bỏ gói không trả lời.
curl -m 5 <URL> ở thông báo cuối script giới hạn toàn bộ lần gọi ở 5 giây; -m là dạng ngắn của --max-time. Chạy phép thử từ laptop để đi qua firewall VM, thay <ip-của-vm> bằng IP thật.
Bấm Kiểm tra kết quả một lần để thấy trạng thái xuất phát.
Điều tra
- Symptom: từ máy của bạn, lỗi là
Connection refused,No route to hosthay timeout? - Scope: trên chính server, dịch vụ có trả lời không? Nó nghe trên địa chỉ nào?
- Layer: interface nhận gói tin thuộc zone nào?
- Evidence: toàn bộ rule của zone đó, ở runtime và permanent, là gì?
- Fix: sửa đúng zone, bỏ rule chặn, áp dụng bền vững, kiểm tra lại từ máy của bạn.
Đọc các công cụ điều tra
--get-active-zones cho biết zone gắn với interface/source; --list-all --zone=internal xem một zone cụ thể, không phải mọi zone. --list-rich-rules in rule đầy đủ. --remove-rich-rule='<chuỗi>' yêu cầu chuỗi rule giống rule đã lưu; --remove-port=<port>/<protocol> bỏ rule port. --permanent và --reload có ý nghĩa đã học ở bài firewall. Các lệnh truy vấn chưa thay đổi rule.
Gợi ý (chỉ mở khi bế tắc)
- Mức 1, hướng nhìn: có hai vấn đề: port được mở nhầm chỗ, và một rule chủ động từ chối.
- Mức 2, công cụ:
ss -ltnp 'sport = :8070',sudo firewall-cmd --get-active-zones,sudo firewall-cmd --list-all,sudo firewall-cmd --list-all --zone=internal,sudo firewall-cmd --list-rich-rules. - Mức 3, một bước:
--remove-rich-rulecần chuỗi rule giống hệt chuỗi trong--list-rich-rules. Copy nguyên văn và nhớ--permanentrồi--reload.
Viết RCA
Tạo ~/kdc-labs/rca/firewall.md:
# RCA: kdc-catalog không truy cập được từ bên ngoài
## Triệu chứng
## Evidence
## Nguyên nhân gốc
## Cách sửa
## Phòng tránh
Trong Evidence, ghi lại lỗi nhìn thấy từ máy của bạn trước và sau khi sửa. Connection refused ngay lập tức và timeout cho bạn biết điều gì khác nhau?
Tự kiểm tra (Verification)
Trên VM:
sudo firewall-cmd --list-all
sudo firewall-cmd --permanent --list-all
Giải thích lệnh và tham số
firewall-cmd --list-all xem zone cùng rule runtime; thêm --permanent để xem cấu hình đã lưu, có hiệu lực sau reload/reboot.
Từ máy của bạn: curl -s http://<ip-của-vm>:8070/ trả trang chứa KDC-CATALOG. Chọn server và bấm Kiểm tra kết quả.
Ghi chú production
- Kiểm tra kết nối từ phía client, không chỉ trên server. Rất nhiều sự cố được báo "đã sửa" chỉ vì
curl localhostchạy được. - Trên cloud còn có security group/firewall mạng; cần kiểm tra cả hai lớp (Module 09).
- Ghi lại lý do của mỗi rule firewall (Ansible, Module 07), để người sau biết rule nào còn cần và rule nào là rác.
Dọn dẹp
zone=$(sudo firewall-cmd --get-default-zone)
sudo firewall-cmd --permanent --zone="$zone" --remove-port=8070/tcp
sudo firewall-cmd --permanent --zone=internal --remove-port=8070/tcp
sudo firewall-cmd --permanent --zone="$zone" --remove-rich-rule='rule family="ipv4" port port="8070" protocol="tcp" reject'
sudo firewall-cmd --reload
sudo systemctl disable --now kdc-catalog.service
sudo rm -f /etc/systemd/system/kdc-catalog.service
sudo systemctl daemon-reload
sudo rm -rf /srv/kdc-catalog
sudo userdel kdc-catalog
Giải thích lệnh và tham số
--get-default-zone lấy zone mặc định vào biến zone; --zone="$zone" chọn zone đó, --zone=internal chọn zone internal. --permanent sửa rule đã lưu; --remove-port=8070/tcp bỏ rule cổng, --remove-rich-rule bỏ rich rule có nội dung khớp. --reload áp dụng lại permanent. systemctl disable --now bỏ cấu hình tự chạy khi boot và dừng service ngay. daemon-reload yêu cầu systemd đọc lại unit file sau khi sửa/xóa. rm -f xóa file và không hỏi xác nhận; -r thêm xóa đệ quy thư mục cùng nội dung, nên -rf gộp hai tùy chọn này.
Một vài lệnh --remove-* có thể báo NOT_ENABLED nếu bạn đã gỡ rule đó khi sửa; như vậy là bình thường.