1. Phân tích cấu trúc và viết tay Profile Seccomp JSON
Profile Seccomp là một file JSON xác định danh sách các system call được phép (allow) hoặc bị chặn (default) khi container chạy. Chúng ta sẽ xây dựng file này từ đầu để hiểu rõ cơ chế lọc.
File JSON có cấu trúc gồm hai phần chính: defaultAction quy định hành động mặc định cho các system call không nằm trong danh sách, và syscalls là danh sách các system call cụ thể cần kiểm soát. Giá trị SCMP_ACT_ERRNO sẽ khiến system call bị từ chối và trả về lỗi -1, trong khi SCMP_ACT_ALLOW cho phép thực thi.
Tạo file profile tùy chỉnh tại đường dẫn /etc/seccomp/my-db-profile.json. Nội dung dưới đây cấu hình chặn tất cả system call trừ những cái được liệt kê trong syscalls và cho phép một số call quan trọng như read, write, open, close.
cat > /etc/seccomp/my-db-profile.json
File /etc/seccomp/my-db-profile.json đã được tạo thành công. Nội dung này thiết lập chính sách mặc định là chặn (SCMP_ACT_ERRNO) và chỉ cho phép thực thi các system call nằm trong danh sách names ở trên.
2. Sử dụng công cụ seccomp-generator để tối ưu hóa danh sách
Việc viết tay danh sách system call rất dễ bỏ sót. Công cụ seccomp-generator (từ package libseccomp-dev hoặc seccomp) giúp tự động tạo profile dựa trên hành vi thực tế của ứng dụng hoặc danh sách trắng an toàn hơn.
Chúng ta sẽ sử dụng seccomp-dump hoặc seccomp-gen (tùy phân phối) để tạo file JSON từ một danh sách system call cơ bản, sau đó mở rộng. Ở đây, giả sử chúng ta có sẵn danh sách các call cần thiết cho Database và dùng seccomp-gen để format thành JSON chuẩn.
Lưu ý: Nếu bạn không cài sẵn seccomp-gen, bạn có thể dùng seccomp-tools hoặc viết script nhỏ. Dưới đây là cách sử dụng seccomp-gen (cần cài đặt trước bằng apt install seccomp-tools hoặc build từ source) để tạo profile từ file danh sách text.
echo "read write open close mmap munmap exit execve brk" > /tmp/db-syscalls.txt
seccomp-gen -f /tmp/db-syscalls.txt -o /etc/seccomp/my-db-profile-gen.json -a SCMP_ACT_ALLOW -d SCMP_ACT_ERRNO
Thay vì cài đặt phức tạp, cách an toàn và nhanh nhất trong môi trường production là dùng file JSON đã tạo ở bước 1, nhưng bổ sung thêm các call cụ thể nếu container báo lỗi. Chúng ta sẽ mô phỏng việc chỉnh sửa file JSON bằng jq để thêm một system call mới (ví dụ: wait4 nếu chưa có) vào danh sách cho phép.
jq '.syscalls[0].names += ["wait4", "waitid"]' /etc/seccomp/my-db-profile.json > /tmp/temp-profile.json && mv /tmp/temp-profile.json /etc/seccomp/my-db-profile.json
File profile /etc/seccomp/my-db-profile.json đã được cập nhật. Bây giờ danh sách names bao gồm thêm wait4 và waitid để đảm bảo các tiến trình con có thể được quản lý đúng cách mà không bị chặn.
Verify: Kiểm tra lại cú pháp JSON và nội dung bằng lệnh jq . /etc/seccomp/my-db-profile.json để đảm bảo không có lỗi syntax.
3. Cấu hình Docker sử dụng Profile Seccomp tùy chỉnh
Docker hỗ trợ nạp profile Seccomp tùy chỉnh thông qua flag --security-opt seccomp= khi chạy container. Nếu không dùng Docker Compose, bạn cần truyền flag này trực tiếp vào lệnh docker run.
Để áp dụng profile chúng ta vừa tạo cho một container Database (ví dụ: PostgreSQL hoặc MySQL), chúng ta sẽ chỉ định đường dẫn tuyệt đối đến file JSON. Docker sẽ tải file này và áp dụng chính sách lọc system call trước khi khởi động tiến trình chính của container.
docker run -d \
--name secure-db \
--security-opt seccomp=/etc/seccomp/my-db-profile.json \
-e POSTGRES_PASSWORD=securepass \
-e POSTGRES_DB=production \
postgres:15-alpine
Lệnh trên khởi tạo container secure-db với PostgreSQL 15 Alpine, đồng thời ép buộc Docker sử dụng file Seccomp ở /etc/seccomp/my-db-profile.json. Nếu profile bị lỗi hoặc thiếu system call quan trọng, container sẽ crash ngay lập tức.
Verify: Kiểm tra trạng thái container bằng docker ps. Nếu container đang chạy (Status: Up), nghĩa là profile hợp lệ. Nếu container đã dừng (Status: Exited), hãy xem log để biết lỗi.
4. Kiểm tra sự cố và xử lý container crash do Seccomp
Khi áp dụng profile Seccomp quá chặt chẽ, container thường bị crash ngay khi khởi động vì thiếu một system call cần thiết mà ta chưa liệt kê. Docker không hiển thị rõ ràng lỗi Seccomp trong docker logs mà thường báo lỗi exec: unknown command hoặc container tắt ngay lập tức.
Để xác định chính xác system call nào bị chặn, chúng ta cần xem log của Docker daemon hoặc kiểm tra dmesg trên host. Tuy nhiên, cách nhanh nhất là chạy container với chế độ debug hoặc xem docker inspect để thấy thông báo lỗi từ kernel.
docker logs secure-db 2>&1 | grep -i "seccomp\|sys\|error"
dmesg | tail -n 20
Nếu thấy thông báo lỗi liên quan đến Seccomp hoặc syscall, hãy xác định tên system call bị chặn. Sau đó, mở file profile JSON và thêm tên system call đó vào mảng names của syscalls.
Ví dụ: Nếu log báo lỗi thiếu socket, bạn cần cập nhật file profile:
jq '.syscalls[0].names += ["socket"]' /etc/seccomp/my-db-profile.json > /tmp/temp-profile.json && mv /tmp/temp-profile.json /etc/seccomp/my-db-profile.json
docker stop secure-db && docker rm secure-db
docker run -d --name secure-db --security-opt seccomp=/etc/seccomp/my-db-profile.json -e POSTGRES_PASSWORD=securepass -e POSTGRES_DB=production postgres:15-alpine
Sau khi cập nhật profile và restart container, hệ thống sẽ cho phép system call mới. Quá trình này lặp lại cho đến khi container chạy ổn định mà không còn báo lỗi Seccomp.
Verify: Chạy docker ps để đảm bảo container đang ở trạng thái Up. Sử dụng docker exec -it secure-db sh để vào bên trong container và chạy một lệnh đơn giản (ví dụ cat /etc/hostname) để xác nhận các system call cơ bản vẫn hoạt động.
Điều hướng series:
Mục lục: Series: Xây dựng Database an toàn với Linux Seccomp và Systemd Service Hardening
« Phần 3: Nguyên lý hoạt động và phân tích System Call của Database
Phần 5: Hardening Docker Container với Linux Capabilities và Namespaces »