通过 Istio 入口网关的 TLS 握手失败 (tlsMode=passthrough)

TLS handshake through Istio ingress gateway fails (tlsMode=passthrough)

从外部客户端到 Kubernetes 集群内服务器的 TLS 握手失败。这是关于理解为什么。

我已经配置了一个 Istio 入口网关以通过在端口 15433 上接收到的 TLS,并将其路由到端口 433 上的服务器。

当客户端尝试 TLS 握手时,入口网关日志显示 activity,但服务器日志和 istio-proxy 日志均不显示。

TLS 客户端:

openssl s_client \ 
        -connect [redacted]-[redacted].us-west-2.elb.amazonaws.com:15443 \ 
        -servername myservice.mynamespace \ 
        -CAfile /path/to/ca.cert \ 
        -cert /path/to/cert.pem \ 
        -key /path/to/cert.key <<< "Q"

日志

CONNECTED(00000006) 
140090868934296:error:140790E5:SSL routines:ssl23_write:ssl handshake failure:s23_lib.c:177: 
--- 
no peer certificate available 
--- 
No client certificate CA names sent 
--- 
SSL handshake has read 0 bytes and written 298 bytes 
--- 
New, (NONE), Cipher is (NONE) 
Secure Renegotiation IS NOT supported 
Compression: NONE 
Expansion: NONE 
No ALPN negotiated 
SSL-Session: 
    Protocol  : TLSv1.2 
    Cipher    : 0000 
    Session-ID:  
    Session-ID-ctx:  
    Master-Key:  
    Key-Arg   : None 
    PSK identity: None 
    PSK identity hint: None 
    SRP username: None 
    Start Time: 1600987862 
    Timeout   : 300 (sec) 
    Verify return code: 0 (ok) 

Istio 入口网关日志:

"- - -" 0 - "-" "-" 298 0 1069 - "-" "-" "-" "-" "192.168.101.136:443" outbound|443||myservice.mynamespace.svc.cluster.local 192.168.115.141:42350 192.168.115.141:15443 192.168.125.206:23298 myservice.mynamespace - 

其中 192.168.101.136 是 myservice pod 的 IP,192.168.115.141 是 ingressgateway pod 的 IP。

根据 IP,这意味着客户端连接已到达网关,网关似乎已应用虚拟服务路由并记录它正在将其转发到 pod。看起来很正常,除了 pod 上的 istio-proxy 没有显示 activity 也没有服务器日志(尽管服务器不记录传输层发生的事情)。

据我所知,服务器已正确配置为 TLS,因为以下端口转发的 TLS 握手成功:

kubectl port-forward -n mynamespace service/myservice 4430:443 &
openssl s_client \
        -connect localhost:4430 \
        -CAfile /path/to/ca.cert \ 
        -cert /path/to/cert.pem \ 
        -key /path/to/cert.key <<< "Q"
# I get back a TLS session ID, looks good.

所以这表明 istio 的网关或虚拟服务配置存在问题。

网关:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: mygateway
  namespace: mynamespace
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 15443
        name: tls-passthrough
        protocol: TLS
      tls:
        mode: PASSTHROUGH
      hosts:
      - "*"

虚拟服务:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: myservice
  namespace: mynamespace
spec:
  hosts:
  - "*" 
  gateways:
  - mygateway
  tls:
    - match:
      - port: 15443
        sniHosts:
        - myservice.mynamespace
      route:
      - destination:
          host: myservice
          port:
            number: 443

Kubernetes 服务:

apiVersion: v1
kind: Service
metadata:
  name: myservice
  namespace: mynamespace
  labels:
    app: myservice
spec:
  selector:
    app: myservice
  ports:
    - protocol: TCP
      port: 443
      targetPort: 443
      name: grpc-svc

更新: 实际上,来自客户端的 TLS 流量确实到达了服务器 pod,我已经通过在服务器 pod 上执行 tcpdump port 443 并在我 运行 openssl s_client 命令时看到数据包来确认这一点。不清楚为什么 pod 上的 istio-proxy 没有显示这个,这并不能解释为什么握手失败。

我注意到了其他事情。将 -msg 标志传递给 openssl s_client,我没有看到任何返回,在“>>>”之后没有“<<<”,但 tcpdump 显示服务器 pod 将数据包发送回网关。

我的配置中有 2 个错误:

  • 在我的 Kubernetes 服务的配置中。不幸的是,我的 TCP 端口 443 的名称是 grpc-svc,这会破坏 TLS 直通。将此端口重命名为 tcp-svc 即可解决问题。

  • 我不应该使用入口端口 15443,它似乎是为其他东西保留的。在入口网关上打开另一个端口 9444,然后完全按照我在问题中配置端口 15443 的方式在网关上配置端口 9444(即 TLS 直通配置),然后将虚拟服务配置为路由 9444,就像我配置虚拟服务一样我的问题是 15433 的路线。

同时执行这两个操作允许外部客户端 openssl s_client 通过入口成功与 kubernetes 服务进行 TLS 握手。