Ringkasan Utama
kubectl-ai meminta konfirmasi sebelum menjalankan perintah apa pun yang tidak ditandai read-only oleh classifier-nya, tetapi classifier itu hanya heuristik, dan mode MCP server-nya melewati prompt serta listen tanpa autentikasi di semua interface secara default. Anggap prompt sebagai kemudahan, batasi dengan identitas RBAC read-only, dan aktifkan OAuth sebelum endpoint HTTP dibuka.
Sebelum mengarahkan kubectl-ai milik Google ke sebuah cluster, ada satu hal yang ingin saya pastikan: ketika ia bilang akan bertanya dulu sebelum mengubah apa pun, apa sebenarnya yang menentukan sebuah perintah dianggap perubahan? README menyebut skipPermissions bernilai false secara default dan perintah yang memodifikasi resource butuh konfirmasi. Itu kalimat tentang niat. Saya ingin melihat fungsi di baliknya.
Jadi panduan ini disusun dari source code, bukan dari materi promosi. Saya membaca README, dokumentasi MCP server, dan tiga file Go dari branch main pada commit 2c8ff82 (15 Juli 2026), lalu membangun binary dan test harness sementara di mesin sendiri untuk mengajukan sepuluh pertanyaan ke classifier dan satu ke MCP server. Rilis terbaru saat artikel ini ditulis adalah v0.0.31 dari Maret 2026, jadi main sudah lebih maju. Semua yang bertanda measured di bawah adalah hasil run saya sendiri, dan apa pun yang tidak bisa saya konfirmasi saya tinggalkan.
Apa itu kubectl-ai, dan empat cara menjalankannya
kubectl-ai adalah agent open-source berbasis Go yang mengubah permintaan seperti bagaimana kabar aplikasi nginx menjadi pemanggilan kubectl dan bash, menjalankannya, membaca output, lalu terus berjalan sampai tugas selesai. Ia bisa dipasang lewat binary rilis, install script, atau Krew (kubectl krew install ai), dan karena kubectl menganggap setiap executable berawalan kubectl- di PATH sebagai plugin, perintah kubectl ai juga berfungsi. Gemini adalah provider default, dengan vertexai, azopenai, openai, grok, bedrock, ollama dan llama.cpp juga tercantum di README.
# Pick one. All three are documented in the README.
curl -sSL https://raw.githubusercontent.com/GoogleCloudPlatform/kubectl-ai/main/install.sh | bash
kubectl krew install ai # then invoke it as: kubectl ai
# Gemini is the default provider; the key comes from the environment.
export GEMINI_API_KEY=your_api_key_here
kubectl-ai # 1. interactive chat, keeps context
kubectl-ai --quiet "fetch logs for nginx app in hello namespace" # 2. one-shot
cat error.log | kubectl-ai "explain the error" # 3. stdin pipeline
kubectl-ai --ui-type web --ui-listen-address localhost:8888 # 4. web UI
# Local model: small models need the tool-use shim to emit tool calls at all.
kubectl-ai --llm-provider ollama --model gemma3:12b-it-qat --enable-tool-use-shimIa berjalan dengan empat cara: chat interaktif yang menjaga konteks, one-shot dengan --quiet, pipeline stdin, dan web UI. README menyatakan proyek ini bukan produk Google yang didukung secara resmi dan tidak memenuhi syarat program hadiah kerentanan open source Google. Hal itu layak dibaca sebelum Anda memberinya kredensial ke sesuatu yang penting. Perintah di bawah adalah yang didokumentasikan README.
Gerbang konfirmasi, dibaca dari source code
Setiap tool di agent mengimplementasikan method CheckModifiesResource yang mengembalikan satu dari tiga string: yes, no, atau unknown. Classifier kubectl mem-parse perintah dengan shell parser, mencari verb, lalu membandingkannya dengan dua daftar hard-coded, satu berisi verb read-only dan satu berisi verb write. Verb write yang disertai flag --dry-run diturunkan menjadi no.
Detail yang penting ada di agent loop pada conversation.go. Gerbang itu tidak bertanya apakah sebuah perintah adalah write. Ia bertanya apakah jawabannya selain no. Jadi unknown bersifat fail closed: verb yang tidak dikenal daftar, flag di posisi tak terduga, perintah yang bukan kubectl, atau pipeline dengan lebih dari satu perintah, semuanya menghasilkan prompt. Komentar kode pada kasus multi-perintah merujuk ke lethal trifecta, yang menunjukkan penulisnya memikirkan exfiltration, bukan hanya delete yang tidak sengaja.
Saya menjalankan classifier dari file test yang diletakkan di samping package-nya. Berikut hasilnya, dan apakah user akan ditanya.
// pkg/tools/zz_probe_test.go: dropped next to the package, then
// go test ./pkg/tools -run TestProbe -v
package tools
import "testing"
func TestProbe(t *testing.T) {
for _, c := range []string{
"kubectl -n prod get pods", // spaced flag before the verb
"kubectl get secret db -o yaml", // a read that leaks
"kubectl auth reconcile -f rbac.yaml", // verb is "auth"
"kubectl get pods | xargs rm", // composite command
} {
t.Logf("%-45s => %s", c, kubectlModifiesResource(c))
}
}| Perintah | Classifier menjawab | Prompt muncul? |
|---|---|---|
| kubectl get pods -n prod | no | Tidak, langsung jalan |
| kubectl -n prod get pods | unknown | Ya: flag berspasi sebelum verb tidak bisa di-parse |
| kubectl delete pod web-1 | yes | Ya |
| kubectl delete pod web-1 --dry-run=client | no | Tidak, dry-run dianggap sebagai read |
| kubectl get pods; kubectl delete ns prod | yes | Ya |
| kubectl get pods | xargs rm | unknown | Ya: perintah gabungan tidak pernah dipercaya |
| kubectl get secret db -o yaml | no | Tidak, dan isi Secret masuk ke konteks chat |
| kubectl auth reconcile -f rbac.yaml | no | Tidak, padahal ia menulis objek RBAC |
| kubectl port-forward svc/db 5432:5432 | no | Tidak, padahal membuka tunnel ke database Anda |
| helm uninstall web | unknown | Ya: bukan kubectl, jadi tidak pernah dianggap aman |
Di mana gerbang itu meloloskan sesuatu
Kegagalan yang akan saya antisipasi adalah klasifikasi verb, bukan prompt-nya. Classifier memakai kata pertama setelah kubectl, sehingga auth dianggap verb read-only karena can-i dan whoami adalah read, dan auth reconcile, yang menerapkan RBAC, ikut lolos di bawahnya. Saya menjalankannya lewat classifier dan tidak ada prompt. Hal yang sama berlaku untuk get secret, yang merupakan read sah sekaligus langkah data exfiltration, karena outputnya ditambahkan ke percakapan dan dikirim ke provider model Anda.
Jebakan kedua ada di menu konfirmasi. Menunya menawarkan tiga pilihan: Yes, Yes and do not ask me again, dan No. Pilihan tengah mengubah SkipPermissions menjadi true untuk sisa sesi. Satu ketukan tombol dalam keadaan lelah di akhir sesi debugging yang panjang mematikan seluruh pengaman, dan perintah berikutnya, semerusak apa pun, berjalan tanpa pertanyaan. Saat Anda memilih No, model diberi tahu bahwa user menolak dan pemanggilan itu tidak boleh di-retry, sehingga ia seharusnya tidak terus mendorong perintah yang sama.
Classifier memutuskan dari teks perintah, bukan dari efek perintah itu terhadap cluster. Read yang membuka Secret, tunnel, dan subperintah seperti auth reconcile diklasifikasikan no dan berjalan tanpa prompt. Jangan mengandalkan prompt untuk melindungi Secret atau RBAC.
Semua ini tidak membuat gerbang itu sia-sia. Ia menangkap kasus umum dengan baik, termasuk pipeline multi-perintah yang ceroboh. Artinya gerbang ini adalah polisi tidur di depan sebuah model, bukan sistem otorisasi, dan keduanya tidak boleh tertukar saat seseorang bertanya apakah agent ini aman dijalankan di production.
Mode quiet, maxIterations, dan run tanpa pengawasan
Dalam mode --quiet tidak ada yang bisa ditanya, dan kodenya menanganinya dengan tegas. Jika model mengusulkan perintah yang akan memicu prompt, run berhenti dengan error yang menyebut RunOnce mode cannot handle permission requests, mendaftar perintahnya, dan mengarahkan Anda ke --skip-permissions. Jadi run one-shot bersifat read-only secara default, sifat yang bagus untuk cron job, dan flag yang mencabutnya diberi label dangerous pada help-nya.
maxIterations bernilai 20 secara default dan membatasi jumlah putaran agent loop untuk satu tugas. Saat batas tercapai, agent mencetak Maximum number of iterations reached dan berhenti. Ia pemutus loop, bukan batas biaya, karena penghitungnya di-reset ke nol setiap kali Anda mengetik query baru.
File config berada di ~/.config/kubectl-ai/config.yaml dan flag command line mengalahkannya. Ini yang akan saya jadikan titik awal untuk asisten yang sebagian besar read-only.
# ~/.config/kubectl-ai/config.yaml
# Flags override this file; this file overrides defaults.
llmProvider: gemini
skipPermissions: false # the default. Never set true in a file you share.
maxIterations: 20 # loop breaker per task, not a cost cap
kubeconfig: ~/.kube/kubectl-ai-readonly.yaml # a dedicated identity, not your admin context
uiType: terminal
uiListenAddress: localhost:8888 # only matters when uiType is webPin kubeconfig secara eksplisit dengan --kubeconfig, dan pakai file yang hanya berisi identitas read-only. Agent yang mewarisi context Anda saat ini bisa menuju production padahal Anda mengira sedang di staging.
Mode MCP client: setiap external tool memicu prompt
Dengan --mcp-client (tersedia sejak v0.0.12 menurut README), kubectl-ai terhubung ke server yang tercantum di ~/.config/kubectl-ai/mcp.yaml dan menawarkan tool mereka ke model di samping kubectl dan bash. Wrapper tool mengembalikan unknown dari CheckModifiesResource untuk setiap external tool, dengan komentar bahwa ia tidak bisa tahu apa yang dilakukan tool sembarang. Digabung dengan gerbang di atas, artinya setiap pemanggilan external tool meminta persetujuan, dan itu default yang benar.
# ~/.config/kubectl-ai/mcp.yaml (list form, as in the README and client guide)
servers:
# stdio server: kubectl-ai starts it as a child process
- name: sequential-thinking
command: npx
args:
- -y
- "@modelcontextprotocol/server-sequential-thinking"
# HTTP server with a bearer token read from the environment
- name: custom-api
url: https://api.example.com/mcp
auth:
type: "bearer"
token: "${MCP_TOKEN}"
# Run with: kubectl-ai --mcp-client
# Every tool above reaches the model as modifies_resource = unknown, so each call prompts.Artinya juga opsi Yes and do not ask me again kini mencakup tool yang tidak Anda tulis. Kalau Anda menjawabnya sekali untuk pencarian dokumentasi yang tidak berbahaya, sisa sesi bisa memanggil server email atau ticketing tanpa bertanya. Jaga sesi tetap pendek dan set tool tetap kecil.
Satu kendala praktis: dokumentasinya tidak konsisten soal bentuk mcp.yaml. README dan panduan client memakai daftar entri dengan field name, sedangkan contoh di panduan server memakai map berkunci nama. Saya memakai bentuk daftar, yaitu yang didokumentasikan untuk client.
Mode MCP server: tanpa gerbang, tanpa auth, di semua interface
kubectl-ai juga bisa menjadi server. Dengan --mcp-server ia mengekspos tool bash dan kubectl ke client lain seperti Claude Desktop lewat stdio, dan dengan --mcp-server-mode streamable-http --http-port 9080 ia melayani MCP lewat HTTP. Di cmd/mcp.go, handler built-in tool men-decode argumen lalu memanggil tool.Run langsung. Saya tidak menemukan pemanggilan CheckModifiesResource di jalur itu, jadi persetujuan apa pun adalah tugas client yang terhubung, bukan tugas kubectl-ai.
Dokumentasi menyebut endpoint HTTP listen di http://localhost:9080/mcp dan memperingatkan bahwa tanpa autentikasi, siapa pun yang bisa menjangkau port itu dapat menjalankan kubectl dan bash. Kodenya menyerahkan endpoint sebagai port saja. Saya menjalankan server dan melihat daftar socket-nya: ia terikat ke *:19080, bukan 127.0.0.1. Lalu saya mengirim request MCP initialize ke alamat LAN mesin saya tanpa token dan mendapat respons valid. Itu measured di macOS dengan binary yang dibangun dari commit di atas, jadi periksa build Anda sendiri dengan lsof sebelum memercayai kata localhost.
Perbaikannya sudah tersedia dan bersifat opt-in. Mengatur --mcp-auth-issuer mengubah endpoint menjadi OAuth 2.1 resource server yang memverifikasi bearer JWT terhadap JWKS milik issuer, dan --mcp-auth-audience menjadi wajib. Pemeriksaan audience adalah yang mencegah token yang diterbitkan untuk layanan lain oleh authorisation server yang sama bisa dipakai di sini, yaitu kasus confused deputy yang disebut spesifikasi MCP authorization. Selama signing key belum dimuat, endpoint menjawab 503, bukan meloloskan request.
# Wrong: the documented quick start. Bound to every interface in my test,
# and any host that can reach :9080 gets the bash and kubectl tools.
kubectl-ai --mcp-server --mcp-server-mode streamable-http --http-port 9080
# Right: OAuth 2.1 resource server. aud must equal the audience string exactly.
kubectl-ai --mcp-server --mcp-server-mode streamable-http --http-port 9080 \
--mcp-auth-issuer https://authgate.corp \
--mcp-auth-audience https://kubectl-ai.corp/mcp
# Verify the server really is locked: expect 401 and a WWW-Authenticate header.
curl -i -X POST https://kubectl-ai.corp/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}'
# Discovery document (RFC 9728), served without a token:
curl -s https://kubectl-ai.corp/.well-known/oauth-protected-resource
# Check what the plain server bound to, on the machine that runs it:
lsof -nP -iTCP:9080 -sTCP:LISTEN # "*:9080" means every interface| Mode | Siapa yang menyetujui write | Paparan jaringan |
|---|---|---|
| Chat interaktif | Anda, per perintah, kecuali memilih do not ask again | Tidak ada, terminal lokal |
| --quiet one-shot | Tidak ada: run dibatalkan saat ada write kecuali --skip-permissions diatur | Tidak ada |
| --mcp-server lewat stdio | MCP client, jika ia menampilkan prompt; kubectl-ai tidak punya gerbang di jalur ini | Child process dari client |
| streamable-http, tanpa flag auth | Tidak ada | Terikat ke semua interface pada test saya |
| streamable-http dengan flag issuer dan audience | Siapa pun yang memegang token valid; tetap tanpa gerbang | Terautentikasi, 401 tanpa token |
OAuth memperbaiki siapa yang boleh memanggil endpoint. Ia tidak menambah langkah konfirmasi, dan tidak mempersempit apa yang bisa dijalankan pemanggil: client yang terautentikasi tetap mendapat tool bash dan kubectl. Padukan dengan identitas read-only, atau pemegang token memiliki apa pun yang dimiliki kubeconfig.
Pasang RBAC di bawah gerbang itu
Satu-satunya kontrol yang tidak bergantung pada pencocokan string adalah otorisasi milik cluster sendiri. Kubernetes menyediakan ClusterRole view untuk pengguna yang mengizinkan akses read-only ke sebagian besar objek dalam sebuah namespace, tidak mengizinkan melihat role atau role binding, dan sengaja mengecualikan Secret. Dokumentasi RBAC menjelaskan alasannya: membaca Secret membuka kredensial ServiceAccount, yang akan menjadi privilege escalation. Satu role itu menutup dua lubang terburuk di tabel probe: get secret mengembalikan error forbidden, dan auth reconcile tidak bisa menulis.
Jalankan kubectl-ai dengan kubeconfig yang memegang ServiceAccount yang di-bind ke role itu, lalu konfirmasi dengan kubectl auth can-i sebelum sesi pertama. Jika model perlu menulis, berikan Role terpisah yang dibatasi namespace untuk verb tepat itu dan ganti kubeconfig dengan sengaja, daripada memperluas identitas yang permanen.
Yang dilakukan pada hari Senin
Daftar singkat, dalam urutan yang akan saya kerjakan, untuk tim yang ingin mencoba kubectl-ai tanpa menjadikannya beban:
- Buat identitas read-only khusus yang di-bind ke ClusterRole view di namespace yang dibutuhkan asisten, lalu arahkan kubectl-ai ke kubeconfig-nya dengan --kubeconfig.
- Biarkan skipPermissions tetap false di mana-mana, dan anggap jawaban Yes and do not ask me again sebagai cara mematikan pengaman untuk seluruh sesi.
- Pakai --quiet hanya untuk pertanyaan read-only, supaya ia gagal dengan jelas saat ada write, bukan diberi --skip-permissions agar lolos.
- Jangan jalankan mode streamable-http di jaringan bersama. Jika tool lain harus menjangkaunya, atur --mcp-auth-issuer dan --mcp-auth-audience, letakkan di belakang TLS, dan pastikan 401-nya dengan curl.
- Tambahkan server MCP client satu per satu, dan ingat bahwa tool mereka sampai ke model sebagai unknown sehingga memicu prompt, sampai jawaban do not ask me again yang pertama.
- Periksa build yang Anda jalankan terhadap commit yang saya baca. Proyek ini bergerak cepat di antara rilis bertag, dan tanggal saya membacanya adalah tanggal temuan ini berlaku.
Perintah di bawah mengubah langkah satu, empat, dan enam menjadi bentuk yang bisa dijalankan.
# 1. A read-only identity. The view ClusterRole excludes Secrets.
kubectl create rolebinding kubectl-ai-view \
--clusterrole=view --serviceaccount=acme:kubectl-ai --namespace=acme
# Prove it with the kubeconfig the agent will use, not your own.
kubectl --kubeconfig ~/.kube/kubectl-ai-readonly.yaml auth can-i delete pods -n acme # expect: no
kubectl --kubeconfig ~/.kube/kubectl-ai-readonly.yaml get secret db -n acme # expect: Forbidden
# 2. Start the agent on that identity only.
kubectl-ai --kubeconfig ~/.kube/kubectl-ai-readonly.yaml
# 6. If you build from source, record which commit your findings are about.
git -C kubectl-ai log -1 --format='%h %cd'Aturan yang saya ambil dari sini: prompt konfirmasi yang bergantung pada parsing perintah adalah polisi tidur yang berguna, dan batas otorisasi yang bergantung pada cluster adalah satu-satunya hal yang pantas disebut kontrol. kubectl-ai mengerjakan yang pertama dengan baik, mendokumentasikan yang kedua dengan jujur, dan membiarkan HTTP server terbuka sampai Anda menutupnya, jadi tutup dulu sebelum hal lain.
Sumber
- GitHub: GoogleCloudPlatform/kubectl-ai, README
- GitHub: dokumentasi MCP server kubectl-ai (docs/mcp-server.md)
- GitHub: agent loop dan prompt konfirmasi kubectl-ai (pkg/agent/conversation.go)
- GitHub: classifier perintah kubectl-ai (pkg/tools/kubectl_filter.go)
- GitHub: entry point MCP server kubectl-ai (cmd/mcp.go)
- GitHub: rilis kubectl-ai v0.0.31
- Model Context Protocol: spesifikasi authorization, 2025-06-18
- Dokumentasi Kubernetes: menggunakan otorisasi RBAC
- Dokumentasi Kubernetes: memperluas kubectl dengan plugin
- Simon Willison: the lethal trifecta untuk AI agent

