如何获取kubectl SSL密钥供Wireshark解密K8s API TLS流量
官方发布的标准kubectl二进制默认不支持通过SSLKEYLOGFILE环境变量输出TLS会话密钥。
kubectl基于Go语言标准库crypto/tls实现TLS能力,Go标准库在1.23版本才正式加入对SSLKEYLOGFILE的原生支持,且截止Kubernetes 1.31版本,官方发布的kubectl二进制并未开启该特性的编译配置,因此直接设置环境变量无法拿到会话密钥是正常现象。
注意:针对依赖系统OpenSSL的程序设计的LD_PRELOAD类密钥抓取工具(如通用sslkeylog注入库),对静态编译的Go语言kubectl二进制无效,无需尝试。
方案1:自行编译带SSL密钥日志支持的kubectl
拉取对应版本的Kubernetes源码,优先使用Go 1.23及以上版本编译kubectl,编译时无需修改代码,产出的二进制在运行时只要设置SSLKEYLOGFILE环境变量指向可写文件路径,就会自动把所有TLS会话的主密钥写入该文件。把这个密钥文件路径配置到Wireshark的TLS协议设置项中,正常抓包即可完成解密。
如果使用低于1.23的Go版本编译,只需要在源码中给kubectl的TLS配置结构体增加KeyLogWriter字段,指向SSLKEYLOGFILE环境变量对应的文件句柄,也能实现同样的密钥输出效果,仅需修改少量源码。
针对exec -it场景的提示:该命令的交互流量基于长连接承载,旧版本用SPDY协议、1.22及以上版本默认用WebSocket协议,TLS解密完成后需要在Wireshark中启用对应协议的解析器,才能看到完整的命令交互明文。方案2:通过HTTPS中间人代理直接获取明文流量
不需要修改kubectl二进制,在本地启动支持HTTPS中间人解密的代理工具(如mitmproxy、Charles等),完成代理根证书的本地信任配置后,通过任意一种方式让kubectl走代理:- 运行kubectl前设置
HTTPS_PROXY=https://127.0.0.1:<代理监听端口>环境变量 - 给kubectl命令追加
--proxy=https://127.0.0.1:<代理监听端口>参数
如果不想配置证书信任,可以临时给kubectl加--insecure-skip-tls-verify参数跳过证书校验,该参数仅适合测试环境使用,不要在生产场景添加。此时代理端会直接拿到所有kubectl和API服务器之间的明文流量,包括exec命令的全量交互内容,不需要额外用Wireshark解密。
- 运行kubectl前设置
方案3:用kubectl内置日志输出请求内容(非链路抓包)
如果不需要严格的链路层原始数据包,只需要查看exec对应的请求和交互内容,可以直接给kubectl命令加最高等级的verbose参数,所有请求头、请求体、响应内容都会打印到标准错误输出:kubectl exec -it <目标Pod名> -v=10 -- <要执行的命令>该方法不需要额外工具,但是输出为调试日志格式,不是标准pcap包结构。
方案4:导入API服务器私钥解密(局限性大)
如果你持有测试集群API服务器的TLS证书私钥,且API服务器未强制使用TLS 1.3协议、未开启前向保密(PFS)密码套件,可以直接把API服务器的私钥导入Wireshark的TLS配置中解密流量。但目前绝大多数集群默认都会开启TLS 1.3和PFS密码套件,该方法基本无法在正常配置的集群上生效,仅适用于特意关闭相关安全特性的自建测试环境。
内容的提问来源于stack exchange,提问作者Babak

