Bốn dấu tick xanh không phủ lớp thật sự hỏng
ufw đang active, scanner không thấy gì, job backup thoát 0, container vẫn up — bốn dấu tick xanh và ba lớp đã hỏng từ trước. Mỗi tick không phủ cái gì, và những lệnh cho thấy điều đó.
Máy chủ tự dựng thường không chết ồn ào. Nó hỏng lặng lẽ ở đúng chỗ dashboard đang xanh: bạn đã bật firewall, đã cắm scanner, job backup đêm qua chạy xong không lỗi. Không tick nào trong ba cái đó chứng minh cổng đã đóng, advisory đã được đọc, hay bản backup khôi phục được.
Bốn dấu tick, và ba trong số đó chỉ là ghi sổ:
ufwđang active.- Scanner đã chạy và không báo gì.
- Lượt backup gần nhất màu xanh.
- Health check của container vẫn trả lời.
Cái cuối chỉ nói tiến trình còn sống. Ba cái đầu trông như bảo mật nhưng thực ra ghi lại rằng đã có người cấu hình một job. Dưới đây là phần mỗi tick không phủ.
1. ufw status ghi active, Docker vẫn publish ra ngoài
ufw ghi luật vào chain INPUT — tức gói tin gửi tới chính máy chủ. Cổng Docker publish thì không gửi tới máy chủ. Docker chèn luật riêng ở chain PREROUTING của bảng nat, gói được viết lại địa chỉ đích thành địa chỉ container, rồi đi tiếp theo đường FORWARD. Nó không bao giờ gặp luật INPUT của bạn. Luật "deny 8080" có thể nằm đó, active, xanh, trong khi docker run -p 8080:80 trả lời cho cả internet. Bài viết về chuyện này còn nhắc một thiết lập log rotation nên đặt trước khi đĩa đầy (luật ufw và cổng Docker).
Xem thực tế có luật gì, và nằm ở chain nào:
sudo iptables -t nat -S DOCKER # Docker đã publish những gì
sudo iptables -S DOCKER-USER # hook của bạn, chạy trước luật FORWARD của Docker
sudo ss -tlnp | grep -E ':(80|443|8080|5432)' # máy đang thật sự nghe cổng nàoRồi đừng hỏi chính máy chủ nữa, hỏi từ bên ngoài — một máy khác, hoặc điện thoại dùng 4G:
nmap -Pn -p 1-65535 <ip-máy-chủ>Đạt không phải là "ufw đang active". Đạt là: không có gì với tới được mà bạn không định mở. Kể cả địa chỉ mà cổng bind vào. -p 8080:80 bind mọi interface; -p 127.0.0.1:8080:80 chỉ bind loopback và đẩy cổng về sau lớp đứng trước — reverse proxy, VPN mesh, tunnel. Nếu cổng buộc phải mở nhưng cần lọc, luật phải nằm ở DOCKER-USER chứ không phải INPUT. Bạn cũng có thể bảo Docker đừng quản iptables nữa và tự viết luật — nhưng khi đó bạn sở hữu mọi luật mà Docker từng viết hộ, kể cả luồng giữa các container.
2. Scanner sạch, còn advisory thì không nêu tên package
Scanner chỉ so được với thứ nó nhìn thấy: layer của image, phiên bản đã cài, package của hệ điều hành. Nó không so được với một advisory không hề nêu tên module bị ảnh hưởng. Một kết quả pre-registered báo rằng khoảng một nửa advisory bảo mật của chính các app self-host không nêu package liên quan (kết quả pre-registered), và bài follow-up xem điều gì quyết định advisory của app có tới được scanner hay không (bài follow-up). Đó chính là khoảng trống: một scanner có thể vừa đúng vừa mù trong cùng một lượt chạy.
Nên hãy giữ thêm một danh sách mà không scanner nào điền hộ. Ghim lại bạn đang chạy đúng cái gì:
docker image ls --digests --format '{{.Repository}}\t{{.Tag}}\t{{.Digest}}'
docker inspect -f '{{index .Config.Labels "org.opencontainers.image.version"}}' <container>Rồi mở trang advisory của chính app, hoặc mục security advisories trên GitHub của nó, đọc phiên bản đã vá, so với phiên bản bạn đang chạy. Mỗi advisory một dòng là đủ — tên app, phiên bản đang chạy, advisory, phiên bản đã vá, ngày kiểm. Mục tiêu không phải tự động hoá, mà là: đọc bằng mắt bắt được đúng loại advisory mà công cụ so phiên bản không thấy.
3. "Backup chạy rồi" không phải "backup khôi phục được"
Job backup xanh nghĩa là job thoát mã 0. Nó không có nghĩa bản dump đầy đủ, không có nghĩa trong đó có bảng bạn cần, và không có nghĩa thông tin đăng nhập trong job còn dùng được trên máy mới. Tập podcast đặt tên cho kiểu nhầm này — "the green check lies" — nói đúng chuyện đó ở cả backup, networking lẫn self-host nói chung (tập podcast).
Phép thử duy nhất có giá trị là một lần khôi phục bạn thật sự chạy, vào một chỗ dùng xong là bỏ:
docker run --rm -d --name restore-test -p 127.0.0.1:55432:5432 postgres:16
pg_restore -h 127.0.0.1 -p 55432 -U postgres -d restored /backups/db.dump
psql -h 127.0.0.1 -p 55432 -U postgres -d restored -c 'select count(*) from orders;'Ghi lại số phút đã mất. Đó là thời gian khôi phục thật của bạn, và cũng là con số backup duy nhất đáng đưa lên dashboard. Đặt lịch diễn tập mỗi tháng một lần: một bản backup chưa từng khôi phục thử chỉ là hy vọng kèm một dòng cron.
Kiểm bằng gì
Tuần này chạy bốn việc sau trên một máy chủ và giữ lại kết quả:
sudo iptables -t nat -S DOCKERvàss -tlnp— cổng đang publish có đúng cái bạn định mở không?nmap -Pn -p 1-65535 <ip-máy-chủ>từ máy khác — có gì với tới được mà lẽ ra không nên không?- Một lần khôi phục vào container dùng-một-lần, rồi đếm số dòng — dữ liệu có về không, và mất bao nhiêu phút?
- Một trang advisory của một app bạn đang chạy, đối chiếu với phiên bản bạn dùng — có bản đã vá nào bạn còn chưa lên không?
Bốn dấu tick có thể cùng xanh trong khi cả bốn câu trả lời đều khó chịu. Tick chỉ nói một job đã xong. Chỉ có phép kiểm mới nói chuyện gì đã xảy ra.
