Một công cụ chuyển đổi ảnh chỉ cần đọc thư mục input, ghi thư mục output và nạp vài thư viện hệ thống. Nếu parser bị khai thác, tại sao tiến trình vẫn có thể đọc SSH key, quét toàn filesystem hoặc kết nối tới dịch vụ khác? Linux Landlock cho phép chính ứng dụng — kể cả tiến trình không đặc quyền — tự thu hẹp quyền truy cập của mình trước khi xử lý dữ liệu không tin cậy.
Bài này xây dựng mô hình triển khai Landlock thực tế: kiểm tra ABI lúc chạy, tạo ruleset deny-by-default cho các quyền được quản lý, allowlist cây thư mục, áp policy trước khi sinh thread, mở rộng sang port mạng và kiểm thử fail-closed. Nội dung được đối chiếu với tài liệu Landlock của Linux kernel, landlock(7) và ba system call create_ruleset, add_rule, restrict_self.
1. Landlock là gì?
Landlock là một Linux Security Module (LSM) có thể xếp chồng với các cơ chế kiểm soát khác. Mục tiêu của nó là cho phép tiến trình tự hạn chế ambient rights — những quyền mà tiến trình vốn có qua UID/GID, file mode và môi trường hiện tại. Landlock xuất hiện từ Linux 5.13 và tiếp tục được mở rộng qua các phiên bản ABI.
Ba system call tạo nên luồng chính:
landlock_create_ruleset(): khai báo nhóm loại truy cập mà sandbox sẽ quản lý.landlock_add_rule(): thêm object và quyền được phép, ví dụ một cây thư mục hoặc port.landlock_restrict_self(): áp ruleset lên thread gọi và các tiến trình con tương lai.
Policy chỉ có thể làm quyền hẹp hơn, không cấp thêm quyền vượt DAC, ACL, SELinux hay AppArmor. Sau khi áp, tiến trình không thể gỡ policy; ruleset xếp chồng sau đó chỉ có thể hạn chế thêm.
2. Landlock không thay thế những lớp nào?
| Cơ chế | Phạm vi chính | Quan hệ với Landlock |
|---|---|---|
| DAC/ACL | Quyền user/group trên file | Landlock chỉ thu hẹp kết quả đã được DAC/ACL cho phép. |
| SELinux/AppArmor | Mandatory policy toàn hệ thống | Tiếp tục áp dụng; quyết định cuối phải qua mọi lớp. |
| seccomp | Lọc system call và argument | Bổ sung nhau: seccomp lọc syscall, Landlock kiểm soát object. |
| namespace/container | Tách view tài nguyên | Landlock thêm lớp giới hạn bên trong namespace. |
| systemd sandbox | Hạn chế service qua unit settings | Có thể xếp chồng với policy tích hợp trong ứng dụng. |
| chroot | Đổi root path resolution | Không phải security boundary hoàn chỉnh; khác mô hình Landlock. |
Landlock đặc biệt hữu ích khi developer biết chính xác ứng dụng cần object nào và muốn policy đi cùng binary. Quản trị viên vẫn nên giữ hardening ở cấp service/container vì ứng dụng có thể chạy trên kernel không hỗ trợ hoặc policy tích hợp có lỗi.
3. Mô hình handled access và allowed access
Đây là phần dễ hiểu sai nhất. Khi tạo ruleset, ứng dụng khai báo các access right mà ruleset handled. Với các loại đó, hành vi mặc định là từ chối; sau đó từng rule cho phép một tập con trên object cụ thể.
Ví dụ ứng dụng quản lý các quyền đọc, ghi và execute:
handled_access_fs =
LANDLOCK_ACCESS_FS_EXECUTE |
LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_READ_DIR |
LANDLOCK_ACCESS_FS_WRITE_FILE |
LANDLOCK_ACCESS_FS_TRUNCATE;
Sau đó rule cho /usr có thể chỉ allow read/execute, rule cho /srv/converter/output allow read/write/truncate. Một access right không nằm trong handled_access_fs không tự nhiên bị Landlock chặn. Vì vậy khai báo thiếu không phải deny-by-default toàn filesystem; nó chỉ bỏ loại hành động đó ra khỏi contract của ruleset.
Landlock dùng allowlist cho các quyền đã chọn quản lý. “Không thêm rule deny” không có nghĩa là cho phép; object không có rule phù hợp sẽ bị từ chối với các handled access.
4. Kiểm tra kernel và ABI lúc chạy
Đừng suy luận từ tên distribution hoặc version kernel do vendor có thể backport hay tắt LSM. Quản trị viên có thể xem log boot:
sudo dmesg | grep -i landlock
# hoặc
sudo journalctl -kb -g landlock
Ứng dụng nên hỏi ABI bằng system call:
int abi = syscall(
SYS_landlock_create_ruleset,
NULL,
0,
LANDLOCK_CREATE_RULESET_VERSION
);
Kết quả không âm là version ABI khả dụng. Các lỗi cần phân biệt:
ENOSYS: kernel không có system call.EOPNOTSUPP: kernel có Landlock nhưng bị tắt lúc boot.EPERMở bước enforce: thường liên quanno_new_privs/capability hoặc trạng thái caller.EINVAL: flag/access không được ABI hỗ trợ hoặc cấu trúc không hợp lệ.
Policy production phải xác định rõ hai chế độ:
- Best effort: dùng tập tính năng mà ABI hiện tại hỗ trợ và ghi metric cảnh báo khi giảm bảo vệ.
- Fail closed: từ chối khởi động nếu thiếu ABI/quyền tối thiểu bắt buộc.
Không silently continue nếu tài liệu bảo mật tuyên bố Landlock là ranh giới bắt buộc.
5. ABI là contract tương thích, không phải số phiên bản để hardcode
Mỗi ABI bổ sung khả năng. Một số mốc quan trọng trong tài liệu kernel hiện hành:
| ABI | Khả năng tiêu biểu | Ý nghĩa |
|---|---|---|
| 1 | Filesystem access cơ bản | Nền tảng read/write/execute và tạo/xóa object. |
| 2 | REFER | Kiểm soát rename/link giữa hierarchy. |
| 3 | TRUNCATE | Chặn truncate file rõ ràng. |
| 4 | TCP bind/connect theo port | Bắt đầu kiểm soát network. |
| 5 | Device ioctl | Hạn chế ioctl trên character/block device. |
| 6 | Scope signal và abstract UNIX socket | Giới hạn IPC theo domain. |
| 8 | Thread synchronization | Áp domain đồng bộ lên các thread. |
| 9 | Pathname UNIX socket | Kiểm soát resolve/connect socket theo path. |
| 10 | UDP và quiet rules | Kiểm soát port UDP, tinh chỉnh log deny. |
| 11 | Flag thiết lập no_new_privs | Enforce ruleset và NNP theo contract mới. |
Ứng dụng nên bắt đầu với mask quyền mong muốn, rồi loại các bit không được ABI hỗ trợ trước khi tạo ruleset. Không gửi flag tương lai vào kernel cũ vì lời gọi sẽ thất bại; cũng không tự động thêm quyền mới sau khi kernel nâng cấp vì điều đó có thể làm ứng dụng bị chặn bất ngờ.
6. Xây ruleset filesystem theo nhu cầu thật
Một image converter có thể cần:
- read/execute runtime dưới
/usrvà thư viện dưới/lib*; - read input ở
/srv/converter/input; - read/write/create/truncate ở
/srv/converter/output; - read một số file cấu hình/CA ở
/etc; - không truy cập
/home, SSH key, backup hoặc socket quản trị.
Luồng thêm một path rule ở mức C:
int parent_fd = open(path, O_PATH | O_CLOEXEC);
struct landlock_path_beneath_attr rule = {
.parent_fd = parent_fd,
.allowed_access = allowed_access,
};
syscall(
SYS_landlock_add_rule,
ruleset_fd,
LANDLOCK_RULE_PATH_BENEATH,
&rule,
0
);
close(parent_fd);
Mở path bằng O_PATH trước khi enforce để lấy file descriptor đại diện hierarchy. Chuẩn hóa cấu hình, từ chối symlink/path ngoài dự kiến và log rule đã áp. Đừng nhận một danh sách path tùy ý từ input không tin cậy rồi biến nó thành allowlist.
Quyền cho thư mục output cần đủ cho workflow thực tế: ứng dụng có thể tạo file tạm, rename atomic, chmod hoặc xóa file cũ. Test bằng syscall thực, không chỉ test “ghi một file mới”.
7. Áp policy đúng thời điểm
Trình tự an toàn thường là:
- Parse và validate cấu hình tin cậy.
- Mở resource tối thiểu cần để xây ruleset.
- Đóng file descriptor, socket và TTY không cần thiết.
- Tạo ruleset và thêm mọi rule.
- Thiết lập
no_new_privshoặc dùng cơ chế ABI phù hợp. - Enforce Landlock.
- Chỉ sau đó mới parse file/upload/request không tin cậy và sinh worker.
Với ABI truyền thống, tiến trình không có CAP_SYS_ADMIN cần:
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
syscall(SYS_landlock_restrict_self, ruleset_fd, 0);
no_new_privs ngăn execve giành thêm privilege qua setuid/file capability. ABI mới có flag giúp gắn bước này vào enforce, nhưng code vẫn phải query ABI và chọn đường tương thích.
Policy áp lên calling thread và con tương lai. Ứng dụng đa luồng nên sandbox trước khi tạo thread. Tính năng TSYNC ở ABI mới có thể đồng bộ domain lên mọi thread, nhưng phải kiểm tra support và xử lý failure như một lỗi security, không bỏ qua.
8. File descriptor mở sẵn và TOCTOU
Sandbox muộn là một lỗi thiết kế phổ biến. File descriptor/socket đã mở có thể tiếp tục mang quyền truy cập hữu ích sau khi policy áp, tùy operation. Vì vậy hãy coi descriptor là capability:
- liệt kê và đóng descriptor không cần trước sandbox;
- dùng
O_CLOEXECmặc định; - không thừa kế stdin/stdout/stderr trỏ tới TTY hoặc file nhạy cảm nếu worker không cần;
- tránh mở toàn bộ filesystem rồi mới “khóa cửa”;
- không dựa vào path string đã kiểm tra nếu sau đó attacker có thể đổi symlink/hierarchy.
Landlock dựa trên object/hierarchy và xử lý nhiều vấn đề path tốt hơn một bộ lọc string tự viết, nhưng ứng dụng vẫn cần mở file an toàn, kiểm soát symlink và thiết kế ownership directory hợp lý.
9. Network rules kiểm soát port, không thay firewall
Từ ABI 4, Landlock có thể hạn chế TCP bind/connect theo port. ABI mới hơn bổ sung UDP bind và connect/send. Ví dụ policy worker chỉ được kết nối TCP tới port 443:
struct landlock_net_port_attr net_rule = {
.port = 443,
.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
};
syscall(
SYS_landlock_add_rule,
ruleset_fd,
LANDLOCK_RULE_NET_PORT,
&net_rule,
0
);
Đây không phải allowlist IP/domain. Nếu cho connect port 443, tiến trình có thể kết nối nhiều địa chỉ dùng port đó, tùy các lớp mạng khác. Dùng firewall, network namespace, proxy egress hoặc policy của orchestrator để giới hạn destination; Landlock thêm một lớp theo hành động/port.
DNS cũng phải được tính đến: resolver có thể cần UDP/TCP 53 hoặc UNIX socket của local resolver. Đừng mở rộng policy mù khi thấy lỗi; trace syscall và xác định dependency thật.
10. Scope signal và UNIX socket
Landlock ABI mới có thể scope signal và abstract UNIX socket theo domain; pathname UNIX socket có quyền resolve riêng. Các cơ chế này hữu ích để worker bị xâm nhập không signal hoặc kết nối tới process/socket ngoài sandbox.
Scope không hoạt động như path/port allowlist có exception tùy ý. Khi thiết kế IPC, xác định process nào cùng domain, thời điểm fork/exec và socket nào cần dùng. Một kiến trúc supervisor mở resource đặc quyền rồi truyền descriptor có kiểm soát cho worker thường dễ audit hơn việc cho worker tự khám phá mọi socket.
11. Logging, audit và chẩn đoán EACCES
Khi ứng dụng bắt đầu fail với EACCES, đừng nới toàn bộ /. Ghi lại ABI, handled masks, từng rule, binary version và phase enforce. Kernel mới có audit/tracepoint Landlock và các flag kiểm soát logging; khả năng cụ thể phụ thuộc ABI/kernel.
Quy trình debug:
- Tái hiện bằng input vô hại trong staging.
- Dùng
straceđể xác định syscall, path/port và errno. - Đọc audit/trace event nếu kernel hỗ trợ.
- Xác định access right thiếu, không chỉ path thiếu.
- Thêm rule hẹp nhất và tạo regression test.
Logging deny có thể bị spam bởi process lỗi hoặc bị khai thác. Rate limit và retention phải được thiết kế, nhưng không tắt toàn bộ telemetry trước khi có cách quan sát thay thế.
12. Những giới hạn phải đưa vào threat model
- Kernel/ABI cũ không kiểm soát một số action mới như truncate, network, ioctl hoặc UDP.
- Filesystem đặc biệt và object không biểu diễn như file/path thông thường có giới hạn riêng.
- Landlock không giới hạn CPU, RAM, số process hay disk quota; dùng cgroup/resource limits.
- Network rule theo port không phải destination firewall.
- Landlock không sửa lỗ hổng kernel và không bảo vệ khỏi mọi side channel.
- Policy tích hợp sai có thể fail open hoặc phá chức năng ứng dụng.
- Descriptor/resource được cấp trước sandbox cần được audit riêng.
Đọc mục “Current limitations”, ABI versions và errata trong tài liệu kernel cho đúng phiên bản production; không sao chép một policy cũ rồi mặc định mọi access đã được quản lý.
13. Kiểm thử policy như một API contract
Bộ test cần cả positive và negative cases:
| Test | Kỳ vọng |
|---|---|
| Đọc input hợp lệ | Thành công. |
| Ghi file tạm và rename vào output | Thành công nếu workflow yêu cầu. |
Đọc ~/.ssh/id_ed25519 | EACCES. |
| Ghi ra ngoài output | EACCES. |
| Execute binary ngoài runtime allowlist | Bị từ chối. |
| Connect port không được phép | Bị từ chối khi ABI network được yêu cầu. |
| Kernel thiếu ABI tối thiểu | Fail closed hoặc degrade có metric đúng policy. |
| Worker/con sinh sau enforce | Kế thừa hạn chế. |
Chạy test trên kernel thấp nhất và cao nhất được hỗ trợ. CI container có thể không phản ánh LSM/boot config của host, nên cần runner/kernel integration thật cho security test.
14. Checklist production
- Viết threat model và danh sách filesystem/network access tối thiểu.
- Query ABI runtime, không suy luận từ distro version.
- Chọn rõ best-effort hay fail-closed và phát metric khi giảm bảo vệ.
- Chỉ khai báo access bit được ABI hỗ trợ.
- Dùng allowlist hẹp cho runtime, input, output, config và port.
- Đóng descriptor không cần và enforce trước input không tin cậy/thread.
- Xếp chồng Landlock với DAC, seccomp, systemd/container và firewall.
- Test cả allow và deny trên kernel production.
- Quan sát audit/trace/EACCES và giữ rollback policy có kiểm soát.
- Theo dõi ABI mới và errata thay vì tự động bật tính năng mới.
Kết luận
Landlock đưa nguyên tắc least privilege vào bên trong ứng dụng: process không root có thể tự tuyên bố “từ thời điểm này, tôi chỉ cần những cây thư mục và port này”. Giá trị lớn nhất không nằm ở một syscall riêng lẻ mà ở trình tự thiết kế: query ABI, quản lý đúng access bits, allowlist object, đóng capability cũ và enforce trước dữ liệu không tin cậy. Khi được xếp chồng với các lớp hardening hệ thống, Landlock giúp biến một lỗi parser hoặc plugin thành sự cố bị giới hạn thay vì quyền truy cập lan ra toàn server.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.