Kiểm thử bảo mật với Docker Bench Security
Để xác minh các biện pháp hardening đã áp dụng trong các phần trước có hiệu quả, ta sử dụng công cụ Docker Bench Security. Công cụ này thực hiện 64 kiểm tra dựa trên CIS Benchmark để phát hiện các lỗ hổng cấu hình.
Tải script kiểm tra về thư mục tạm thời để chạy mà không cần cài đặt phức tạp.
curl -L https://github.com/docker/docker-bench-security/releases/download/v0.6.3/docker-bench-security-v0.6.3-linux-amd64.tar.gz | tar -xz
Chạy script với quyền root để quét toàn bộ hệ thống, bao gồm cả kernel và cấu hình container đang chạy.
cd docker-bench-security-v0.6.3-linux-amd64 && ./docker-bench-security.sh
Kết quả mong đợi: Bạn sẽ thấy danh sách các lỗi (FAIL) và cảnh báo (WARN). Mục tiêu là giảm số lượng FAIL xuống mức thấp nhất, đặc biệt ở các mục liên quan đến container database.
Để kiểm tra cụ thể container database của bạn, hãy lấy ID container và chạy lệnh kiểm tra có chọn lọc.
docker container ls --format "{{.ID}}" | grep -E "postgres|mysql|mariadb" | xargs -I {} docker inspect --format '{{.Name}}' {}
Chạy lại Docker Bench nhưng tập trung vào container cụ thể bằng cách thêm tham số --target-container nếu script hỗ trợ, hoặc phân tích log đầu ra để tìm các dòng có chứa ID container của bạn.
./docker-bench-security.sh | grep -i "FAIL" | head -n 20
Kiểm tra lại kết quả: Các lỗi liên quan đến Seccomp (thường là test 20.x) và Capabilities (test 21.x) nên chuyển sang trạng thái PASS hoặc WARN nếu đã cấu hình đúng trong phần trước.
Xử lý lỗi 'Permission denied' và 'System call not allowed'
Khi áp dụng Seccomp profile quá khắt khe, database thường gặp lỗi "Permission denied" hoặc "Operation not permitted" khi cố thực hiện một hệ thống gọi (syscall) chưa được whitelist.
Để xác định chính xác syscall nào bị chặn, hãy bật logging chi tiết của Docker hoặc sử dụng công cụ `strace` bên trong container (nếu container cho phép).
Tốt hơn hết là kiểm tra log của systemd service quản lý container, vì nó sẽ ghi lại lỗi khởi động hoặc crash ngay lập tức.
journalctl -u db-container-service -f
Trong trường hợp container bị kill ngay khi start, hãy tạm thời gỡ bỏ Seccomp để xác định nguyên nhân, sau đó thêm syscall cụ thể vào profile.
Chỉnh sửa file profile Seccomp (ví dụ: /etc/seccomp/db-profile.json) để thêm syscall bị thiếu. Giả sử lỗi là do syscall `clone3` bị chặn.
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"syscalls": [
{
"names": [
"accept",
"accept4",
"bind",
"clone3",
"connect",
"epoll_create",
"epoll_create1",
"epoll_ctl",
"epoll_pwait",
"epoll_wait",
"exit",
"exit_group",
"futex",
"getsockopt",
"listen",
"mmap",
"mprotect",
"open",
"openat",
"read",
"recvfrom",
"sendto",
"setsockopt",
"shutdown",
"socket",
"stat",
"statfs",
"sysinfo",
"uname",
"write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Khởi động lại service để áp dụng profile mới.
systemctl restart db-container-service
Kiểm tra lại trạng thái service và log để đảm bảo không còn lỗi "System call not allowed".
systemctl status db-container-service
Nếu vẫn còn lỗi, hãy sử dụng `seccomp` tool để dump ra danh sách syscall mà ứng dụng đang sử dụng khi chạy bình thường (không có profile), sau đó lọc ra danh sách cần thiết.
seccomp -d -e clone3 -o /tmp/syscall-log.json
Xử lý lỗi "Permission denied" do SELinux hoặc AppArmor: Nếu Seccomp đã đúng nhưng vẫn bị chặn, kiểm tra context của file mount.
ls -Z /var/lib/docker/volumes/db-data/_data
Đảm bảo volume mount có label đúng để container có quyền truy cập, thường là `:Z` hoặc `:z` tùy vào cấu hình Docker daemon.
Phân tích log systemd và journalctl để tìm nguyên nhân crash
Khi database bị crash ngay sau khi khởi động, log của systemd là nguồn thông tin đầu tiên và quan trọng nhất để xác định nguyên nhân.
Sử dụng `journalctl` để xem log của service cụ thể, lọc theo thời gian gần nhất để tìm lỗi khởi động.
journalctl -u db-container-service --since "10 minutes ago" --no-pager
Để xem chi tiết hơn, bao gồm cả output stdout/stderr của container (nếu cấu hình `StandardOutput=journal` trong unit file).
journalctl -u db-container-service -o verbose
Phân tích các dòng log chứa "Failed", "Error", hoặc "Killed". Nếu thấy "OOMKilled", đây là dấu hiệu container bị thiếu RAM.
grep -i "oomkilled\|out of memory" /var/log/syslog
Kiểm tra giới hạn tài nguyên (cgroup) của container trong systemd unit file. Nếu container bị crash do vượt quá giới hạn, hãy tăng `MemoryLimit`.
[Unit]
Description=Hardened Database Container
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/docker start db-container
ExecStop=/usr/bin/docker stop db-container
LimitNOFILE=65536
MemoryLimit=4G
MemoryMax=4G
[Install]
WantedBy=multi-user.target
Khởi động lại service sau khi điều chỉnh giới hạn bộ nhớ.
systemctl daemon-reload && systemctl restart db-container-service
Để theo dõi liên tục và bắt lỗi ngay khi xảy ra, hãy chạy lệnh watch log.
watch -n 1 'journalctl -u db-container-service -n 10 --no-pager'
Kiểm tra lại kết quả: Log không còn xuất hiện các thông báo crash liên tục, và trạng thái service chuyển sang "active (running)".
Mẹo tối ưu hiệu năng khi sử dụng Seccomp profile khắt khe
Seccomp profile khắt khe có thể gây ra overhead nhỏ cho kernel khi lọc syscall, nhưng lợi ích bảo mật lớn hơn nhiều. Tuy nhiên, nếu cấu hình sai có thể gây giảm hiệu năng đáng kể.
Sử dụng `SCMP_ACT_TRAP` thay vì `SCMP_ACT_ERRNO` cho các syscall ít quan trọng nếu bạn cần debug, nhưng trong môi trường production, luôn dùng `SCMP_ACT_ERRNO` hoặc `SCMP_ACT_KILL` để tránh leak thông tin.
Giảm số lượng syscall trong whitelist. Chỉ cho phép các syscall thực sự cần thiết cho database (như `read`, `write`, `open`, `mmap`, `futex`). Loại bỏ các syscall không liên quan như `ptrace`, `process_vm_readv`.
{
"defaultAction": "SCMP_ACT_KILL",
"syscalls": [
{
"names": [
"accept", "accept4", "bind", "connect", "epoll_ctl", "epoll_pwait",
"epoll_wait", "exit", "exit_group", "futex", "getsockopt", "listen",
"mmap", "mprotect", "munmap", "openat", "read", "recvfrom", "sendto",
"setsockopt", "shutdown", "socket", "statfs", "write", "writev",
"clock_gettime", "clock_settime", "gettimeofday", "settimeofday"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Tối ưu hóa cache của Seccomp: Kernel có cache cho các profile Seccomp. Đảm bảo không thay đổi profile liên tục trong runtime để tránh phải reload cache.
Sử dụng Docker Bench Security để so sánh hiệu năng trước và sau khi áp dụng Seccomp. Chạy benchmark đơn giản như `sysbench` bên trong container.
docker exec db-container sysbench --test=threads --num-threads=4 --max-requests=100000 run
Chạy lại benchmark sau khi tối ưu Seccomp profile và so sánh số lượng request/giây (RPS).
docker exec db-container sysbench --test=threads --num-threads=4 --max-requests=100000 run
Giảm overhead của Systemd: Tắt các feature không cần thiết trong unit file như `StandardOutput=journal` nếu không cần log chi tiết, hoặc chuyển sang `StandardOutput=null` để giảm I/O disk.
[Service]
Type=oneshot
ExecStart=/usr/bin/docker start db-container
ExecStop=/usr/bin/docker stop db-container
StandardOutput=null
StandardError=journal
Kiểm tra lại kết quả: Thời gian phản hồi (latency) của database không tăng đáng kể so với khi không có Seccomp, và CPU usage ổn định.
docker stats db-container
Đ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 7: Tích hợp Seccomp và Capabilities vào Systemd Service