Vấn đề thực tế: Khi quyền hạn tiêu chuẩn bất lực trước một cuộc tấn công RCE
Năm ngoái, một microservice FastAPI nội bộ chạy phía sau Nginx gateway của chúng tôi đã bị tấn công trực diện bởi một lỗ hổng zero-day path traversal. Chỉ trong vòng 12 phút, lỗ hổng này đã leo thang thành thực thi mã từ xa (RCE). Chúng tôi đã cho tiến trình chạy dưới một người dùng không có đặc quyền riêng biệt tên là svc-worker, tuân thủ đúng các checklist hardening tiêu chuẩn. Về lý thuyết, điều đó đáng lẽ phải cô lập được phạm vi ảnh hưởng (blast radius).
Nhưng thực tế lại không như vậy.
Kẻ tấn công đã mở một reverse shell kết nối về một VPS qua cổng 4444. Chúng đọc được hơn 40 tệp cấu hình có quyền đọc mở (world-readable) trong /etc, thả một script thu thập thông tin vào /tmp, tải công cụ về bằng curl và bắt đầu dò quét các hướng tấn công vào kernel. Phân quyền tệp truyền thống đơn giản là không thể ngăn chặn một kẻ tấn công đang hoạt động bên trong một phiên người dùng hợp lệ.
Một khi kẻ tấn công chiếm quyền điều khiển daemon, Linux sẽ coi mọi hành động từ PID đó là hợp lệ. Điều này khiến toàn bộ filesystem của bạn dễ dàng bị khai thác theo chiều ngang (lateral movement).
Phân tích nguyên nhân gốc rễ: Vì sao DAC sụp đổ trước các cuộc tấn công
Bảo mật Linux tiêu chuẩn dựa trên Kiểm soát truy cập tùy ý (Discretionary Access Control – DAC), gắn quyền hạn với User ID (UID) và Group ID (GID). Khi bạn chạy một worker dưới danh nghĩa www-data (UID 33), kernel sẽ cấp cho nó toàn bộ đặc quyền mà UID 33 sở hữu trên toàn bộ ổ đĩa và network socket.
Mô hình này dẫn đến hai lỗ hổng kiến trúc nghiêm trọng:
- Thẩm quyền bao trùm (Ambient authority): Một dịch vụ web chỉ cần truy cập vào
/var/www/htmlvà một đường dẫn log duy nhất. Thế nhưng DAC lại trao cho nó quyền đọc/etc/passwd, quyền thực thi hơn 1.500 file nhị phân trong/usr/bin, cùng quyền tạo raw TCP socket ra bên ngoài mà không bị giới hạn. - Thừa kế nhị phân (Binary inheritance): Khi ứng dụng web của bạn gọi shell ra
/bin/sh, tiến trình con mới sẽ kế thừa toàn bộ quyền hạn rộng rãi của tiến trình cha. Mọi rào chắn bảo vệ ở cấp ứng dụng lập tức bốc hơi hoàn toàn.
Để cô lập phần mềm đã bị xâm nhập, chúng ta cần Kiểm soát truy cập bắt buộc (Mandatory Access Control – MAC). MAC giới hạn chính xác những gì một đường dẫn file nhị phân cụ thể có thể làm, bất kể tài khoản người dùng nào khởi chạy nó.
So sánh các giải pháp cô lập tiến trình
Trước khi khóa chặt các dịch vụ của mình, hãy đánh giá ba tầng bảo vệ và cô lập tiến trình chính:
1. Hardening DAC tiêu chuẩn (Chroot & Systemd Sandboxing)
Các chỉ thị như ProtectSystem=strict và PrivateTmp=true tạo nên lớp phòng thủ ban đầu rất tốt. Tuy nhiên, chúng thiếu khả năng lọc đường dẫn động và kiểm soát mạng chi tiết ở cấp độ kernel. Việc thoát khỏi chroot jail (jailbreak) vẫn rất dễ dàng nếu tiến trình giữ quyền CAP_SYS_CHROOT hoặc các capability của root.
2. SELinux (Kiểm soát kiểu truy cập dựa trên nhãn – Type Enforcement)
SELinux vượt trội trên các hệ thống RHEL, Rocky và Fedora. Nó gắn các quyết định chính sách vào thuộc tính mở rộng (extended attributes) của filesystem. Dù đã qua thử lửa thực tế, việc viết các policy SELinux tùy chỉnh từ đầu lại đòi hỏi trình biên dịch chuyên dụng và quá trình gỡ lỗi context rất phức tạp. Nhiều đội ngũ kỹ sư nản lòng thường chuyển thẳng sang chế độ permissive khi production gặp sự cố.
3. AppArmor (MAC dựa trên đường dẫn)
AppArmor gắn ranh giới bảo mật trực tiếp vào các đường dẫn trên filesystem như /opt/myapp/bin/server. Cú pháp hoàn toàn bằng văn bản thuần túy (plain text), được bật mặc định trên Ubuntu và Debian, cùng các công cụ profiling có khả năng học hành vi hoạt động theo thời gian thực. Đối với các đội ngũ kỹ thuật cần tốc độ, đây là lựa chọn cân bằng hoàn hảo giữa bảo mật và khả năng bảo trì.
Giải pháp tối ưu: Xây dựng Profile AppArmor tùy chỉnh
AppArmor tạo ra một vòng đai cô lập xung quanh các file nhị phân. Ngay cả khi kẻ tấn công thực thi shellcode trực tiếp trong bộ nhớ ứng dụng, kernel vẫn chặn đứng mọi hành vi đọc file trái phép, bind socket hay gọi subshell chưa được khai báo ngay tại ranh giới system call.
Bước 1: Cài đặt tiện ích và kiểm tra hỗ trợ từ Kernel
Trước tiên, hãy kiểm tra trạng thái kernel của bạn:
sudo aa-status
Nếu chưa kích hoạt hoặc thiếu công cụ, hãy cài đặt bộ tiện ích userspace trên Debian hoặc Ubuntu:
sudo apt update
sudo apt install apparmor apparmor-utils auditd -y
sudo systemctl enable --now apparmor
Bước 2: Tạo Profile cơ sở với aa-genprof
Giả sử chúng ta có một dịch vụ Python nội bộ chạy tại /opt/api-service/server.py. Chúng ta bắt đầu bằng cách khởi chạy công cụ profiling của AppArmor:
sudo aa-genprof /opt/api-service/server.py
Hãy giữ nguyên terminal đó. Ở một terminal thứ hai, hãy chạy toàn bộ test suite end-to-end hoặc giả lập tải thông thường bằng curl và Apache Bench. Kích hoạt health check, xoay vòng log, gọi query vào database pool và tạo ra cả các lỗi ứng dụng. AppArmor sẽ can thiệp vào các audit hook của kernel và ghi lại từng system call.
Bước 3: Cấu trúc và tinh chỉnh Profile thủ công
AppArmor lưu trữ các profile trong /etc/apparmor.d/, chuyển đổi dấu gạch chéo của đường dẫn thành dấu chấm. Hãy chỉnh sửa trực tiếp /etc/apparmor.d/opt.api-service.server.py để kiểm tra các rule.
Dưới đây là cấu hình profile đã được làm cứng (hardened):
#include <tunables/global>
/opt/api-service/server.py {
# Các abstraction cơ sở tiêu chuẩn
#include <abstractions/base>
#include <abstractions/python>
#include <abstractions/nameservice>
# Các tệp mã nguồn ứng dụng
/opt/api-service/server.py r,
/opt/api-service/** r,
# Các tệp log chỉ cho phép ghi nối tiếp (append-only)
/var/log/api-service/output.log w,
/var/log/api-service/*.log a,
# Quyền chỉ đọc với tệp cấu hình
/etc/api-service/config.yaml r,
# Giới hạn các tệp tạm thời
/tmp/api-service-* rw,
# Chặn shell, trình biên dịch và thông tin xác thực nhạy cảm
deny /bin/** x,
deny /usr/bin/** x,
deny /etc/shadow* rwklx,
deny /etc/ssh/** rwklx,
deny /root/** rwklx,
# Truy cập mạng: chỉ cho phép stream socket IPv4 và IPv6
network inet stream,
network inet6 stream,
}
Các cờ phân quyền quan trọng bạn cần nắm rõ:
r: Đọc dữ liệu từ đĩaw: Ghi hoặc truncate tệp hiện cóa: Chỉ ghi nối tiếp (append-only – ngăn kẻ tấn công xóa trắng log)k: Khóa tệp (file lock)x: Thực thi file nhị phândeny: Chặn tuyệt đối và có độ ưu tiên cao hơn các rule include rộng hơn
Bước 4: Sử dụng Chế độ Complain trước khi thực thi nghiêm ngặt (Enforce)
Đừng bao giờ áp dụng trực tiếp một profile mới sang chế độ enforce trên môi trường production. Hãy chuyển sang chế độ complain trước:
sudo aa-complain /etc/apparmor.d/opt.api-service.server.py
Trong chế độ complain, AppArmor chỉ ghi log lại các hành vi không khớp policy mà không chặn lời gọi lệnh. Hãy để nó theo dõi lưu lượng production thực tế từ 48 đến 72 giờ, sau đó kiểm tra log:
sudo grep "apparmor=\"ALLOWED\"" /var/log/audit/audit.log
# Hoặc trên các hệ thống sử dụng systemd-journald:
sudo journalctl -fx | grep -i apparmor
Khi các sự kiện bình thường của ứng dụng xuất hiện trong luồng audit, hãy chạy aa-logprof để đánh giá và tự động import các ngoại lệ đó vào cấu hình:
sudo aa-logprof
Bước 5: Kích hoạt Sandbox và kiểm tra khả năng giảm thiểu Zero-Day
Khi log audit không còn phát sinh cảnh báo bất thường trong các hoạt động hàng ngày, hãy khóa chặt profile:
sudo aa-enforce /etc/apparmor.d/opt.api-service.server.py
Bây giờ, hãy thử nghiệm khả năng cô lập bằng một kịch bản mô phỏng tấn công. Nếu kẻ tấn công kích hoạt payload cố gắng gọi /bin/bash, ghi vào /etc/crontab hoặc chạy curl, kernel sẽ lập tức chặn đứng quá trình thực thi kèm theo mã lỗi Permission denied (EACCES):
type=AVC msg=audit(1710928421.412:942): apparmor="DENIED" operation="exec" profile="/opt/api-service/server.py" name="/bin/bash" pid=4128 comm="python3" requested_mask="x" denied_mask="x"
Kinh nghiệm thực chiến khi vận hành trên Production
Viết profile mới chỉ là bước khởi đầu. Quản lý chúng ổn định trên hàng trăm node đòi hỏi quy trình vận hành cẩn trọng:
- Bảo vệ thông tin xác thực dịch vụ: Chính sách chặt chẽ sẽ ngăn chặn tiến trình bị chiếm quyền quét cây thư mục để tìm token nhạy cảm. Hãy kết hợp chính sách sandbox với các secret có độ ngẫu nhiên cao (high-entropy) dành riêng cho cơ sở dữ liệu nội bộ và API, sinh chuỗi tối thiểu 32 ký tự bằng các tiện ích tạo secret phía client trước khi commit thông số triển khai.
- Quản lý profile bằng hệ thống kiểm soát phiên bản (VCS): Theo dõi toàn bộ các tệp trong
/etc/apparmor.d/cùng với Ansible playbook hoặc Dockerfile của bạn. Khi cần khôi phục node sau sự cố, việc reload profile chỉ mất chưa đầy 5 giây. - Profile theo target thực thi, không profile runtime: Khi làm việc với Python, Ruby hoặc Node.js, hãy gắn profile vào script đích hoặc wrapper (chẳng hạn như
/opt/api-service/run.shhoặc/opt/api-service/server.py). Nếu bạn áp policy trực tiếp lên/usr/bin/python3, bạn sẽ vô tình bóp nghẹt các script bảo trì trên toàn bộ hệ điều hành. - Tích hợp kiểm thử hồi quy profile vào CI/CD: Khởi chạy test container với AppArmor ở chế độ enforce trong các lần chạy CI. Nếu một pull request thêm vào một thư mục cache hoặc driver database chưa được khai báo trong profile, pipeline sẽ fail ngay trước khi code kịp lên staging.

