Bối cảnh: Tại sao cần siết quyền Linux Capabilities?
Đúng 2 giờ sáng, hệ thống SOC rung chuông báo động đỏ. Một web service Node.js bị khai thác lỗ hổng RCE từ thư viện npm bên thứ ba, cho phép kẻ tấn công chèn webshell. Ngay lập tức, chúng cố quét mạng nội bộ, sửa routing table và dump gói tin của máy chủ.
Rất may kịch bản tồi tệ nhất — thoát khỏi container để kiểm soát server mẹ — đã không xảy ra. Lý do rất đơn giản: container này đã bị tước sạch quyền can thiệp kernel ngay từ khi khởi chạy.
Theo mặc định, Docker không cấp toàn quyền root máy chủ cho container. Dù vậy, nó vẫn gán sẵn 14 Linux Capabilities (như CAP_NET_RAW, CAP_CHOWN, CAP_MKNOD, CAP_SYS_CHROOT…).
Nếu hacker chiếm được quyền root trong container, chúng sẽ tận dụng ngay các capability dư thừa này. Chúng có thể sniff gói tin mạng nội bộ, giả mạo ARP cache hoặc khai thác lỗi kernel chưa vá để leo thang đặc quyền ra máy chủ host.
Nguyên tắc vàng trên production là Đặc quyền tối thiểu (Least Privilege). Cách an toàn nhất: tước sạch toàn bộ quyền bằng --cap-drop=ALL, sau đó chỉ cấp lại đúng 1–2 quyền mà service thực sự cần qua --cap-add.
Chuẩn bị công cụ kiểm tra Capabilities
Để biết chính xác container đang giữ những quyền gì, bạn cần cài đặt bộ công cụ libcap trên máy Linux.
Chạy lệnh cài đặt trên Ubuntu/Debian:
sudo apt-get update && sudo apt-get install -y libcap2-bin libcap-ng-utils
Giờ hãy chạy thử một container Alpine để xem Docker mặc định cấp những gì:
# Khởi chạy container alpine thử nghiệm
docker run -d --name test-cap alpine sleep 3600
# Lấy PID của process container trên host
PID=$(docker inspect --format '{{.State.Pid}}' test-cap)
# Xem capabilities mà process đang giữ
getpcaps $PID
# Dọn dẹp sau khi kiểm tra
docker rm -f test-cap
Kết quả trả về sẽ gồm nhiều quyền như cap_chown, cap_net_raw, cap_sys_chroot. Một API microservice thông thường hoàn toàn không cần tới CAP_NET_RAW hay CAP_MKNOD.
Cấu hình thực tế: Kết hợp cap-drop và cap-add
Quy tắc thực chiến chuẩn nhất là whitelist: Drop sạch trước, Add từng quyền sau.
1. Cấu hình trực tiếp bằng Docker CLI
Xét ví dụ container Nginx cần lắng nghe cổng 80/443 và phân quyền file khi khởi động. Bạn chỉ cần cấp đúng 3 quyền: NET_BIND_SERVICE, SETUID và SETGID:
docker run -d \
--name secure-web \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--cap-add=SETUID \
--cap-add=SETGID \
-p 80:80 \
nginx:alpine
Khi đã khóa quyền, hacker dù có shell cũng không thể chạy ping hay nmap quét mạng vì mất CAP_NET_RAW. Chúng cũng không thể tạo block device ảo để chọc xuống ổ cứng host do thiếu CAP_MKNOD.
2. Chuẩn hóa cấu hình với Docker Compose
Khi quản lý hàng chục microservices, việc gõ lệnh CLI thủ công rất dễ sót cờ bảo mật. Toàn bộ thiết lập cần được đồng bộ trực tiếp vào file docker-compose.yml:
version: '3.8'
services:
api-backend:
image: node:20-alpine
container_name: production-api
working_dir: /app
command: node server.js
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
ports:
- "80:80"
restart: always
Cấu hình no-new-privileges:true là chốt chặn quan trọng. Nó ngăn chặn process con tự động nâng quyền thông qua các binary có cờ setuid/setgid như sudo hay suid.
Bảng tra cứu Linux Capabilities quan trọng
- CAP_NET_BIND_SERVICE: Cho phép bind port dưới 1024 (80, 443). Bắt buộc phải có nếu chạy web server non-root mở port đặc quyền.
- CAP_NET_RAW: Dùng để tạo raw packet hoặc chạy lệnh ICMP (ping). Nên DROP trên toàn bộ container web/API để chặn quét cổng và giả mạo ARP.
- CAP_SYS_ADMIN: Quyền tương đương 90% full root. Tuyệt đối không bật cờ này trên production, trừ khi chạy Docker-in-Docker hoặc trace kernel.
- CAP_CHOWN: Cho phép thay đổi UID/GID sở hữu file. Chỉ cấp nếu script entrypoint cần
chownlại thư mục dữ liệu lúc khởi động.
Kiểm tra và giám sát đặc quyền sau khi deploy
Sau khi siết quyền, bạn cần kiểm chứng lại xem container có chạy ổn định không và quyền đã thực sự bị thu hồi hay chưa.
Kiểm tra quyền trực tiếp từ bên trong container
Đọc trạng thái process qua file /proc/1/status:
docker exec -it secure-web sh -c "grep Cap /proc/1/status"
Hệ thống trả về các chuỗi hex đại diện cho bitmap capability:
CapInh: 0000000000000000
CapPrm: 00000000000004c0
CapEff: 00000000000004c0
CapBnd: 00000000000004c0
Giải mã chuỗi hex này trên máy host bằng lệnh capsh:
capsh --decode=00000000000004c0
Màn hình hiển thị: cap_setgid,cap_setuid,cap_net_bind_service. Toàn bộ các capability thừa thãi đã bị loại bỏ hoàn toàn.
Xử lý sự cố khi ứng dụng báo lỗi Permission Denied
Nếu ứng dụng bị crash sau khi thêm --cap-drop=ALL, đừng vội bật lại quyền bừa bãi. Hãy tra cứu log container và kernel audit log trên host:
# Kiểm tra log ứng dụng
docker logs --tail 50 secure-web
# Tìm capability bị kernel chặn
sudo dmesg -T | grep -i "apparmor\|audit\|denied"
Khi thấy thông báo Operation not permitted, hãy xác định thao tác gây lỗi rồi bổ sung đúng capability đó vào cap_add. Giữ vững kỷ luật này sẽ bảo vệ toàn bộ hạ tầng Docker của bạn trước các đòn tấn công container escape.

