Skip to content

GKE Sandbox (gVisor)

Tại sao kernel isolation là ranh giới quyết định cho untrusted

Mọi cơ chế ở các file trước (namespace, RBAC, NetworkPolicy, Quota, PSA) hoạt động ở lớp Kubernetes/mạng. Không cơ chế nào trong số đó ngăn được một lỗ hổng kernel: nếu code untrusted khai thác một CVE trong syscall của kernel Linux, hoặc một lỗ hổng container escape trong runtime (runc), nó thoát khỏi container và chiếm node — vượt qua mọi NetworkPolicy và restricted PSA.

Vấn đề gốc: container chia sẻ kernel của host. Container isolation (Linux namespaces, cgroups, seccomp) thu hẹp những gì container thấy, nhưng mọi syscall vẫn đi thẳng tới cùng một kernel được chia sẻ với mọi container/tenant khác trên node. Kernel Linux có hàng trăm syscall, mỗi cái là một mặt phẳng tấn công. Đây là ranh giới yếu nhất trong soft multi-tenancy.

GKE Sandbox, dựa trên gVisor, giải quyết chính xác vấn đề này: nó chèn một kernel userspace riêng cho mỗi Pod giữa workload và kernel host, drastically thu hẹp mặt phẳng tấn công vào kernel thật.

Internal model: gVisor hoạt động thế nào

Theo tài liệu GKE Sandbox, gVisor là một kernel userspace (userspace kernel) re-implement phần lớn Linux kernel API trong không gian người dùng. Kiến trúc gồm các thành phần:

┌─────────────────────────────────────────────┐
│  Untrusted container (workload của tenant)    │
│       │ syscall                                │
│       ▼                                        │
│  ┌──────────────────────────────────┐         │
│  │  Sentry (userspace kernel)        │         │  ← chặn & xử lý
│  │  - re-implement syscall           │         │     syscall thay
│  │  - không gọi thẳng host kernel    │         │     cho host kernel
│  └──────────────────────────────────┘         │
│       │ (chỉ một tập syscall rất hẹp,          │
│       │  đã được kiểm soát)                     │
│       ▼                                         │
│  ┌──────────────────────────────────┐         │
│  │  Gofer (file system proxy)        │         │  ← I/O file qua
│  └──────────────────────────────────┘         │     proxy riêng
└───────┼────────────────────────────────────────┘

   Host kernel (chỉ nhận syscall đã được Sentry lọc)
  • Sentry: thành phần cốt lõi — chặn (intercept) mọi syscall từ container và re-implement chúng trong userspace, không elevate privilege. Theo tài liệu, "userspace kernel re-implement phần lớn syscall và phục vụ chúng thay cho host kernel". Container nghĩ nó nói chuyện với Linux kernel, nhưng thực ra nói với Sentry.

  • Gofer: process riêng xử lý thao tác file system, cô lập I/O file khỏi host.

  • Kết quả: container untrusted không bao giờ gọi trực tiếp host kernel. Chỉ một tập syscall rất hẹp và được kiểm soát từ Sentry tới host kernel được phép. Bề mặt tấn công vào kernel thật giảm từ hàng trăm syscall xuống còn rất ít.

gVisor bảo vệ chống lại gì

Theo tài liệu, GKE Sandbox giảm thiểu các mối đe dọa:

  • Container escape lên host kernel: lỗ hổng kernel/runtime bị chặn bởi lớp Sentry.
  • Truy cập dữ liệu workload khác: mỗi sandbox có kernel userspace riêng, cô lập khỏi Pod khác.
  • Truy cập trái phép cluster metadata và Google Cloud services: hạn chế đường đi tới metadata server.

Điểm cốt lõi: gVisor biến soft multi-tenancy (chia sẻ kernel) thành một dạng hard multi-tenancy nhẹ (kernel riêng mỗi Pod) — mà không cần VM/node riêng cho mỗi tenant.

Chi phí hiệu năng (overhead)

Không có gì miễn phí. Theo tài liệu, "việc áp đặt một lớp gián tiếp để truy cập kernel của node đi kèm với trade-off hiệu năng". Chi phí cụ thể:

  • Syscall-heavy workloads chịu phạt nặng nhất. Mỗi syscall giờ đi qua Sentry (userspace) thay vì gọi trực tiếp — overhead per-syscall. Workload "tạo nhiều syscall low-overhead" (ví dụ I/O nhỏ liên tục, networking nhiều kết nối ngắn) có thể cần thêm tài nguyên hoặc thêm node.

  • CPU-bound workloads ít bị ảnh hưởng. Tính toán thuần (ít syscall) gần như không bị phạt vì phần lớn thời gian không gọi kernel.

  • Memory metrics ở container level không có (chỉ Pod-level) — một giới hạn quan sát.

Hệ quả thiết kế: gVisor phù hợp với workload không quá syscall-intensive nhưng cần isolation. Một web service thông thường, batch job tính toán, function người dùng — phù hợp. Một database I/O-intensive hay proxy network throughput cao — cân nhắc kỹ overhead.

Giới hạn: cái gì không hỗ trợ

Đây là phần quyết định gVisor có dùng được cho workload của bạn không. Theo tài liệu, GKE Sandbox không hỗ trợ:

  • Privileged containershostPath storage (hợp lý — đó là những thứ gVisor được thiết kế để chặn).
  • Port forwardingVolumeDevices.
  • Kernel security modules: Seccomp, AppArmor, SELinux (gVisor tự là lớp bảo vệ thay thế).
  • Memory metrics ở container level (Pod-level vẫn có).

Yêu cầu node pool (Standard clusters):

  • Không dùng được Windows Server node pools.
  • Tối thiểu hai node pool: phải có ít nhất một node pool không sandbox với ≥1 node (cho system workload không chạy được trong sandbox, ví dụ một số DaemonSet).
  • Container-Optimized OS với containerd (cos_containerd) bắt buộc.

