Ringkasan Utama
CVE-2026-46680 membuat image rakitan bisa berjalan sebagai root di containerd walaupun Pod Kubernetes sudah memakai runAsNonRoot, karena User numerik yang terlalu besar dibaca sebagai username. Patch ke 1.7.32, 2.0.9, 2.2.4 atau 2.3.1 ke atas; lini 2.1 tidak mendapat fix. Sebelum itu, set runAsUser numerik eksplisit di setiap Pod.
Saya membaca GHSA-fqw6-gf59-qr4w untuk satu hal saja, apakah cluster saya terdampak, dan jawabannya ada di satu kalimat. Container yang dijalankan dengan User numerik yang terlalu besar untuk 32 bit diperlakukan sebagai username, dan image rakitan bisa memetakan string itu ke root lewat /etc/passwd miliknya sendiri. Spec Pod tetap berisi runAsNonRoot: true. Container tetap berjalan sebagai UID 0.
Tulisan ini membahas advisory yang terbit pada 20 Mei 2026 dengan nomor CVE-2026-46680 dan rating Moderate dari GitHub. Semua isi berasal dari advisory itu, kebijakan rilis dan halaman releases containerd, advisory terkait sebelumnya, serta sumber API dan Pod Security Standards dari Kubernetes, yang saya baca ulang pada 17 Oktober 2026. Saya tidak membuat atau menjalankan image berbahaya, dan tidak ada exploit di sini; ini materi untuk defender.
Apa isi advisory sebenarnya
Judul advisory-nya containerd user ID handling bypass allows runAsNonRoot evasion. Klaimnya sempit: container yang dijalankan dengan nilai User numerik yang tidak bisa di-parse sebagai integer 32 bit diperlakukan sebagai username. Jika /etc/passwd di dalam image memetakan string numerik besar itu ke root, container berjalan sebagai UID 0, sehingga pembatasan runAsNonRoot Kubernetes bisa dilewati.
Ada tiga batasan yang penting. GitHub memberi rating Moderate. Pelapornya Lei Wang (ssst0n3) lewat security policy containerd. Dan image rakitan itu harus benar-benar image yang Anda jalankan: workaround di advisory dimulai dengan memakai image tepercaya saja dan membatasi import image ke user tepercaya, yang menyiratkan ancaman realistisnya adalah image beracun atau tiruan yang masuk ke cluster yang menjadikan runAsNonRoot sebagai jaring pengaman.
Kenapa pengecekan policy dan runtime bisa berbeda
Kubernetes mendokumentasikan runAsNonRoot sebagai janji tentang image: jika true, kubelet memvalidasi image saat runtime agar tidak berjalan sebagai UID 0, dan gagal menjalankan container jika ternyata iya. runAsUser berbeda; ia adalah UID untuk entrypoint container dan, jika tidak diisi, mengikuti user di metadata image. Menurut pembacaan saya, ada dua komponen yang menafsirkan field User milik image, satu untuk validasi dan satu untuk menjalankan proses, dan advisory menggambarkan sisi runtime yang menganggap angka terlalu besar sebagai nama.
Karena itu perbaikan di level Pod berhasil. Saat runAsUser diisi eksplisit, direktif USER di image tidak lagi menentukan UID; advisory mencantumkan hal persis ini sebagai workaround. Berikut bentuk yang rentan dan bentuk yang lebih aman, berdampingan.
# Wrong: relies on the image's USER directive being honest. On an affected
# containerd, an oversized numeric USER can resolve to root through the
# image's own /etc/passwd, and runAsNonRoot alone did not stop it.
apiVersion: v1
kind: Pod
metadata:
name: api-wrong
spec:
securityContext:
runAsNonRoot: true
containers:
- name: api
image: registry.example.com/team/api:1.4.2
---
# Right: an explicit numeric UID overrides the image metadata (the advisory's
# workaround), and runAsNonRoot stays on as a second, independent check.
apiVersion: v1
kind: Pod
metadata:
name: api-right
spec:
securityContext:
runAsUser: 10001
runAsGroup: 10001
runAsNonRoot: true
containers:
- name: api
image: registry.example.com/team/api:1.4.2
securityContext:
allowPrivilegeEscalation: falseVersi terdampak dan versi fix di empat lini rilis
Advisory memberi versi terdampak dan versi fix untuk module 1.7 dan module v2. Saya gabungkan dengan status dukungan dari RELEASES.md containerd dan nomor patch terbaru di halaman releases, karena versi fix hanya berguna kalau lininya masih dirawat.
| Lini rilis | Terdampak sejak | Fix di | Status dan patch terbaru |
|---|---|---|---|
| 1.7 | 1.7.27 | 1.7.32 | End of life sejak 30 September 2026. Halaman releases menunjukkan 1.7.36. |
| 2.0 | 2.0.4 | 2.0.9 | LTS. Extended support sampai Maret 2027 dari dua maintainer yang disebut. Terbaru 2.0.14. |
| 2.1 | Dalam rentang v2 sejak 2.0.4 | Tidak ada fix | End of life sejak 3 Juli 2026. |
| 2.2 | Dalam rentang v2 sejak 2.0.4 | 2.2.4 | Active. End of life 6 November 2026. Terbaru 2.2.10. |
| 2.3 | Dalam rentang v2 sejak 2.0.4 | 2.3.1 | LTS sampai 30 April 2028. Terbaru 2.3.7. |
Baca kolom terakhir sebelum kolom ketiga. Menurut halaman kebijakan, 1.7 mencapai end of life pada 30 September 2026, jadi node 1.7 bisa dipatch ke 1.7.32 atau lebih baru tetapi bukan lini untuk bertahan. Lini 2.1 berakhir pada 3 Juli 2026 dan advisory menyebut ia tidak mendapat fix, jadi satu-satunya jalan adalah pindah ke 2.2 atau 2.3. Kebijakannya mendukung upgrade antar minor release yang berurutan dan langsung antar LTS yang berurutan, misalnya 1.7 ke 2.3.
Saya akan patch ke versi terbaru di lini Anda, bukan versi fix minimum. Halaman releases menunjukkan 2.0.14, 2.2.10 dan 2.3.7 pada 13 Oktober, masing-masing dengan perubahan hardening yang menyaring label ID-mapping dari annotation image saat unpack, yang tidak ada di 1.7.36. Saya tidak bisa memastikan perubahan itu terkait CVE ini, jadi anggap itu alasan mengambil patch terbaru, bukan bagian dari fix.
Kelas bug yang sama muncul dua tahun lalu
Ini bukan advisory pertama di area ini. GHSA-265r-hfxg-fhmg (CVE-2024-40635), juga Moderate, menyebut container yang dijalankan dengan User berbentuk UID:GID lebih besar dari integer 32 bit bertanda maksimum bisa overflow, sehingga container akhirnya berjalan sebagai root. Fix-nya ada di 1.6.38, 1.7.27 dan 2.0.4, dengan workaround yang sama: hanya image tepercaya, dan hanya user tepercaya yang boleh mengimport image.
Rentang terdampak pada advisory baru dimulai dari 1.7.27 dan 2.0.4, rilis yang sama dengan yang memperbaiki advisory lama, dan advisory baru mencantumkan yang lama sebagai referensi. Sumber tidak menyebut apakah fix lama membuka jalur baru atau hanya meninggalkan jalur di sebelahnya, dan saya tidak akan menebak. Pembacaan praktisnya lebih sederhana: mengubah string User menjadi UID sudah dua kali salah, jadi jangan jadikan itu satu-satunya lapisan pertahanan.
Empat mitigasi dan apa yang sebenarnya dicakup masing-masing
Advisory menawarkan beberapa workaround dan maintainer menambahkan catatan soal Kubernetes. Saya letakkan di samping fix permanen supaya trade-off-nya terlihat.
| Mitigasi | Bergantung pada | Batasan |
|---|---|---|
| Patch runtime | containerd pada atau di atas versi fix di setiap node | Perlu rollout node, dan 2.1 tidak punya rilis fix untuk dituju. |
| runAsUser numerik eksplisit | securityContext Pod, yang menurut advisory menimpa USER di image | Harus diisi di setiap workload; yang terlewat tetap terpapar di node terdampak. |
| Kubernetes 1.34 atau lebih baru | Advisory menyebut versi ini tampaknya menegakkan runAsNonRoot apa pun yang terjadi | Maintainer sendiri memberi hedge; jangan dijadikan dasar rencana. |
| Image tepercaya dan import terbatas | Izin registry dan import image | Mengurangi siapa yang bisa memasok image rakitan; ini bukan fix. |
Hanya dua yang pertama mengubah apa yang terjadi di node yang Anda kendalikan. Yang ketiga hanyalah pengamatan, dan yang keempat kebersihan dasar yang seharusnya sudah ada.
Lakukan dua yang pertama sekaligus. Perubahan Pod hanya butuh edit manifest dan melindungi Anda selama node di-drain dan di-roll; patch lalu menghilangkan ketergantungan pada edit itu yang harus ada di mana-mana.
Mengaudit cluster untuk melihat paparan
Tiga pertanyaan menentukan paparan Anda: node mana menjalankan containerd versi berapa, container mana menyerahkan UID ke image, dan image mana punya User numerik terlalu besar. Perintah di bawah menjawab masing-masing lewat kubectl dan, untuk yang ketiga, crane.
# 1. Runtime version per node. Compare against the fixed versions:
# 1.7.32, 2.0.9, 2.2.4, 2.3.1. A node on containerd://1.7.31 is affected.
kubectl get nodes -o custom-columns=NAME:.metadata.name,RUNTIME:.status.nodeInfo.containerRuntimeVersion
# 2. Containers with no runAsUser at pod or container level, so the image's
# own USER decides the UID. These are the workloads this advisory touches.
kubectl get pods -A -o json | jq -r '
.items[] | . as $p
| $p.spec.containers[]
| select($p.spec.securityContext.runAsUser == null and .securityContext.runAsUser == null)
| [$p.metadata.namespace, $p.metadata.name, .name, .image] | @tsv'
# 3. Images whose configured User is numeric and above the signed 32-bit limit.
# The advisory says "cannot be parsed as a 32-bit integer" and the 2024 one
# says "signed", so 2147483647 is the conservative line. Names and empty
# values are normal and skipped. Init containers are not covered here.
kubectl get pods -A -o jsonpath='{range .items[*].spec.containers[*]}{.image}{"\n"}{end}' | sort -u |
while read -r img; do
user=$(crane config "$img" 2>/dev/null | jq -r '.config.User // ""')
uid=${user%%:*} # drop an optional :GID suffix
case "$uid" in
''|*[!0-9]*) ;; # empty or a name: not this check
*) if [ "${#uid}" -gt 10 ] || [ "$uid" -gt 2147483647 ]; then
echo "SUSPECT $img User=$user"
fi ;;
esac
doneAnggap pengecekan ketiga sebagai petunjuk, bukan bukti. User numerik yang tampak terlalu besar baru berbahaya bila ada entri yang cocok di /etc/passwd image, dan saya tidak menguji itu. Ia juga melewati init container dan membaca platform mana pun yang dipilih crane, jadi perluas bila fleet Anda multi-arsitektur.
Di mana ini bisa salah
Kesalahan yang mahal adalah menganggap runAsNonRoot: true sebagai kontrol tunggal. Profil Restricted di Pod Security Standards mewajibkan runAsNonRoot bernilai true dan mengizinkan runAsUser berupa nilai non-nol apa pun atau tidak diisi. Pod yang membiarkan runAsUser kosong lolos admission, dan admission hanya melihat spec, bukan /etc/passwd di image. Di runtime terdampak, Pod itu bisa berjalan sebagai root dengan semua pengecekan hijau.
Jebakan kedua adalah biaya fix-nya. Mengunci runAsUser berarti proses tidak lagi berjalan sebagai user yang dipakai saat image dibuat, sehingga file milik user bawaan image bisa tidak terbaca atau tidak bisa ditulis. Ini penalaran saya dari cara kerja UID, bukan pernyataan di sumber, jadi uji setiap workload sebelum menerapkannya ke seluruh fleet, dan pilih image yang dibangun untuk berjalan dengan UID numerik yang jelas.
Node yang melaporkan containerd 2.1.x sudah lewat end of life dan, menurut advisory, tidak mendapat fix. Patch di tempat tidak mungkin. Rencanakan pindah ke 2.2 atau 2.3 dan, sampai saat itu, set runAsUser eksplisit di setiap workload yang dijadwalkan ke node tersebut.
Jangan juga membaca catatan Kubernetes 1.34 sebagai mitigasi. Advisory hanya menyebut versi 1.34 ke atas tampaknya menegakkan runAsNonRoot dengan benar terlepas dari bug ini. Kata tampaknya adalah hedge dari maintainer sendiri, dan saya tidak menemukan perubahan yang mendasarinya di sumber yang saya baca.
Terakhir, label severity bisa menyesatkan ke dua arah. GitHub menilai ini Moderate, dan tracker lain menerbitkan skor berbeda, jadi saya tidak akan mengurutkan prioritas dari satu angka. Cluster yang menjalankan image pihak ketiga dan mengandalkan runAsNonRoot untuk isolasi sebaiknya menjadikannya prioritas; yang hanya menjalankan image buatan sendiri wajar menjadwalkannya bersama upgrade runtime berikutnya.
Yang dikerjakan hari Senin
Berurutan, masing-masing cukup kecil untuk selesai dalam sehari.
- Jalankan query node dan daftar setiap node di bawah versi fix untuk lininya, catat yang masih di 2.1.x.
- Tambahkan runAsUser numerik eksplisit ke workload yang belum punya, mulai dari namespace yang menarik image dari registry publik atau menerima image pihak ketiga.
- Roll node ke patch terbaru di lininya, yang di halaman releases tercatat 1.7.36, 2.0.14, 2.2.10 dan 2.3.7, dan jadwalkan pindah dari 1.7 dan 2.1.
- Jalankan audit image, periksa asal setiap image yang mencurigakan, dan batasi siapa yang boleh mengimport image ke registry dan runtime node Anda.
- Jalankan ulang query node setelah rollout dan catat tanggal serta hasilnya sebagai baseline audit berikutnya.
Nomor versi dan tanggal di sini dibaca dari sumber pada 17 Oktober 2026. Halaman releases terus berubah, jadi cek ulang kolom patch terbaru sebelum mengandalkannya.
Field security context menyatakan niat; container runtime yang menjalankannya. Kunci UID secara eksplisit, patch containerd ke rilis fix di lini Anda, dan pensiunkan node pool yang tidak bisa di-upgrade, mulai dari yang 2.1.
Sumber dan bacaan lanjutan
- containerd GHSA-fqw6-gf59-qr4w: advisory runAsNonRoot evasion (CVE-2026-46680)
- containerd GHSA-265r-hfxg-fhmg: advisory user ID sebelumnya (CVE-2024-40635)
- containerd RELEASES.md: lini rilis dan kebijakan dukungan
- Halaman releases containerd: versi patch terbaru
- Dokumentasi Kubernetes: Pod Security Standards, profil Restricted
- Kubernetes API types: dokumentasi field SecurityContext runAsNonRoot dan runAsUser

