Cơ chế hoạt động của Seccomp trong Linux Kernel
Seccomp (Secure Computing Mode) là một cơ chế bảo mật tích hợp sẵn trong nhân Linux, cho phép hạn chế các hệ thống gọi (system calls) mà một tiến trình được phép thực thi.
Mục tiêu chính là giảm bề mặt tấn công (attack surface) bằng cách ngăn chặn các system call không cần thiết hoặc nguy hiểm, ngay cả khi ứng dụng bị khai thác lỗ hổng.
Seccomp hoạt động bằng cách thiết lập một bộ lọc (BPF - Berkeley Packet Filter) trên mỗi luồng (thread). Khi luồng này thực hiện một system call, kernel sẽ kiểm tra với bộ lọc trước khi chuyển quyền điều khiển sang kernel.
Có hai chế độ chính của Seccomp:
- Seccomp mode strict (Legacy): Chỉ cho phép 4 system calls: read, write, exit, sigreturn. Mọi call khác đều bị kill ngay lập tức. Chế độ này quá hạn chế cho Database.
- Seccomp mode filter (Modern): Sử dụng BPF để định nghĩa chính xác những system call nào được phép, bị chặn, hoặc bị chuyển sang xử lý khác (trap).
Để kiểm tra trạng thái Seccomp hiện tại của một tiến trình, ta đọc file trạng thái trong procfs.
Thực hiện lệnh sau để xem trạng thái Seccomp của tiến trình hiện tại:
cat /proc/self/status | grep Seccomp
Kết quả mong đợi: Dòng Seccomp: 0 (Bật tắt), 1 (Strict mode), hoặc 2 (Filter mode). Nếu đang chạy trong container Docker mặc định, thường là 0 hoặc 2 tùy cấu hình.
Phân tích System Call của Database bằng strace
Để xây dựng chính sách Seccomp an toàn, ta cần biết chính xác Database (ví dụ: PostgreSQL hoặc MySQL) sử dụng những system call nào để hoạt động.
Công cụ strace (system call tracer) sẽ theo dõi và ghi lại mọi tương tác giữa ứng dụng và kernel. Đây là bước bắt buộc để liệt kê white-list.
Giả sử chúng ta đang phân tích PostgreSQL. Chúng ta sẽ chạy một instance test và dùng strace để theo dõi trong một khoảng thời gian ngắn.
Thực hiện lệnh sau để bắt đầu trace các system call của tiến trình postgres:
strace -f -o /tmp/pg_calls.log -e trace=all /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main
Giải thích các tham số:
- -f: Theo dõi cả các tiến trình con (fork) được tạo ra.
- -o file.log: Xuất kết quả ra file thay vì stdout để dễ phân tích.
- -e trace=all: Theo dõi tất cả các system call (có thể lọc cụ thể sau).
- -c: (Tùy chọn) Thống kê tần suất các call nếu muốn xem nhanh.
Đợi khoảng 10-15 giây để Database khởi động và xử lý một vài query đơn giản (hoặc dùng psql -c "SELECT 1" từ terminal khác), sau đó nhấn Ctrl+C để dừng strace.
Kết quả mong đợi: File /tmp/pg_calls.log được tạo ra, chứa hàng nghìn dòng log chi tiết về các system call như openat, read, write, socket, connect, mmap...
Để trích xuất danh sách các system call độc nhất (unique) từ log, dùng lệnh:
grep -oP '^\w+\(' /tmp/pg_calls.log | sed 's/($//' | sort -u > /tmp/pg_syscalls.txt
Kết quả mong đợi: File /tmp/pg_syscalls.txt chứa danh sách sạch các tên system call mà Database đã sử dụng. Đây là cơ sở để tạo Seccomp profile.
Phân biệt các mức độ xử lý của Seccomp: Allow, Deny, Trap
Khi xây dựng profile Seccomp, mỗi system call sẽ được gán một hành động (action) cụ thể. Hiểu rõ sự khác biệt giữa các hành động này là chìa khóa để cân bằng giữa bảo mật và tính năng.
Có 3 hành động chính trong bộ lọc Seccomp:
- SECCOMP_RET_ALLOW (0x7fff0000): Cho phép system call thực thi bình thường. Đây là hành động mặc định cho các call trong danh sách trắng (whitelist).
- SECCOMP_RET_ERRNO (0x00000000): Chặn call và trả về mã lỗi (thường là
EPERM hoặc EACCES) cho ứng dụng. Ứng dụng sẽ nhận lỗi nhưng không bị crash. Dùng cho các call nguy hiểm nhưng ứng dụng có thể xử lý lỗi.
- SECCOMP_RET_KILL (0x00000000) hoặc SECCOMP_RET_KILL_THREAD (0x00000000): Chặn call và giết chết ngay lập tức tiến trình hoặc luồng. Dùng cho các call cực kỳ nguy hiểm hoặc khi phát hiện hành vi đáng ngờ.
Trong bối cảnh Database, chúng ta thường sử dụng:
- Allow: Cho các call thiết yếu như I/O, Memory, Network, Process management.
- Deny (Errno): Cho các call ít dùng hoặc có thể gây rủi ro nếu bị lạm dụng (ví dụ:
ptrace, mount).
- Kill: Cho các call rõ ràng là độc hại trong môi trường container (ví dụ:
reboot, swapon).
Để kiểm tra hành động của một call cụ thể, ta có thể dùng seccomp utility hoặc kiểm tra log của kernel (dmesg) nếu call bị chặn với hành động Kill/Errno.
Thực hiện lệnh sau để xem log kernel khi một call bị chặn (cần root):
dmesg | grep -i "seccomp"
Kết quả mong đợi: Nếu có call bị chặn với hành động Kill hoặc Errno, bạn sẽ thấy thông báo trong log kernel ghi rõ "seccomp: killed process" hoặc "seccomp: denied syscall".
Xác định danh sách System Call cần thiết cho Database
Dựa trên kết quả phân tích từ strace ở phần trước, chúng ta cần tổng hợp lại danh sách các system call thực sự cần thiết để Database hoạt động ổn định.
Đối với hầu hết các Database quan hệ (RDBMS) chạy trên Linux, danh sách này thường bao gồm các nhóm sau:
- I/O File:
openat, close, read, write, lseek, stat, fstat, access.
- Memory Management:
brk, mmap, mprotect, munlock, munmap.
- Process & Thread:
clone, execve, exit, exit_group, wait4, getpid, gettid.
- Network:
socket, connect, accept, bind, listen, sendto, recvfrom, shutdown.
- Signal:
rt_sigaction, rt_sigprocmask, rt_sigreturn.
- System:
arch_prctl, prlimit64, getrandom.
Chúng ta sẽ tạo một file danh sách trắng (whitelist) để dùng cho bước tiếp theo (tạo Seccomp profile). File này chứa tên các call được phép (Allow).
Tạo file /etc/seccomp/db_allowlist.txt với nội dung sau (ví dụ cho PostgreSQL, các call khác có thể thêm tùy phân tích strace thực tế):
cat > /etc/seccomp/db_allowlist.txt
Kết quả mong đợi: File /etc/seccomp/db_allowlist.txt được tạo thành công, chứa danh sách các system call an toàn cần thiết cho Database.
Để verify, kiểm tra số lượng dòng trong file:
wc -l /etc/seccomp/db_allowlist.txt
Kết quả mong đợi: Số lượng dòng lớn hơn 0 (khoảng 60-80 dòng tùy hệ thống), xác nhận danh sách đã được tạo.
Đ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 2: Cài đặt và cấu hình Database trong môi trường Docker
Phần 4: Tạo profile Seccomp tùy chỉnh cho Docker Container »