mirror of
https://github.com/ahmetb/kubectx.git
synced 2026-05-15 11:42:04 +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>
52 lines
1.5 KiB
Go
52 lines
1.5 KiB
Go
package proxy
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
|
)
|
|
|
|
// RewriteKubeconfig takes minified kubeconfig bytes and rewrites them so that:
|
|
// - The cluster server URL points to the local proxy address (plain HTTP).
|
|
// - insecure-skip-tls-verify is set (needed for plain HTTP).
|
|
// - Certificate authority data is removed.
|
|
// - User auth fields (client certs, tokens, exec, auth-provider) are removed
|
|
// since the proxy handles authentication to the real API server.
|
|
func RewriteKubeconfig(data []byte, proxyAddr string) ([]byte, error) {
|
|
cfg, err := clientcmd.Load(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse kubeconfig: %w", err)
|
|
}
|
|
|
|
for _, cluster := range cfg.Clusters {
|
|
cluster.Server = "http://" + proxyAddr
|
|
cluster.InsecureSkipTLSVerify = true
|
|
cluster.CertificateAuthority = ""
|
|
cluster.CertificateAuthorityData = nil
|
|
}
|
|
|
|
for name := range cfg.AuthInfos {
|
|
cfg.AuthInfos[name] = &clientcmdapi.AuthInfo{}
|
|
}
|
|
|
|
// Rename contexts with [RO] suffix to indicate readonly mode.
|
|
renames := make(map[string]string, len(cfg.Contexts))
|
|
for name := range cfg.Contexts {
|
|
renames[name] = name + "[RO]"
|
|
}
|
|
for old, roName := range renames {
|
|
cfg.Contexts[roName] = cfg.Contexts[old]
|
|
delete(cfg.Contexts, old)
|
|
if cfg.CurrentContext == old {
|
|
cfg.CurrentContext = roName
|
|
}
|
|
}
|
|
|
|
out, err := clientcmd.Write(*cfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to serialize kubeconfig: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|