Hỗ trợ accelerator (mới hơn):

  • GPU từ GKE 1.29.2-gke.1108000+, chỉ workload CUDA.
  • TPU từ GKE 1.31.3-gke.1111001+.
  • Không tính thêm phí cho workload accelerator chạy trong sandbox.

GCP-native implementation

Standard cluster: tạo node pool sandbox

bash
gcloud container node-pools create sandbox-pool \
  --cluster=my-cluster \
  --region=us-central1 \
  --sandbox type=gvisor \
  --machine-type=e2-standard-4 \
  --image-type=cos_containerd \
  --num-nodes=2

Node pool sandbox tự động nhận taint sandbox.gke.io/runtime=gvisor:NoSchedule, đảm bảo chỉ Pod khai báo dùng gVisor mới được schedule lên đó.

Chạy Pod trong sandbox: RuntimeClass

Workload chọn sandbox qua runtimeClassName: gvisor:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: untrusted-workload
  namespace: tenant-untrusted
spec:
  template:
    spec:
      runtimeClassName: gvisor          # ← chạy trong gVisor sandbox
      tolerations:
      - key: sandbox.gke.io/runtime     # toleration cho taint của node pool
        operator: Equal
        value: gvisor
        effect: NoSchedule
      containers:
      - name: app
        image: us-docker.pkg.dev/my-project/repo/app:latest
        # KHÔNG được privileged, KHÔNG hostPath — gVisor không hỗ trợ

Autopilot

Trên GKE Autopilot, sandbox được kích hoạt đơn giản bằng runtimeClassName: gvisor trong Pod spec — Autopilot tự quản lý node pool sandbox phía dưới. Không cần tạo node pool thủ công.

Production architecture patterns

Pattern 1: Sandbox bắt buộc cho namespace untrusted

Mọi Pod trong namespace untrusted bắt buộc dùng gVisor, enforce qua một policy (Policy Controller/Kyverno) chặn Pod không có runtimeClassName: gvisor. Kết hợp restricted PSA (file 06) và default-deny NetworkPolicy (file 04). Đây là bộ ba phòng thủ chuẩn cho code untrusted không dùng cluster riêng.

Pattern 2: Sandbox chọn lọc cho workload rủi ro cao

Trong cluster trusted phần lớn, chỉ một số workload xử lý input không tin cậy (parser xử lý file người dùng upload, image/video transcoder, PDF renderer — các vector tấn công kinh điển) chạy trong sandbox. Phần còn lại chạy native để tránh overhead. gVisor được áp đúng nơi rủi ro cao nhất.

Real-world scenario

CI/CD runner platform: Một platform chạy pipeline CI tùy ý của các đội — về bản chất là thực thi code không kiểm soát (build script, test có thể làm bất cứ gì). Mỗi job chạy trong Pod với runtimeClassName: gvisor, trên node pool sandbox riêng, NetworkPolicy egress chặn metadata server + internal services, ResourceQuota chống fork bomb. Khi một job độc hại cố gắng khai thác kernel để đào crypto hoặc thoát ra đọc secret của job khác, gVisor chặn ở lớp syscall — host kernel không bao giờ nhận syscall nguy hiểm. Overhead syscall chấp nhận được vì build job chủ yếu CPU/IO disk, không syscall-intensive cực đoan.

Common mistakes / anti-patterns

Anti-pattern 1: Dùng sandbox cho workload syscall-intensive

Vì sao xảy ra: Áp gVisor cho mọi thứ "để an toàn", kể cả database hay high-throughput proxy.

Hệ quả ở scale: Workload syscall-heavy chịu phạt hiệu năng nặng — latency tăng, throughput giảm, cần thêm node để bù. Chi phí tăng và SLA bị ảnh hưởng.

Cách phòng tránh: Đo overhead cho workload cụ thể trước. Dùng sandbox cho workload cần isolation không quá syscall-intensive. Database I/O-heavy thường nên cô lập bằng node/cluster riêng thay vì gVisor.

Anti-pattern 2: Workload cần privileged/hostPath chạy trong sandbox

Vì sao xảy ra: Không biết gVisor không hỗ trợ các tính năng này.

Hệ quả ở scale: Pod fail khởi động hoặc tính năng không hoạt động (privileged container, hostPath mount, port-forward đều không được). Tốn thời gian debug vì lỗi không rõ ràng.

Cách phòng tránh: Kiểm tra danh sách không hỗ trợ trước. Workload cần các tính năng này không phù hợp với sandbox — thiết kế lại hoặc dùng isolation khác.

Anti-pattern 3: Quên non-sandbox node pool

Vì sao xảy ra: Tạo cluster chỉ với node pool sandbox.

Hệ quả ở scale: Một số system workload/DaemonSet không chạy được trong gVisor; cluster gặp lỗi vì không có node pool thường để chạy chúng. Tài liệu yêu cầu tối thiểu một node pool non-sandbox với ≥1 node.

Cách phòng tránh: Luôn giữ ít nhất một node pool non-sandbox cho system component.

Anti-pattern 4: Coi sandbox là thay thế cho mọi lớp khác

Vì sao xảy ra: "Đã có gVisor, không cần NetworkPolicy/PSA".

Hệ quả ở scale: gVisor cô lập kernel, nhưng không kiểm soát mạng (Pod sandbox vẫn gọi được tenant khác nếu không có NetworkPolicy) hay quyền API (vẫn cần RBAC). Một lớp đơn không đủ.

Cách phòng tránh: gVisor là một lớp. Kết hợp với NetworkPolicy default-deny, restricted PSA, RBAC tối thiểu, ResourceQuota. Defense-in-depth.

References