Kubernetes (GKE) 裡 TCP Socket 連線取得正確來源(玩家) IP

Wrong Source IPs
來源 IP 都是內部 IP
這次的案例是之前曾經幫忙過的一個案子 , 一個原本是在虛擬主機上運行, 最後進行容器化並且轉到 Kubernates 上營運的遊戲產品, 這案子大都是使用 GCP 的解決方案來處理。

在這案例遇到的問題是: 在稽核玩家資訊時發現, 後台紀錄的玩家來源 IP 都是 Cluster 中 Node 的 IP, 所以需要儘速進行修正。

值得一提的是這個遊戲在玩家和伺服器間是使用 TCP Socket 連線, 所以有些和 HTTP Header X-Forwarded-For 相關的解決方案就可能不是那麼適用了, proxy protocol 也有可能是另一種解法, 只是這次的解法是從 K8S 直接有提供的方式來處理。

大概簡單介紹一下系統的架構實作:這個遊戲產品採用的是 Master version 為 1.16.9-gke.6 的 GKE 服務 (一組在 GCP 上的 Kubernetes Cluster)。 發生問題的服務是由 Service type 為 Load balancer 在最前線, 接著 request 會被導向相關的 Pod 裡, 具體細節可以從下面的 spec 看出來。

以下為隱碼過的部分示意  YAML 檔:
spec:
  clusterIP: xx.xx.xx.xx
  healthCheckNodePort: xxxx
  ports:
  - name: xxxService
    nodePort: xxxxx
    port: xxxx
    protocol: TCP
    targetPort: xxxx
  selector:
    app: xxx
    release: xxxcluster
  sessionAffinity: None
  type: LoadBalancer
遊戲是使用 Apache MINA 來開發, 取得來源 IP 的程式碼和網路上的範例類似:
String clientIP = ((InetSocketAddress)session.getRemoteAddress()).getAddress().getHostAddress();
Google 後有很多解法, 但大都不是我要的解法。最後還是得乖乖回去查 Kubernetes 官方文件, 文件內有一個地方引起我的注意, 看了一下後覺得應該可以解決這次發生的問題, 文件裡面提到 externalTrafficPolicy 的說明:
Kubernetes has a feature to preserve the client source IP. If you set service.spec.externalTrafficPolicy to the value Local, kube-proxy only proxies proxy requests to local endpoints, and does not forward traffic to other nodes. This approach preserves the original source IP address. If there are no local endpoints, packets sent to the node are dropped, so you can rely on the correct source-ip in any packet processing rules you might apply a packet that make it through to the endpoint.
只要把 externalTrafficPolicy: Local 加進去 Service 的 YAML 檔裡就可以了:
spec:
  clusterIP: xx.xx.xx.xx
  healthCheckNodePort: xxxx
  externalTrafficPolicy: Local
  ports:
  - name: xxxService
    nodePort: xxxxx
    port: xxxx
    protocol: TCP
    targetPort: xxxx
  selector:
    app: xxx
    release: xxxcluster
  sessionAffinity: None
  type: LoadBalancer

加完更新後也的確順利地讓這個案件結案。



註:具體處理還是得看架構和服務, 請先了解這個設定和原理, 確認是不是你所需要的再進行改動, 擅自更動可能會造成 Service 無法正常啟動。

留言