iptables redirect 劫持跳转引起 Go 服务故障
???? 這是一個(gè)很有趣的事情。由于流量突增臨時(shí)擴(kuò)充多個(gè)node部署服務(wù),但遇到一個(gè)問(wèn)題全量接口調(diào)用失敗總是返回?zé)o關(guān)的返回結(jié)果。簡(jiǎn)單說(shuō)在服務(wù)里本調(diào)用其他服務(wù)接口,返回的結(jié)果莫名其妙。
由于出問(wèn)題節(jié)點(diǎn)已經(jīng)被修復(fù),所以該問(wèn)題是在虛擬機(jī)里重現(xiàn)的。
排查問(wèn)題
首先確認(rèn)其他接口方是否收到該請(qǐng)求,日志沒(méi)有里沒(méi)有請(qǐng)求,考慮到是自研封裝的golang web框架可能會(huì)有問(wèn)題,所以調(diào)用tcpdump抓包,結(jié)果看不到請(qǐng)求報(bào)文。
那么可以確認(rèn)請(qǐng)求包沒(méi)有來(lái)這臺(tái)服務(wù)器上,通常來(lái)說(shuō)這類請(qǐng)求大多出現(xiàn)在dns解析錯(cuò)誤引起的。
在本機(jī)dig dns解析無(wú)異常,由于請(qǐng)求構(gòu)建起來(lái)有些麻煩,所以沒(méi)有單獨(dú)拿到curl下測(cè)試。但通過(guò) lsof 和 netstat 可以看到已建立連接是正常的解析ip,但是對(duì)端確實(shí)沒(méi)有收到該請(qǐng)求。
重點(diǎn),客戶端可以看到已經(jīng)建立的連接,但是服務(wù)端看不到。
//?xiaorui.ccpusher??10902?root????3u??IPv4?314336???????0t0???????TCP?192.168.124.13:54504->123.56.223.52:80?(ESTABLISHED)通過(guò)strace是可以看到服務(wù)請(qǐng)求過(guò)程中所涉及到的系統(tǒng)調(diào)用。
先是dns請(qǐng)求,當(dāng)開(kāi)啟dns緩存服務(wù)nscd時(shí),程序里的域名解析不是直接連接resolver.conf的nameserver地址,而是直接跟nscd socket通信,nscd作為緩存服務(wù)有個(gè)名為hosts的持久化db。當(dāng)nscd無(wú)域名的緩存時(shí)會(huì)跟nameserver進(jìn)行udp請(qǐng)求。如果無(wú)nscd服務(wù),那么strace可以看到nameserver建連及解析過(guò)程。
總之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的數(shù)據(jù)請(qǐng)求,connect ip過(guò)程沒(méi)問(wèn)題,發(fā)送的請(qǐng)求體也是沒(méi)問(wèn)題的,但返回值有問(wèn)題。
//?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:?... ...大疑問(wèn)?
奇怪了。。。
那么再嘗試使用tcpdump來(lái)抓包。每次請(qǐng)求時(shí)都會(huì)跟127.0.0.1:80建連,請(qǐng)求體也會(huì)轉(zhuǎn)到127.0.0.1:80上。這類情況很像是做了端口劫持跳轉(zhuǎn)。
在iptables里發(fā)現(xiàn)了redirect跳轉(zhuǎn)。所有output請(qǐng)求會(huì)轉(zhuǎn)到sidecar_outbound自定義鏈,在sidecar自定義鏈中又把目標(biāo)地址中80的請(qǐng)求轉(zhuǎn)到本地的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請(qǐng)求?sidecar唄… 現(xiàn)在很火的istio service mesh就是使用iptables redirect方法劫持?jǐn)?shù)據(jù)到envoy里,這樣減少了用戶對(duì)微服務(wù)的使用成本。把服務(wù)發(fā)現(xiàn)、負(fù)載均衡、熔斷器/限頻器、追蹤等等都放在sidecar里,用戶只需要關(guān)注自己的業(yè)務(wù)就可以了。
那為啥這回sidecar不能正確轉(zhuǎn)發(fā)了?
因?yàn)樵搒idecar是半成品,開(kāi)發(fā)這玩意的人跑路了,然后策略依舊存在。
如何測(cè)試?
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總結(jié)
通過(guò)strace和lsof都不好分析到問(wèn)題,而tcpdump是可以的。現(xiàn)在想想其實(shí)通過(guò)netstat也是可以發(fā)現(xiàn)問(wèn)題的,奈何在使用netstat時(shí)加入了pid過(guò)濾。
記得前段時(shí)間一個(gè)同事出現(xiàn)過(guò)域名拼寫(xiě)錯(cuò)誤引起的問(wèn)題,這哥們一出問(wèn)題就懷疑是不是 go web 問(wèn)題,再就是懷疑到 golang 本身,最后都懷疑到操作系統(tǒng)。???? 所以說(shuō)要冷靜,別瞎想….
????長(zhǎng)按圖片打賞芮神
總結(jié)
以上是生活随笔為你收集整理的iptables redirect 劫持跳转引起 Go 服务故障的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 编写与优化 Go 代码(一)
- 下一篇: 图文结合,白话 Go 的垃圾回收原理