1. 導(dǎo)言
絕大多數(shù)業(yè)務(wù)場景都是需要知道客戶端IP的
在k8s中運(yùn)行的業(yè)務(wù)項(xiàng)目凉唐,如何獲取到客戶端真實(shí)IP?
本文總結(jié)了通行的2種方式
要答案的直接看方式一、方式二和總結(jié)
SEO 關(guān)鍵字
nginx ingress客戶端真實(shí)ip
kubernets獲取客戶端真實(shí)ip
rke獲取客戶端真實(shí)ip
rancher獲取客戶端真實(shí)ip
本文由 www.iamle.com 流水理魚 原創(chuàng),wx公眾號同名
1.1 流量鏈路介紹
7層轉(zhuǎn)發(fā)鏈路 Client(客戶端) > Nginx > K8s Ingress(Nginx ingress)
4層轉(zhuǎn)發(fā)鏈路 Client(客戶端) > 公有云LB > K8s Ingress(Nginx ingress)
ps: 實(shí)際業(yè)務(wù)會串聯(lián)更多層級的轉(zhuǎn)發(fā)。WAF惯退、CDN、Api Gateway一般是http 7層轉(zhuǎn)發(fā)从藤,LB一般是4層tcp轉(zhuǎn)發(fā)
1.2 準(zhǔn)備whoami探針
whomai是一個(gè)go編寫的調(diào)試探針工具催跪,回顯http頭信息
在k8s中部署一個(gè)containous/whoami用來作為探針,配置好ingress公網(wǎng)和訪問夷野,這樣客戶端web訪問可以看到基本的http頭信息懊蒸,方便調(diào)試
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自行增加
客戶端web訪問,回顯http頭示例
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透傳
獲得客戶端真實(shí)IP有針對7層和針對4層兩種方式
2.1 7層http頭X-Forwarded-For透傳介紹
http工作在網(wǎng)絡(luò)第7層,http中有個(gè)X-Forwarded-For字段
大部分CDN悯搔、WAF骑丸、LB用X-Forwarded-For字段來存客戶端IP,也有用X-Real-Ip字段妒貌,cloudflare通危、百度云加速還擴(kuò)展了CF-Connecting-IP字段
標(biāo)準(zhǔn)數(shù)據(jù)為
X-Forwareded-For:Client,proxy1灌曙,proxy2菊碟,proxy3……
第一個(gè)ip是客戶端ip,后面的proxy為路過一層就加一層的ip
這里的proxy可以是WAF在刺、CDN逆害、LB、Api Gateway等
2.2 4層Proxy Protocol透傳
tcp工作在網(wǎng)絡(luò)第4層,Proxy Protocol就是在tcp中增加一個(gè)小的報(bào)頭增炭,用來存儲額外的信息
代理協(xié)議即 Proxy Protocol,是haproxy的作者Willy Tarreau于2010年開發(fā)和設(shè)計(jì)的一個(gè)Internet協(xié)議忍燥,通過為tcp添加一個(gè)很小的頭信息,來方便的傳遞客戶端信息(協(xié)議棧隙姿、源IP、目的IP厂捞、源端口输玷、目的端口等),在網(wǎng)絡(luò)情況復(fù)雜又需要獲取客戶IP時(shí)非常有用靡馁。
其本質(zhì)是在三次握手結(jié)束后由代理在連接中插入了一個(gè)攜帶了原始連接四元組信息的數(shù)據(jù)包欲鹏。
目前 proxy protocol有兩個(gè)版本,v1僅支持human-readable報(bào)頭格式(ASCIII碼)臭墨,v2需同時(shí)支持human-readable和二進(jìn)制格式赔嚎,即需要兼容v1格式
proxy protocol的接收端必須在接收到完整有效的 proxy protocol 頭部后才能開始處理連接數(shù)據(jù)。因此對于服務(wù)器的同一個(gè)監(jiān)聽端口,不存在兼容帶proxy protocol包的連接和不帶proxy protocol包的連接尤误。如果服務(wù)器接收到的第一個(gè)數(shù)據(jù)包不符合proxy protocol的格式侠畔,那么服務(wù)器會直接終止連接。
Proxy protocol是比較新的協(xié)議损晤,但目前已經(jīng)有很多軟件支持软棺,如haproxy、nginx尤勋、apache喘落、squid、mysql等等最冰,要使用proxy protocol需要兩個(gè)角色sender和receiver,sender在與receiver之間建立連接后瘦棋,會先發(fā)送一個(gè)帶有客戶信息的tcp header,因?yàn)楦牧藅cp協(xié)議頭,需receiver也支持proxy protocol暖哨,否則不能識別tcp包頭兽狭,導(dǎo)致無法成功建立連接。
nginx是從1.5.12起開始支持的
3. 方式一 X-Forwarded-For配置
適用于7層http轉(zhuǎn)發(fā)
3.1 NGINX Ingress Controller X-Forwarded-For配置
查看NGINX Ingress Controller的ConfigMaps配置文檔鹿蜀,可以找到以下配置項(xiàng)
use-forwarded-headers
如果為true箕慧,NGINX會將傳入的 X-Forwarded-* 頭傳遞給upstreams。當(dāng)NGINX位于另一個(gè)正在設(shè)置這些標(biāo)頭的 L7 proxy / load balancer 之后時(shí)茴恰,請使用此選項(xiàng)颠焦。
如果為false,NGINX會忽略傳入的 X-Forwarded-* 頭往枣,用它看到的請求信息填充它們伐庭。如果NGINX直接暴露在互聯(lián)網(wǎng)上,或者它在基于 L3/packet-based load balancer 后面分冈,并且不改變數(shù)據(jù)包中的源IP圾另,請使用此選項(xiàng)。
ps: NGINX Ingress Controller直接暴露互聯(lián)網(wǎng)也就是Edge模式不能開啟為true雕沉,否則會有偽造ip的安全問題集乔。也就是k8s有公網(wǎng)ip,直接讓客戶端訪問坡椒,本配置不要設(shè)為true扰路!
forwarded-for-header
設(shè)置標(biāo)頭字段以標(biāo)識客戶端的原始IP地址。 默認(rèn): X-Forwarded-For
ps:如果 NGINX Ingress Controller 在CDN倔叼,WAF汗唱,LB等后面,設(shè)置從頭的哪個(gè)字段獲取IP丈攒,默認(rèn)是X-Forwarded-For
這個(gè)配置應(yīng)該和use-forwarded-headers配合使用
compute-full-forwarded-for
將遠(yuǎn)程地址附加到 X-Forwarded-For 標(biāo)頭哩罪,而不是替換它授霸。 啟用此選項(xiàng)后,upstreams應(yīng)用程序?qū)⒏鶕?jù)其自己的受信任代理列表提取客戶端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作為邊緣節(jié)點(diǎn)(Edge)配置
作為Edge需要重寫remote_addr碘耳,保證了客戶端IP不會被偽造
必須:X-Forwarded-For 重寫為 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,取決于邊緣節(jié)點(diǎn)(Edge)是如何處理X-Forwarded-For字段的腹鹉。
客戶端直接連接的首個(gè)proxy節(jié)點(diǎn)都叫做邊緣節(jié)點(diǎn)(Edge)藏畅,不管是網(wǎng)關(guān)、CDN功咒、LB等只要這一層是直接接入客戶端訪問的愉阎,那么他就是一個(gè)邊緣節(jié)點(diǎn)。
不重寫-不安全的邊緣節(jié)點(diǎn)(Edge)
邊緣節(jié)點(diǎn)如果是透傳http頭中的X-Forwarded-For字段力奋,那么這個(gè)就是不安全的榜旦,客戶端可以在http中實(shí)現(xiàn)包含X-Forwarded-For字段值,這個(gè)值又被透傳了景殷。
#不安全
X-Forwareded-For:Client(Edge不重寫溅呢,只透傳),proxy1猿挚,proxy2咐旧,proxy3……
重寫-安全的邊緣節(jié)點(diǎn)(Edge)
邊緣節(jié)點(diǎn)(Edge)如果重寫remote_addr到X-Forwarded-For,那么這就是安全的绩蜻。邊緣節(jié)點(diǎn)(Edge)獲取的remote_addr就是客戶端的真實(shí)IP
#安全
X-Forwareded-For:Client(Edge獲取的remote_addr)铣墨,proxy1,proxy2办绝,proxy3……
4. 方式二 Proxy Protocol 配置實(shí)例
適用于4層tcp轉(zhuǎn)發(fā)
公有云的負(fù)載均衡LB一般都支持Proxy Protocol
查看NGINX Ingress Controller的ConfigMaps配置文檔伊约,可以找到如何配置Proxy Protocol
use-proxy-protocol
啟用或禁用roxy Protocol,以接收通過代理服務(wù)器和負(fù)載均衡器(例如HAProxy和Amazon Elastic Load Balancer(ELB))傳遞的客戶端連接(真實(shí)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,才能這么配置降淮,否則會導(dǎo)致無法鏈接
5. 總結(jié)
7層http頭X-Forwarded-For透傳
鏈路proxy有透傳X-Forwarded-For
訪問鏈路上多層proxy超埋,任意一個(gè)節(jié)點(diǎn)不支持Proxy Protocol
4層協(xié)議Proxy Protocol透傳
上下游可控都支持Proxy Protocol協(xié)議
鏈路proxy中丟失了http頭
https反向代理http(某些情況下由于Keep-alive導(dǎo)致不是每次請求都傳遞x-forword-for
應(yīng)該用那種方式?
7層用X-Forwarded-For骤肛,4層用Proxy Protocol
如果鏈路的邊緣節(jié)點(diǎn)(Edge)X-Forwarded-For字段是安全的纳本,建議用X-Forwarded-For
如果鏈路proxy全路徑都支持Proxy Protocol,那么建議用Proxy Protocol
如果有4層tcp業(yè)務(wù)應(yīng)用腋颠,那么獲取客戶端IP就的用Proxy Protocol
總之搞清楚了這2種方式的原理按照場景選擇
5. 參考
- HTTP HeadersX-Forwarded-For https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/X-Forwarded-For
- [ Proxy Protocol Introduction https://www.haproxy.com/blog/haproxy/Proxy Protocol/ ](https://www.haproxy.com/blog/haproxy/Proxy Protocol/)
- NGINX Ingress Controller ConfigMaps https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#use-forwarded-headers
- Configuring NGINX to Accept the PROXY Protocol https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/#configuring-nginx-to-accept-the-proxy-protocol
- nginx Module ngx_http_realip_module https://nginx.org/en/docs/http/ngx_http_realip_module.html
- rancher K8s Ingress Controllers https://rancher.com/docs/rke/latest/en/config-options/add-ons/ingress-controllers/
- rancher Set Up Load Balancer and Ingress Controller within Rancher https://rancher.com/docs/rancher/v2.x/en/k8s-in-rancher/load-balancers-and-ingress/
- aliyun負(fù)載均衡 如何獲取客戶端真實(shí)IP https://help.aliyun.com/document_detail/54007.html
- aliyun全站加速 獲取客戶端真實(shí)IP https://help.aliyun.com/document_detail/119658.html
- 騰訊云高防IP 獲取客戶端真實(shí)IP https://cloud.tencent.com/document/product/1014/31124
-
How does Cloudflare handle HTTP Request headers?
本文首發(fā)于流水理魚博客,如要轉(zhuǎn)載請注明出處吓笙。
歡迎關(guān)注我的公眾號:流水理魚(liushuiliyu)淑玫,全棧、云原生、團(tuán)隊(duì)管理交流絮蒿。
如果您對相關(guān)文章感興趣尊搬,也可以關(guān)注我的博客:www.iamle.com 上面有更多內(nèi)容