iptables redirect 劫持跳转引起 Go 服务故障
???? 這是一個很有趣的事情。由于流量突增臨時擴充多個node部署服務,但遇到一個問題全量接口調用失敗總是返回無關的返回結果。簡單說在服務里本調用其他服務接口,返回的結果莫名其妙。
由于出問題節點已經被修復,所以該問題是在虛擬機里重現的。
排查問題
首先確認其他接口方是否收到該請求,日志沒有里沒有請求,考慮到是自研封裝的golang web框架可能會有問題,所以調用tcpdump抓包,結果看不到請求報文。
那么可以確認請求包沒有來這臺服務器上,通常來說這類請求大多出現在dns解析錯誤引起的。
在本機dig dns解析無異常,由于請求構建起來有些麻煩,所以沒有單獨拿到curl下測試。但通過 lsof 和 netstat 可以看到已建立連接是正常的解析ip,但是對端確實沒有收到該請求。
重點,客戶端可以看到已經建立的連接,但是服務端看不到。
//?xiaorui.ccpusher??10902?root????3u??IPv4?314336???????0t0???????TCP?192.168.124.13:54504->123.56.223.52:80?(ESTABLISHED)通過strace是可以看到服務請求過程中所涉及到的系統調用。
先是dns請求,當開啟dns緩存服務nscd時,程序里的域名解析不是直接連接resolver.conf的nameserver地址,而是直接跟nscd socket通信,nscd作為緩存服務有個名為hosts的持久化db。當nscd無域名的緩存時會跟nameserver進行udp請求。如果無nscd服務,那么strace可以看到nameserver建連及解析過程。
總之ip的解析正確的。
//?xiaori.cc[pid?10007]?connect(3,?{sa_family=AF_LOCAL,?sun_path="/var/run/nscd/socket"},?110??<unfinished?...> [pid?10007]?sendto(3,?"\2\0\0\0\r\0\0\0\6\0\0\0hosts\0",?18,?MSG_NOSIGNAL,?NULL,?0?<unfinished?…> [pid?10007]?close(3?<unfinished?...>再是http的數據請求,connect ip過程沒問題,發送的請求體也是沒問題的,但返回值有問題。
//?xiaorui.ccfcntl(3,?F_SETFL,?O_RDWR|O_NONBLOCK)????=?0 connect(3,?{sa_family=AF_INET,?sin_port=htons(80),?sin_addr=inet_addr("123.56.223.52")},?16)?=?-1?EINPROGRESS?(Operation?now?in?progress) poll([{fd=3,?events=POLLOUT|POLLWRNORM}],?1,?0)?=?1?([{fd=3,?revents=POLLOUT|POLLWRNORM}]) getsockopt(3,?SOL_SOCKET,?SO_ERROR,?[0],?[4])?=?0 getpeername(3,?{sa_family=AF_INET,?sin_port=htons(80),?sin_addr=inet_addr("123.56.223.52")},?[16])?=?0 getsockname(3,?{sa_family=AF_INET,?sin_port=htons(54508),?sin_addr=inet_addr("192.168.124.13")},?[16])?=?0 sendto(3,?"GET?/6666?HTTP/1.1\r\nUser-Agent:?curl/7.29.0\r\nHost:?xiaorui.cc\r\nAccept:?*/*\r\n\r\n",?78,?MSG_NOSIGNAL,?NULL,?0)?=?78 poll([{fd=3,?events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}],?1,?0)?=?0?(Timeout) poll([{fd=3,?events=POLLIN}],?1,?1000)??=?1?([{fd=3,?revents=POLLIN}]) poll([{fd=3,?events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}],?1,?0)?=?1?([{fd=3,?revents=POLLIN|POLLRDNORM}]) recvfrom(3,?"HTTP/1.1?404?Not?Found\r\nServer:?... ...大疑問?
奇怪了。。。
那么再嘗試使用tcpdump來抓包。每次請求時都會跟127.0.0.1:80建連,請求體也會轉到127.0.0.1:80上。這類情況很像是做了端口劫持跳轉。
在iptables里發現了redirect跳轉。所有output請求會轉到sidecar_outbound自定義鏈,在sidecar自定義鏈中又把目標地址中80的請求轉到本地的80端口上。
//?xiaorui.ccChain?OUTPUT?(policy?ACCEPT) target?????prot?opt?source???????????????destination OUTPUT_direct??all??--??anywhere?????????????anywhere SIDECAR_OUTBOUND??tcp??--??anywhere?????????????anywhere...Chain?SIDECAR_OUTBOUND?(1?references) target?????prot?opt?source???????????????destination REDIRECT???tcp??--??anywhere?????????????123.56.223.0??????tcp?dpt:http?redir?ports?80 ...本地為啥接收劫持的http請求?sidecar唄… 現在很火的istio service mesh就是使用iptables redirect方法劫持數據到envoy里,這樣減少了用戶對微服務的使用成本。把服務發現、負載均衡、熔斷器/限頻器、追蹤等等都放在sidecar里,用戶只需要關注自己的業務就可以了。
那為啥這回sidecar不能正確轉發了?
因為該sidecar是半成品,開發這玩意的人跑路了,然后策略依舊存在。
如何測試?
iptables劫持腳本
//?xiaorui.cciptables?-t?nat?-N?SIDECAR_OUTBOUND iptables?-t?nat?-A?OUTPUT?-p?tcp?-j?SIDECAR_OUTBOUND iptables?-t?nat?-A?SIDECAR_OUTBOUND?-p?tcp?-d?123.56.0.0?--dport?80?-j?REDIRECT?--to-port?80總結
通過strace和lsof都不好分析到問題,而tcpdump是可以的。現在想想其實通過netstat也是可以發現問題的,奈何在使用netstat時加入了pid過濾。
記得前段時間一個同事出現過域名拼寫錯誤引起的問題,這哥們一出問題就懷疑是不是 go web 問題,再就是懷疑到 golang 本身,最后都懷疑到操作系統。???? 所以說要冷靜,別瞎想….
????長按圖片打賞芮神
總結
以上是生活随笔為你收集整理的iptables redirect 劫持跳转引起 Go 服务故障的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编写与优化 Go 代码(一)
- 下一篇: 图文结合,白话 Go 的垃圾回收原理