Node Security — Cô Lập Tầng Hạ Tầng
Vì sao node là ranh giới phòng thủ quan trọng nhất ở tầng hạ tầng
Container không phải là một security boundary mặc định. Mọi container trên một node chia sẻ cùng một kernel Linux. Khi một container escape — qua lỗ hổng kernel, qua cấu hình privileged, qua một CVE runtime — kẻ tấn công không chỉ chiếm một Pod, mà chiếm toàn bộ node và mọi Pod đang chạy trên đó. Đây là lý do node, chứ không phải container, là ranh giới phòng thủ thật ở tầng hạ tầng.
Mức độ nghiêm trọng của một node compromise rất cao vì node nắm giữ ba thứ giá trị: (1) bộ nhớ và filesystem của mọi Pod trên nó (bao gồm secret đã giải mã trong RAM), (2) credential của node service account (cửa vào GCP API), và (3) quyền root để cài rootkit hoặc persist. Bảo vệ node là bảo vệ tất cả những thứ đó cùng lúc.
GKE cung cấp bốn cơ chế bảo vệ node độc lập, mỗi cơ chế chống một lớp đe dọa khác nhau: Shielded Nodes chống tampering ở tầng boot, gVisor chống container escape qua syscall, Confidential Nodes chống đọc trộm bộ nhớ từ host/hypervisor, và COS giảm attack surface của chính OS. Cộng với nguyên tắc node service account tối thiểu để giới hạn thiệt hại khi node thất thủ. File này phân tích từng cơ chế: nó chống gì, không chống gì, và trade-off.
Shielded GKE Nodes: chống tampering ở tầng boot
Shielded GKE Nodes bảo vệ tính toàn vẹn của quá trình khởi động và identity của node, chống lại các tấn công tinh vi như rootkit và bootkit — malware nhúng vào trước cả khi OS chạy, vô hình với mọi công cụ chạy trong OS. Theo tài liệu Shielded GKE Nodes, nó gồm ba thành phần:
- Secure Boot: đảm bảo node chỉ chạy software ký bởi authority tin cậy. Tại mỗi bước boot (firmware → bootloader → kernel), chữ ký được verify; nếu một thành phần bị thay bằng phiên bản không ký hợp lệ (ví dụ kernel nhiễm rootkit), boot bị từ chối. Điều này chặn malware persist ở tầng boot.
- virtual Trusted Platform Module (vTPM): một TPM ảo cung cấp measured boot — ghi lại "measurement" (hash) của từng thành phần boot vào các thanh ghi PCR. vTPM cũng bảo vệ key và cho phép chứng thực mật mã rằng node boot đúng trạng thái kỳ vọng.
- Integrity Monitoring: so sánh measurement boot thực tế với một baseline tin cậy đã biết. Nếu measurement lệch (dấu hiệu node bị tamper), nó phát cảnh báo có thể quan sát trong Cloud Monitoring/Logging. Đây là lớp phát hiện bổ sung cho lớp ngăn chặn của Secure Boot.
Shielded Nodes là bật mặc định trên cluster mới và gần như không có lý do để tắt — chi phí hiệu năng không đáng kể, và nó chặn một lớp tấn công mà không công cụ runtime nào thấy được. Đây là baseline hardening đầu tiên, không phải tùy chọn nâng cao.
Lưu ý ngữ nghĩa: Shielded Nodes bảo vệ boot integrity và node identity, không bảo vệ workload đang chạy khỏi container escape (đó là việc của gVisor) hay khỏi đọc trộm bộ nhớ (đó là việc của Confidential Nodes). Nó là một lớp trong defense-in-depth, không phải giải pháp toàn diện.
GKE Sandbox (gVisor): chống container escape qua syscall
Đây là cơ chế trực tiếp nhất chống container escape. Theo tài liệu GKE Sandbox, gVisor là "a userspace re-implementation of the Linux kernel API that does not need elevated privileges" — một bản hiện thực lại API kernel Linux ở userspace, không cần đặc quyền cao.
Cơ chế: thêm một lớp kernel ở giữa
Bình thường, container gọi syscall thẳng xuống kernel của host. Với gVisor, có một kernel userspace (gọi là Sentry) đứng giữa: container gọi syscall → gVisor chặn lại → Sentry tự xử lý phần lớn syscall trong userspace, chỉ chuyển một tập rất nhỏ và được kiểm soát xuống host kernel. Hệ quả bảo mật: attack surface của host kernel co lại drastically — kẻ tấn công trong container không nói chuyện trực tiếp với host kernel nữa, mà với Sentry, nên một CVE kernel khai thác qua syscall thường bị Sentry chặn hoặc cô lập.
Sử dụng đơn giản qua RuntimeClass:
apiVersion: v1
kind: Pod
metadata:
name: untrusted-workload
spec:
runtimeClassName: gvisor # chạy Pod trong sandbox gVisor
containers:
- name: app
image: registry.example.com/third-party@sha256:...Trên Autopilot, bạn yêu cầu sandbox theo từng Pod. Trên Standard, bật sandbox trên node pool và mọi Pod trên node pool đó chạy sandboxed.
Trade-off hiệu năng
Tài liệu chính thức không công bố một con số phần trăm overhead cụ thể. Nó nêu rằng việc thêm một lớp gián tiếp để truy cập kernel "comes with performance trade-offs", và workload sinh ra nhiều syscall nhỏ với tần suất cao (nhiều thao tác I/O nhỏ) sẽ chịu overhead lớn hơn, có thể cần thêm tài nguyên hoặc node mạnh hơn. Nguyên tắc thực hành: gVisor phù hợp với workload CPU-bound hoặc compute-heavy (ML inference, xử lý media, web frontend); kém phù hợp với workload syscall-heavy/IO-intensive (database hiệu năng cao, workload nhiều network I/O nhỏ). Hãy benchmark workload cụ thể thay vì tin một con số chung — overhead biến thiên mạnh theo syscall profile.
Giới hạn quan trọng
gVisor đánh đổi tính tương thích lấy cô lập. Các tính năng không hoạt động với GKE Sandbox:
- Container chạy privileged.
- Volume hostPath và raw block volume.
- Port forwarding.
- Seccomp, AppArmor, SELinux ở tầng kernel module — vì gVisor thay thế lớp kernel, các cơ chế này không áp dụng theo cách thông thường (bản thân gVisor đã là một lớp lọc syscall mạnh hơn).
- Metric ở cấp container (chỉ có metric cấp Pod); một số tích hợp như Traffic Director.
Yêu cầu node pool (Standard): tối thiểu hai node pool (không sandbox được default pool), node image cos_containerd, không dùng Windows node pool.
Use case đúng
gVisor sinh ra cho các tình huống chạy code không tin cậy hoặc multi-tenant:
- Chạy third-party application hoặc code do người dùng cuối submit (FaaS, code execution platform).
- Multi-tenant SaaS nơi tenant không tin nhau và bạn cần cô lập mạnh hơn namespace.
- Workload xử lý dữ liệu/input không tin cậy từ bên ngoài (media processing, parsing untrusted file).
Với các tình huống này, gVisor biến một container escape từ "node compromise" thành "kẹt trong sandbox" — một nâng cấp bảo mật đáng giá ngay cả khi chấp nhận overhead.
Confidential GKE Nodes: mã hóa bộ nhớ data-in-use
Shielded bảo vệ boot; gVisor bảo vệ khỏi escape; nhưng cả hai đều không bảo vệ nội dung bộ nhớ khỏi chính hạ tầng host. Confidential GKE Nodes lấp khoảng trống đó.
Theo tài liệu Confidential GKE Nodes, nó dùng hardware-based memory encryption — điển hình là AMD SEV (Secure Encrypted Virtualization) — để mã hóa data-in-use, tức dữ liệu đang nằm trong RAM của node khi đang xử lý. Khóa mã hóa do CPU quản lý và không hiển lộ cho hypervisor hay host OS. Hệ quả: ngay cả Google (operator của hạ tầng) hay một kẻ tấn công chiếm được hypervisor cũng không đọc được nội dung bộ nhớ của node ở dạng rõ.
Mô hình ba trạng thái dữ liệu giúp định vị Confidential Nodes:
| Trạng thái dữ liệu | Cơ chế bảo vệ | Có sẵn |
|---|---|---|
| At-rest (trên đĩa) | Mã hóa đĩa (PD encryption) | Mặc định |
| In-transit (trên mạng) | TLS / mã hóa mạng | Mặc định |
| In-use (trong RAM) | Confidential Nodes (AMD SEV) | Tùy chọn, phải bật |
Confidential Nodes lấp đúng ô cuối — trạng thái mà mã hóa truyền thống không chạm tới.
Đặc tính triển khai:
- Machine series: AMD SEV hỗ trợ dòng N2D (trên Autopilot, machine series mặc định khi bật là N2D). GKE còn hỗ trợ các công nghệ confidential computing khác (AMD SEV-SNP, Intel TDX) trên các dòng máy tương ứng.
- Cách bật: cấp cluster (
--confidential-node-type=sev, không đảo ngược được), cấp node pool, hoặc cấp workload qua ComputeClass vớiconfidentialNodeType: SEV. - Overhead: tài liệu không công bố con số định lượng; mã hóa bộ nhớ phần cứng có chi phí nhưng thường ở mức chấp nhận được cho phần lớn workload.
Use case: compliance yêu cầu mã hóa data-in-use (một số quy định tài chính, y tế, chính phủ), workload xử lý dữ liệu cực kỳ nhạy cảm (khóa mật mã, PII, IP độc quyền), và mô hình tin cậy nơi bạn muốn giảm cả việc tin tưởng vào chính cloud operator.
Lưu ý ranh giới: Confidential Nodes bảo vệ bộ nhớ khỏi host/hypervisor, không bảo vệ khỏi tấn công trong node (một Pod privileged trên cùng node vẫn có thể đọc bộ nhớ Pod khác qua các kênh khác). Nó là một lớp cụ thể chống một threat cụ thể (đọc trộm từ hạ tầng), không thay thế gVisor hay securityContext.
Container-Optimized OS (COS): thu nhỏ attack surface
COS là OS mặc định của node GKE, thiết kế chuyên cho việc chạy container, và bản thân nó là một biện pháp hardening. Các đặc tính bảo mật cốt lõi:
- Read-only root filesystem: filesystem gốc của OS được mount read-only, mọi nơi có thể ghi đều có
noexecở những chỗ thích hợp. Kẻ tấn công không sửa được binary hệ thống hay cài persistent malware vào OS — một rào cản lớn cho việc duy trì quyền sau khi chiếm. - Minimal package set: COS chỉ chứa những gì cần để chạy container. Ít package = ít CVE = ít attack surface. Không có package manager, không có shell tooling thừa.
- Seccomp mặc định / locked-down kernel: COS áp các profile bảo mật kernel mặc định, giảm syscall surface ngay từ baseline.
- Automatic updates qua auto-upgrade: khi node auto-upgrade bật, Google vá OS node tự động (đối chiếu file 01 về shared responsibility). COS được thiết kế để update an toàn và thường xuyên.
Khuyến nghị: dùng cos_containerd làm node image (cũng là yêu cầu của gVisor). Tránh node image Ubuntu trừ khi có nhu cầu cụ thể (kernel module tùy chỉnh) — Ubuntu image có attack surface lớn hơn và đòi hỏi bạn gánh nhiều trách nhiệm hardening hơn.
Node service account: cửa vào GCP API phải hẹp nhất có thể
Đây là khía cạnh node security có tác động lớn nhất tới blast radius, và thường bị bỏ qua. Mỗi node chạy với một IAM service account (node SA), và mọi Pod trên node — nếu không có Workload Identity và metadata concealment — có thể lấy token của SA này qua metadata server.
Vấn đề: mặc định node dùng Compute Engine default service account, thường có role roles/editor ở cấp project — quyền cực rộng. Nếu một Pod bị chiếm và lấy được token này, kẻ tấn công có quyền editor trên toàn project. Đây chính là vector "credential theft → privilege escalation" trong threat model (file 01).
Hai biện pháp bắt buộc, áp dụng cùng nhau:
Node SA tối thiểu: tạo một service account riêng cho node với chỉ các quyền node thực sự cần — kéo image (
roles/artifactregistry.reader), ghi log (roles/logging.logWriter), ghi metric (roles/monitoring.metricWriter), và ít hơn nữa nếu được. Không bao giờ dùng Compute Engine default SA cho node production. Theo tài liệu hardening, hãy "use least-privilege service accounts" cho node, tránh default account.Metadata concealment qua GKE_METADATA: bật Workload Identity (file 04) đặt node pool vào chế độ GKE_METADATA, che metadata endpoint của node khỏi Pod. Khi đó Pod không lấy được token node SA qua metadata — nó chỉ nhận được token của KSA-mapped identity. Đây là cách cắt đứt vector ngay cả khi node SA vẫn còn một ít quyền.
# Tạo node SA tối thiểu (không dùng Compute Engine default)
gcloud iam service-accounts create gke-node-minimal \
--display-name="GKE node minimal SA"
# Chỉ cấp đúng quyền node cần
for ROLE in roles/logging.logWriter roles/monitoring.metricWriter \
roles/monitoring.viewer roles/artifactregistry.reader; do
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:gke-node-minimal@PROJECT_ID.iam.gserviceaccount.com" \
--role="$ROLE"
done
# Tạo node pool dùng SA tối thiểu + GKE_METADATA (che metadata node)
gcloud container node-pools create secure-pool \
--cluster CLUSTER --region REGION \
--service-account=gke-node-minimal@PROJECT_ID.iam.gserviceaccount.com \
--workload-metadata=GKE_METADATA \
--shielded-secure-boot --shielded-integrity-monitoring \
--image-type=cos_containerdKết hợp đầy đủ — node SA tối thiểu + metadata concealment + Workload Identity — biến một Pod compromise từ "leo thang lên quyền editor toàn project" thành "kẹt với quyền tối thiểu của chính workload đó". Đây là một trong những thay đổi giảm blast radius hiệu quả nhất bạn có thể làm.
Production architecture patterns
Pattern: node pool theo cấp độ tin cậy
Tách node pool theo mức độ tin cậy của workload: một pool gVisor cho untrusted/third-party/multi-tenant code; một pool Confidential cho workload nhạy cảm compliance; một pool thường (Shielded + COS + node SA tối thiểu) cho microservice nội bộ tin cậy. Dùng taint/toleration và nodeSelector để route workload đúng pool. Cách này áp đúng mức bảo vệ (và đúng chi phí/overhead) cho từng loại workload, thay vì áp gVisor cho mọi thứ (lãng phí hiệu năng) hay không áp cho gì (rủi ro).
Pattern: tenant nhạy cảm trên node pool/cluster riêng
Với multi-tenant nơi tenant là khách hàng bên ngoài không tin nhau, kết hợp node pool riêng cho mỗi tenant nhạy cảm + gVisor + Network Policy. Lý do: namespace chỉ là soft boundary; node pool riêng đảm bảo tenant A và tenant B không chia sẻ kernel, nên một container escape của A không chạm tới B. Với tenant cực nhạy cảm, leo lên cluster riêng để có cả ranh giới control plane.
Common mistakes / anti-patterns
1. Dùng Compute Engine default SA cho node. Vì sao xảy ra: đó là mặc định, không ai đổi. Hệ quả: mọi Pod có đường tới quyền editor toàn project qua metadata. Đây là một trong những misconfiguration nguy hiểm nhất của GKE. Phòng tránh: node SA riêng tối thiểu + GKE_METADATA.
2. Tắt auto-upgrade rồi quên patch node OS. Hệ quả: node chạy kernel có CVE đã biết — đúng thứ Shielded/gVisor cố bảo vệ lại bị bỏ ngỏ ở tầng dưới. Phòng tránh: dùng release channel + surge upgrade thay vì tắt (đối chiếu file 01, Chương 6).
3. Áp gVisor cho database hiệu năng cao. Vì sao xảy ra: "sandbox mọi thứ cho an toàn". Hệ quả: workload syscall-heavy chịu overhead nặng, latency tăng. Phòng tránh: gVisor cho untrusted/CPU-bound; workload tin cậy hiệu năng cao dùng node pool thường với securityContext chặt.
4. Tin rằng Shielded Nodes chống được container escape. Hệ quả: cảm giác an toàn sai; Shielded chỉ chống boot tampering, không chống escape runtime. Phòng tránh: hiểu mỗi cơ chế chống threat nào — Shielded (boot) + gVisor (escape) + Confidential (memory) là ba thứ khác nhau, dùng phối hợp.
5. Bật Confidential Nodes và nghĩ đã "mã hóa tất cả". Hệ quả: bỏ qua việc Confidential chỉ mã hóa memory khỏi host, không chống Pod privileged cùng node hay container escape. Phòng tránh: kết hợp Confidential với securityContext, gVisor, Network Policy — nó là một lớp, không phải toàn bộ.
GCP-native implementation guidance
# Kiểm tra node pool đã hardening đầy đủ chưa
gcloud container node-pools describe POOL \
--cluster CLUSTER --region REGION \
--format="yaml(config.serviceAccount,
config.shieldedInstanceConfig,
config.workloadMetadataConfig.mode,
config.sandboxConfig,
config.imageType,
config.confidentialNodes)"Trên một node pool đã hardening tốt, kỳ vọng thấy: serviceAccount là SA tùy chỉnh (không phải ...-compute@developer.gserviceaccount.com), shieldedInstanceConfig bật secure boot + integrity monitoring, workloadMetadataConfig.mode: GKE_METADATA, imageType: COS_CONTAINERD, và sandboxConfig/confidentialNodes bật trên các pool tương ứng nhu cầu.
Operational implications
Node security có một đặc tính vận hành đáng chú ý: phần lớn các cơ chế (Shielded, COS, node SA, metadata concealment) được cấu hình một lần lúc tạo node pool và sau đó hoạt động trong suốt — chi phí vận hành liên tục gần như bằng không, nên không có lý do để bỏ qua chúng. Ngược lại, gVisor và Confidential Nodes có chi phí hiệu năng liên tục, nên chúng là quyết định theo workload cần cân nhắc trade-off, không phải bật đại trà.
Hệ quả thực hành: hãy coi Shielded + COS + node SA tối thiểu + GKE_METADATA là baseline bắt buộc cho mọi node pool (chi phí thấp, lợi ích cao, giảm blast radius mạnh), và coi gVisor + Confidential là công cụ chuyên dụng áp cho đúng workload cần (untrusted, hoặc compliance memory encryption). Phân biệt rõ hai nhóm này giúp tránh cả hai cực: hardening thiếu (bỏ baseline) và hardening thừa (áp gVisor cho mọi thứ làm chậm hệ thống mà không tăng an toàn tương xứng).