部署在GKE上的Go API抛出SSL错误:无法验证第一个证书,是否遗漏了配置项?
问题核心分析
你遇到的错误本质是客户端在校验SSL证书时,无法构建完整的信任链——要么是你的证书文件缺少中间/根证书,要么是当前的K8s配置和Go服务的SSL设置出现了重复终止的冲突,导致证书不匹配。
先看你当前的配置矛盾:你的Ingress已经配置了TLS终止,但Go服务本身又通过ListenAndServeTLS在8080端口启用了SSL监听,这意味着流量会经过两次SSL处理,很容易出现证书不匹配的问题。
解决方案一:用Ingress统一处理SSL终止(推荐的K8s最佳实践)
这种方式下,Ingress负责对外的SSL加密,内部流量以HTTP方式转发到Go服务,简化配置且便于统一管理证书:
修改Go服务代码,去掉TLS监听,改用普通HTTP:
http.HandleFunc("/hello", HelloServer) err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }确保SSL证书Secret包含完整证书链
很多证书颁发机构会提供中间证书,你需要将服务器证书和中间证书拼接成完整的证书链文件,再创建Secret:# 拼接证书链:服务器证书在前,中间证书在后 cat server.crt intermediate.crt > fullchain.crt # 创建K8s Secret kubectl create secret tls test-ssl-certificate --cert=fullchain.crt --key=server.key修正Ingress配置的冗余部分
你的Ingress同时定义了backend和rules,这可能导致版本兼容问题,建议只保留rules:apiVersion: "extensions/v1beta1" kind: "Ingress" metadata: name: "test-ingress" annotations: kubernetes.io/ingress.global-static-ip-name: "test-static-ip" labels: app: "test" spec: tls: - hosts: - "test.myhost.com" secretName: "test-ssl-certificate" rules: - host: "test.myhost.com" http: paths: - path: "/*" backend: serviceName: "test-service-nodeport" servicePort: 8080
解决方案二:让Go服务直接处理SSL(不推荐,除非有特殊需求)
如果必须让Go服务自己处理SSL,需要调整Ingress为TCP转发(跳过HTTP层的SSL处理),同时确保Go服务使用完整证书链:
更新Go服务的证书文件
把中间证书追加到server.crt末尾,确保证书链完整:cat intermediate.crt >> server.crt然后重新构建镜像,确保容器内的
server.crt是完整链。修改Ingress配置为TCP转发
以NGINX Ingress为例,需要先在Ingress Controller的ConfigMap中配置TCP端口映射:apiVersion: v1 kind: ConfigMap metadata: name: nginx-ingress-tcp namespace: kube-system data: 8080: "default/test-service-nodeport:8080"然后更新Ingress Controller的Deployment,挂载这个ConfigMap并启动时指定
--tcp-services-configmap=kube-system/nginx-ingress-tcp。最后移除Ingress中的TLS配置,因为SSL由Go服务直接处理。
验证步骤
- 重启Deployment和Ingress,确保配置生效:
kubectl rollout restart deployment test kubectl apply -f test-ingress.yaml - 用curl测试SSL验证:
如果没有证书验证错误,说明配置正确。curl -v https://test.myhost.com/hello
你的原始配置参考
Kubernetes配置:
apiVersion: "v1" kind: "ConfigMap" metadata: name: "test-config" namespace: "default" labels: app: "test" data: ENV: "DEV" --- apiVersion: "apps/v1" kind: "Deployment" metadata: name: "test" namespace: "default" labels: app: "test" spec: replicas: 1 selector: matchLabels: app: "test" template: metadata: labels: app: "test" spec: containers: - name: "test" image: "gcr.io/test-project/test:latest" env: - name: "ENV" valueFrom: configMapKeyRef: key: "ENV" name: "test-config" --- apiVersion: "extensions/v1beta1" kind: "Ingress" metadata: name: "test-ingress" annotations: kubernetes.io/ingress.global-static-ip-name: "test-static-ip" labels: app: "test" spec: tls: - hosts: - "test.myhost.com" secretName: "test-ssl-certificate" backend: serviceName: "test-service-nodeport" servicePort: 8080 rules: - host: "test.myhost.com" http: paths: - path: "/*" backend: serviceName: "test-service-nodeport" servicePort: 8080 --- kind: "Service" apiVersion: "v1" metadata: name: "test-service-nodeport" spec: selector: app: "test" ports: - protocol: "TCP" port: 8080 targetPort: 8080 type: "NodePort"
Go服务器代码:
http.HandleFunc("/hello", HelloServer) err := http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }
内容的提问来源于stack exchange,提问作者sumit

