mirror of
https://github.com/ahmetb/kubectx.git
synced 2026-05-14 19:12:07 +00:00
Introduces a new internal/proxy package that provides a localhost HTTP reverse proxy enforcing read-only access to the Kubernetes API server. - Allows GET, HEAD, OPTIONS requests (kubectl get/describe/logs/top/watch) - Blocks POST, PUT, DELETE, PATCH with metav1.Status 405 responses - Blocks Connection: Upgrade requests (kubectl exec/cp/port-forward) - Uses client-go transport for TLS/auth to the real API server - Rewrites kubeconfig: server URL to proxy, strips auth, sets insecure-skip-tls-verify - Appends [RO] suffix to context name in rewritten kubeconfig - DEBUG=1 enables request/response logging - Comprehensive test coverage for all proxy behavior Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
122 lines
2.9 KiB
Go
122 lines
2.9 KiB
Go
package proxy
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
)
|
|
|
|
const sampleKubeconfig = `apiVersion: v1
|
|
kind: Config
|
|
clusters:
|
|
- cluster:
|
|
certificate-authority-data: dGVzdC1jYS1kYXRh
|
|
server: https://my-cluster.example.com:6443
|
|
name: my-cluster
|
|
contexts:
|
|
- context:
|
|
cluster: my-cluster
|
|
user: my-user
|
|
name: my-context
|
|
current-context: my-context
|
|
users:
|
|
- name: my-user
|
|
user:
|
|
client-certificate-data: dGVzdC1jZXJ0LWRhdGE=
|
|
client-key-data: dGVzdC1rZXktZGF0YQ==
|
|
token: test-token
|
|
`
|
|
|
|
func TestRewriteKubeconfig(t *testing.T) {
|
|
result, err := RewriteKubeconfig([]byte(sampleKubeconfig), "127.0.0.1:12345")
|
|
if err != nil {
|
|
t.Fatalf("RewriteKubeconfig failed: %v", err)
|
|
}
|
|
|
|
cfg, err := clientcmd.Load(result)
|
|
if err != nil {
|
|
t.Fatalf("failed to parse rewritten kubeconfig: %v", err)
|
|
}
|
|
|
|
cluster := cfg.Clusters["my-cluster"]
|
|
if cluster == nil {
|
|
t.Fatal("cluster my-cluster not found")
|
|
}
|
|
if cluster.Server != "http://127.0.0.1:12345" {
|
|
t.Errorf("expected server http://127.0.0.1:12345, got %q", cluster.Server)
|
|
}
|
|
if !cluster.InsecureSkipTLSVerify {
|
|
t.Error("expected insecure-skip-tls-verify to be true")
|
|
}
|
|
if len(cluster.CertificateAuthorityData) != 0 {
|
|
t.Error("expected certificate-authority-data to be removed")
|
|
}
|
|
if cluster.CertificateAuthority != "" {
|
|
t.Error("expected certificate-authority to be removed")
|
|
}
|
|
|
|
user := cfg.AuthInfos["my-user"]
|
|
if user == nil {
|
|
t.Fatal("user my-user not found")
|
|
}
|
|
if len(user.ClientCertificateData) != 0 {
|
|
t.Error("expected client-certificate-data to be removed")
|
|
}
|
|
if len(user.ClientKeyData) != 0 {
|
|
t.Error("expected client-key-data to be removed")
|
|
}
|
|
if user.Token != "" {
|
|
t.Error("expected token to be removed")
|
|
}
|
|
|
|
if _, ok := cfg.Contexts["my-context[RO]"]; !ok {
|
|
t.Error("expected context to be renamed to \"my-context[RO]\"")
|
|
}
|
|
if _, ok := cfg.Contexts["my-context"]; ok {
|
|
t.Error("expected original context name to be removed")
|
|
}
|
|
if cfg.CurrentContext != "my-context[RO]" {
|
|
t.Errorf("expected current-context to be \"my-context[RO]\", got %q", cfg.CurrentContext)
|
|
}
|
|
}
|
|
|
|
const execKubeconfig = `apiVersion: v1
|
|
kind: Config
|
|
clusters:
|
|
- cluster:
|
|
server: https://my-cluster.example.com:6443
|
|
name: my-cluster
|
|
contexts:
|
|
- context:
|
|
cluster: my-cluster
|
|
user: my-user
|
|
name: my-context
|
|
current-context: my-context
|
|
users:
|
|
- name: my-user
|
|
user:
|
|
exec:
|
|
apiVersion: client.authentication.k8s.io/v1beta1
|
|
command: gke-gcloud-auth-plugin
|
|
`
|
|
|
|
func TestRewriteKubeconfig_ExecPlugin(t *testing.T) {
|
|
result, err := RewriteKubeconfig([]byte(execKubeconfig), "127.0.0.1:54321")
|
|
if err != nil {
|
|
t.Fatalf("RewriteKubeconfig failed: %v", err)
|
|
}
|
|
|
|
cfg, err := clientcmd.Load(result)
|
|
if err != nil {
|
|
t.Fatalf("failed to parse rewritten kubeconfig: %v", err)
|
|
}
|
|
|
|
user := cfg.AuthInfos["my-user"]
|
|
if user == nil {
|
|
t.Fatal("user my-user not found")
|
|
}
|
|
if user.Exec != nil {
|
|
t.Error("expected exec plugin config to be removed")
|
|
}
|
|
}
|