Namespaces, cgroups và Linux security boundary
Container cô lập process view và kiểm soát resource usage bằng các primitive của Linux kernel. Điểm quan trọng nhất: container không có guest kernel riêng như VM — các container trên cùng host vẫn chia sẻ host kernel.
1. Isolation: boundary thật nằm ở đâu?
Khi Docker chạy một container, runtime tạo namespace và cgroup cho các process của container, sau đó áp dụng các giới hạn quyền như Linux capabilities, seccomp và LSM policy nếu host hỗ trợ. Vì kernel vẫn được chia sẻ, một kernel vulnerability hoặc cấu hình đặc quyền quá rộng có thể làm blast radius vượt qua boundary của container.
Do đó không nên mô tả container như “một VM nhẹ”. Container là một nhóm process được cô lập và giới hạn bằng kernel primitives. Mức an toàn thực tế phụ thuộc vào kernel, runtime, daemon, mount/device exposure, network policy và cấu hình của workload.
2. Namespaces
Các namespace thường gặp gồm PID, mount, network, IPC, UTS, user và cgroup. Chúng tạo ra các “view” riêng: process list riêng, mount tree riêng, network stack riêng, hostname riêng hoặc mapping UID/GID riêng.
| Namespace | Cô lập điều gì? | Ví dụ failure / escape surface |
|---|---|---|
| PID | Process IDs và process visibility. | Dùng host PID namespace làm mất phần lớn process isolation. |
| Mount | Mount points và filesystem view. | Bind-mount host path nhạy cảm có thể làm dữ liệu host lộ trực tiếp vào container. |
| Network | Interfaces, routes, ports, network stack. | Host networking làm container dùng trực tiếp network namespace của host. |
| IPC | System V IPC / POSIX message queues, shared IPC objects. | Chia sẻ IPC namespace làm tăng coupling và blast radius giữa workloads. |
| UTS | Hostname/domain name. | Chủ yếu là isolation về identity/view, không phải authorization. |
| User | UID/GID mapping giữa container và host. | Container UID 0 có thể map sang UID ít quyền hơn trên host; nhưng mapping không thay thế hardening khác. |
| cgroup | View về cgroup hierarchy. | Giúp che/giới hạn view quản lý resource, không tự tạo authorization boundary đầy đủ. |
User namespace đặc biệt hữu ích khi workload cần chạy UID 0 bên trong container: có thể remap UID đó sang một UID không đặc quyền trên host. Dù vậy, nguyên tắc ưu tiên vẫn là chạy application bằng user không đặc quyền khi có thể.
3. Cgroups: giới hạn và accounting tài nguyên
Cgroups dùng để account và phân phối CPU, memory, PIDs và I/O. Nếu không đặt giới hạn, container có thể dùng tài nguyên theo mức host scheduler cho phép. Đây là lý do resource isolation cũng là một phần của availability/security: một container misbehave có thể gây denial-of-service cho workload khác.
3.1 CPU
--cpus / CPU quota đặt ceiling; CPU weight/shares điều chỉnh mức ưu tiên tương đối khi có contention. Khi quota bị dùng hết trong một period, workload sẽ bị throttled. Kết quả thường là p95/p99 latency tăng dù host vẫn chưa trông giống “100% CPU” ở dashboard tổng.
Với cgroup v2, cpu.stat cung cấp các counter như nr_throttled và throttled_usec. Khi latency tăng, cần nhìn các counter này cùng application latency, run queue và CPU saturation thay vì chỉ nhìn host CPU average.
3.2 Memory
Memory limit không chỉ có Java heap. Footprint của process/container còn có native allocations, thread stacks, mmap, kernel-accounted memory và file/page cache tùy workload và kernel accounting. Nếu vượt giới hạn hoặc host rơi vào memory pressure, kernel có thể OOM-kill process; với container một-process, process đó thường chính là PID 1 và container sẽ exit/restart.
Với cgroup v2, memory.current, memory.stat và memory.events giúp phân biệt footprint, pressure và OOM-related events. Không nên “fix” bằng cách vô hiệu OOM killer mà không hiểu failure mode; điều đó có thể chuyển rủi ro từ một container sang cả host.
3.3 PIDs và I/O
--pids-limit bảo vệ host khỏi process/thread explosion như fork bomb. Trong cgroup v2, pids.current và pids.events cho biết mức sử dụng và số lần đụng giới hạn. I/O controller có thể dùng để điều phối bandwidth/weight; các signal như io.stat hỗ trợ chẩn đoán khi disk contention làm latency kéo dài.
| Failure window | Triệu chứng | Signal nên xem | Recovery / rollback |
|---|---|---|---|
| CPU quota quá thấp | Latency tăng, timeout, throughput tụt. | cpu.stat, app latency, request queue. | Tăng quota/replica tạm thời; sau đó right-size bằng load test production-like. |
| Memory limit quá thấp | OOM kill, restart loop, cache churn. | memory.current, memory.events, container exit reason. | Rollback limit hoặc release; giảm heap/native footprint; giữ headroom. |
| PIDs limit quá thấp | Không tạo được process/thread mới. | pids.current, pids.events, application errors. | Tăng limit có kiểm soát; tìm leak/fork storm trước khi bỏ limit. |
| I/O contention | Request stall, fsync/read/write chậm. | io.stat, device latency/utilization. | Giảm concurrency/ingress; tách noisy neighbor hoặc điều chỉnh I/O policy. |
4. Capabilities, seccomp và LSM
Linux capabilities tách quyền của root thành các quyền nhỏ hơn. Docker mặc định chạy container với một tập capability bị giới hạn; khi cần quyền bổ sung, ưu tiên --cap-add tối thiểu và cân nhắc --cap-drop ALL rồi add lại đúng capability cần thiết.
Seccomp lọc system calls. Docker có default seccomp profile và khuyến nghị không tắt profile này một cách tùy tiện. AppArmor/SELinux là Linux Security Modules (LSM) cung cấp policy confinement bổ sung. Đây là defense in depth: capabilities giới hạn quyền, seccomp giảm syscall attack surface, LSM kiểm soát hành vi theo policy.
--privileged. Chế độ privileged cấp tất cả Linux capabilities, mở rộng device access và gỡ/giảm nhiều confinement mặc định. Nếu workload chỉ cần một quyền hoặc device cụ thể, cấp đúng capability/device thay vì privileged toàn phần.4.1 Baseline hardening thực tế
- Chạy bằng non-root user khi ứng dụng cho phép; cân nhắc user namespace/rootless theo threat model.
- Giữ default seccomp profile; custom profile chỉ khi có requirement và test regression rõ ràng.
- Giữ AppArmor/SELinux enabled nếu distribution hỗ trợ; sửa policy thay vì tắt để “cho chạy”.
- Drop capabilities không cần thiết; tránh
CAP_SYS_ADMINnếu không thực sự bắt buộc. - Dùng
--security-opt no-new-privileges=truekhi phù hợp để ngăn process đạt thêm privilege. - Giới hạn bind mounts, đặc biệt Docker socket, device files, host root filesystem và credential paths.
- Đặt CPU/memory/PIDs limits có headroom và alert trước khi chạm hard limit.
5. Container vs VM
| Khía cạnh | Container | VM |
|---|---|---|
| Kernel | Chia sẻ host kernel. | Có guest kernel riêng. |
| Isolation boundary | Namespace/cgroup/capability/seccomp/LSM + runtime/kernel hardening. | Hardware virtualization + hypervisor + guest kernel tạo boundary mạnh hơn cho nhiều threat model. |
| Startup & density | Thường nhanh và mật độ cao vì không boot guest OS. | Overhead lớn hơn nhưng boundary độc lập hơn. |
| Multi-tenancy | Cần hardening cao, policy chặt và đôi khi sandbox/runtime bổ sung. | Thường phù hợp hơn khi tenant không tin cậy lẫn nhau và yêu cầu kernel boundary riêng. |
| Chọn khi nào? | Workload tin cậy tương đối, cần density/deploy speed, có platform controls tốt. | Threat model cần isolation mạnh hơn, OS/kernel khác nhau hoặc compliance yêu cầu boundary riêng. |
Quyết định nên dựa trên threat model, mức độ multi-tenancy, blast radius chấp nhận được và operational model, không dựa trên slogan “container = lightweight VM”. Trong hệ thống nhạy cảm, container vẫn có thể chạy bên trong VM để kết hợp deployment density với một boundary host/tenant rõ hơn.
6. Kiểm tra nhanh trên host
Các lệnh dưới đây giúp xác minh cấu hình runtime; đường dẫn/counter cụ thể có thể khác giữa cgroup v1/v2, rootless mode và distribution.
# Xem Docker đang dùng cgroup / security options gì
docker info
# Xem limits / security config đã apply lên container
docker inspect <container>
# Quan sát usage ở mức Docker
docker stats <container>
# Ví dụ chạy với resource/security baseline
docker run --rm \
--memory=512m \
--cpus=1.0 \
--pids-limit=256 \
--cap-drop=ALL \
--security-opt=no-new-privileges=true \
IMAGE
7. Production checklist
- Threat model đã xác định tenant trust, kernel/shared-host blast radius và dữ liệu nhạy cảm chưa?
- Workload có chạy non-root, drop capability và tránh privileged không?
- Seccomp và LSM profile có đang active; có ai tắt chúng để workaround lỗi không?
- CPU/memory/PIDs limit có dựa trên đo đạc thật và còn headroom cho burst/recovery không?
- Dashboard/alert có CPU throttling, memory/OOM events, restart, PIDs và I/O pressure không?
- Bind mount, device và Docker socket exposure có được review theo least privilege không?
- Khi limit/policy mới gây lỗi, có rollback path và evidence để phân biệt app bug với resource/security denial không?