Kubernets中獲取客戶端真實IP總結

1. 導言

絕大多數業務場景都是須要知道客戶端IP的
在k8s中運行的業務項目,如何獲取到客戶端真實IP?
本文總結了通行的2種方式
要答案的直接看方式1、方式二和總結
SEO 關鍵字
nginx ingress客戶端真實ip
kubernets獲取客戶端真實ip
rke獲取客戶端真實ip
rancher獲取客戶端真實ip
本文由 www.iamle.com 流水理魚 原創,wx公衆號同名html

1.1 流量鏈路介紹

7層轉發鏈路 Client(客戶端) > Nginx > K8s Ingress(Nginx ingress)
4層轉發鏈路 Client(客戶端) > 公有云LB > K8s Ingress(Nginx ingress)
ps: 實際業務會串聯更多層級的轉發。WAF、CDN、Api Gateway通常是http 7層轉發,LB通常是4層tcp轉發mysql

1.2 準備whoami探針

whomai是一個go編寫的調試探針工具,回顯http頭信息
在k8s中部署一個containous/whoami用來做爲探針,配置好ingress公網和訪問,這樣客戶端web訪問能夠看到基本的http頭信息,方便調試nginx

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  namespace: default
  labels:
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - image: containous/whoami
        imagePullPolicy: Always
        name: whoami
        ports:
        - containerPort: 80
          name: 80tcp02
          protocol: TCP
      dnsPolicy: ClusterFirst
      restartPolicy: Always
EOF

ps:ingress自行增長git

客戶端web訪問,回顯http頭示例github

Hostname: whoami-65b8cc4b-6vwns
IP: 127.0.0.1
IP: 10.42.2.12
RemoteAddr: 10.42.1.0:47850
GET / HTTP/1.1
Host: whoami.iamle.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,la;q=0.5
Cookie: _ga=GA1.2.30707523.1570429261;
Dnt: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 8.8.8.8, 10.0.0.1
X-Forwarded-Host: whoami.iamle.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Original-Forwarded-For: 8.8.8.8
X-Original-Uri: /
X-Real-Ip: 8.8.8.8
X-Request-Id: 3852c9780589ffba4c1f9f2785691d5f
X-Scheme: https

2. 兩種方式 7層http頭X-Forwarded-For透傳 和 4層Proxy Protocol透傳

得到客戶端真實IP有針對7層和針對4層兩種方式web

2.1 7層http頭X-Forwarded-For透傳介紹

http工做在網絡第7層,http中有個X-Forwarded-For字段sql

大部分CDN、WAF、LB用X-Forwarded-For字段來存客戶端IP,也有用X-Real-Ip字段,cloudflare、百度雲加速還擴展了CF-Connecting-IP字段
標準數據爲apache

X-Forwareded-For:Client,proxy1,proxy2,proxy3……

第一個ip是客戶端ip,後面的proxy爲路過一層就加一層的ip
這裏的proxy能夠是WAF、CDN、LB、Api Gateway等api

2.2 4層Proxy Protocol透傳

tcp工做在網絡第4層,Proxy Protocol就是在tcp中增長一個小的報頭,用來存儲額外的信息安全

代理協議即 Proxy Protocol,是haproxy的做者Willy Tarreau於2010年開發和設計的一個Internet協議,經過爲tcp添加一個很小的頭信息,來方便的傳遞客戶端信息(協議棧、源IP、目的IP、源端口、目的端口等),在網絡狀況複雜又須要獲取客戶IP時很是有用。
其本質是在三次握手結束後由代理在鏈接中插入了一個攜帶了原始鏈接四元組信息的數據包。

目前 proxy protocol有兩個版本,v1僅支持human-readable報頭格式(ASCIII碼),v2需同時支持human-readable和二進制格式,即須要兼容v1格式
proxy protocol的接收端必須在接收到完整有效的 proxy protocol 頭部後才能開始處理鏈接數據。所以對於服務器的同一個監聽端口,不存在兼容帶proxy protocol包的鏈接和不帶proxy protocol包的鏈接。若是服務器接收到的第一個數據包不符合proxy protocol的格式,那麼服務器會直接終止鏈接。

Proxy protocol是比較新的協議,但目前已經有不少軟件支持,如haproxy、nginx、apache、squid、mysql等等,要使用proxy protocol須要兩個角色sender和receiver,sender在與receiver之間創建鏈接後,會先發送一個帶有客戶信息的tcp header,由於更改了tcp協議頭,需receiver也支持proxy protocol,不然不能識別tcp包頭,致使沒法成功創建鏈接。
nginx是從1.5.12起開始支持的

3. 方式一 X-Forwarded-For配置

適用於7層http轉發

3.1 NGINX Ingress Controller X-Forwarded-For配置

查看NGINX Ingress Controller的ConfigMaps配置文檔,能夠找到如下配置項
use-forwarded-headers
若是爲true,NGINX會將傳入的 X-Forwarded-* 頭傳遞給upstreams。當NGINX位於另外一個正在設置這些標頭的 L7 proxy / load balancer 以後時,請使用此選項。
若是爲false,NGINX會忽略傳入的 X-Forwarded-* 頭,用它看到的請求信息填充它們。若是NGINX直接暴露在互聯網上,或者它在基於 L3/packet-based load balancer 後面,而且不改變數據包中的源IP,請使用此選項。
ps: NGINX Ingress Controller直接暴露互聯網也就是Edge模式不能開啓爲true,不然會有僞造ip的安全問題。也就是k8s有公網ip,直接讓客戶端訪問,本配置不要設爲true!

forwarded-for-header
設置標頭字段以標識客戶端的原始IP地址。 默認: X-Forwarded-For
ps:若是 NGINX Ingress Controller 在CDN,WAF,LB等後面,設置從頭的哪一個字段獲取IP,默認是X-Forwarded-For
這個配置應該和use-forwarded-headers配合使用

compute-full-forwarded-for
將遠程地址附加到 X-Forwarded-For 標頭,而不是替換它。 啓用此選項後,upstreams應用程序將根據其本身的受信任代理列表提取客戶端IP

修改configmap nginx-configuration配置

kubectl -n ingress-nginx edit cm nginx-configuration

在apiVersion: v1下,kind: ConfigMap上加入

data:
  compute-full-forwarded-for: "true"
  forwarded-for-header: "X-Forwarded-For"
  use-forwarded-headers: "true"

或者直接apply附加配置

kubectl apply -f - <<EOF
apiVersion: v1
data:
  compute-full-forwarded-for: "true"
  forwarded-for-header: X-Forwarded-For
  use-forwarded-headers: "true"
kind: ConfigMap
metadata:
  labels:
    app: ingress-nginx
  name: nginx-configuration
  namespace: ingress-nginx
EOF

ps:若是nginx-configuration不在namespace ingress-nginx中就在namespace kube-system中找

3.2 Nginx做爲邊緣節點(Edge)配置

做爲Edge須要重寫remote_addr,保證了客戶端IP不會被僞造
必須:X-Forwarded-For 重寫爲 $remote_addr
非必須擴展:X-Real-IP 重寫爲 $remote_addr

upstream wwek-k8s {
    server 8.8.8.8:443;
    server 8.8.8.7:443;
    server 8.8.8.6:443;
}

map $http_upgrade $connection_upgrade {
    default Upgrade;
    ''      close;
}
server {
    if ($http_x_forwarded_proto = '') {
        set $http_x_forwarded_proto  $scheme;
   }
location / {

        proxy_set_header Host              $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port  $server_port;
        #proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-For   $remote_addr;
        proxy_set_header X-Real-IP         $remote_addr;

        proxy_pass          https://wwek-k8s;
        proxy_http_version  1.1;
        proxy_set_header    Upgrade $http_upgrade;
        proxy_set_header    Connection $connection_upgrade;
        proxy_read_timeout 900s;
        proxy_buffering off;
    }
}

3.3 X-Forwarded-For是否能夠僞造

客戶端是否能僞造IP,取決於邊緣節點(Edge)是如何處理X-Forwarded-For字段的。
客戶端直接鏈接的首個proxy節點都叫作邊緣節點(Edge),無論是網關、CDN、LB等只要這一層是直接接入客戶端訪問的,那麼他就是一個邊緣節點。

不重寫-不安全的邊緣節點(Edge)
邊緣節點若是是透傳http頭中的X-Forwarded-For字段,那麼這個就是不安全的,客戶端能夠在http中實現包含X-Forwarded-For字段值,這個值又被透傳了。

#不安全
X-Forwareded-For:Client(Edge不重寫,只透傳),proxy1,proxy2,proxy3……

重寫-安全的邊緣節點(Edge)
邊緣節點(Edge)若是重寫remote_addr到X-Forwarded-For,那麼這就是安全的。邊緣節點(Edge)獲取的remote_addr就是客戶端的真實IP

#安全
X-Forwareded-For:Client(Edge獲取的remote_addr),proxy1,proxy2,proxy3……

4. 方式二 Proxy Protocol 配置實例

適用於4層tcp轉發
公有云的負載均衡LB通常都支持Proxy Protocol

查看NGINX Ingress Controller的ConfigMaps配置文檔,能夠找到如何配置Proxy Protocol
use-proxy-protocol
啓用或禁用roxy Protocol,以接收經過代理服務器和負載均衡器(例如HAProxy和Amazon Elastic Load Balancer(ELB))傳遞的客戶端鏈接(真實IP地址)信息。

NGINX Ingress Controller 做爲receiver角色 Proxy Protocol配置

kubectl -n ingress-nginx edit cm nginx-configuration

在apiVersion: v1下,kind: ConfigMap上加入

data:
  use-proxy-protocol: "true"

或者直接apply附加配置

kubectl apply -f - <<EOF
apiVersion: v1
data:
  use-proxy-protocol: "true"
kind: ConfigMap
metadata:
  labels:
    app: ingress-nginx
  name: nginx-configuration
  namespace: ingress-nginx
EOF

ps: 注意須要上一層LB支持Proxy Protocol,才能這麼配置,不然會致使沒法連接

5. 總結

7層http頭X-Forwarded-For透傳
鏈路proxy有透傳X-Forwarded-For
訪問鏈路上多層proxy,任意一個節點不支持Proxy Protocol

4層協議Proxy Protocol透傳
上下游可控都支持Proxy Protocol協議
鏈路proxy中丟失了http頭
https反向代理http(某些狀況下因爲Keep-alive致使不是每次請求都傳遞x-forword-for

應該用那種方式?
7層用X-Forwarded-For,4層用Proxy Protocol
若是鏈路的邊緣節點(Edge)X-Forwarded-For字段是安全的,建議用X-Forwarded-For
若是鏈路proxy全路徑都支持Proxy Protocol,那麼建議用Proxy Protocol
若是有4層tcp業務應用,那麼獲取客戶端IP就的用Proxy Protocol
總之搞清楚了這2種方式的原理按照場景選擇

5. 參考

本文由博客一文多發平臺 OpenWrite 發佈!

相關文章
相關標籤/搜索