Linux TPROXY
利用iptables TPROXY target,可以在skb進(jìn)入到協(xié)議棧之前孝情,將skb關(guān)聯(lián)到一個(gè)本地監(jiān)聽的socket蚤蔓,并且設(shè)置skb的fwmark页畦。可以利用fwmark配置高級(jí)路由功能將非本地流量送到本地lo interface焊唬,從而能進(jìn)入本地協(xié)議棧的處理。skb隨后通過ip_rcv進(jìn)入本地協(xié)議棧處理后看靠,可以直接利用已關(guān)聯(lián)的socket進(jìn)行處理赶促,而不需像普通的處理流程那樣,使用skb中的tcp 4-tuples來查找listening socket或者established socket挟炬。這樣可以把去往任意目的IP和目的端口的skb鸥滨,關(guān)聯(lián)到本地代理程序監(jiān)聽的socket上。本地代理程序通過accept返回的socket中包含的local address和local port仍然為原始連接的信息谤祖。關(guān)聯(lián)到哪個(gè)本地監(jiān)聽的socket婿滓,可以通過on-port和on-ip option來指定,并且本地監(jiān)聽的socket需要設(shè)置IP_TRANSPARENT option泊脐,否則TPROXY target會(huì)當(dāng)做找不到指定的本地socket空幻,將skb丟棄。
使用TPROXY的三個(gè)步驟:
- 配置代理程序容客,確定程序監(jiān)聽的IP地址和端口秕铛,確保監(jiān)聽socket已打開IP_TRANSPARENT選項(xiàng)。這里程序監(jiān)聽的IP地址可以是
A:0.0.0.0
B: 127.0.0.1
C: 任意本地接口的IP地址
D: 其它IP地址
選擇使用哪種IP來監(jiān)聽缩挑,都是可以的但两,只要TPROXY target配置中的on-ip配置匹配就好了。
- 配置iptables規(guī)則供置,將需要透明代理的流量使用TPROXY target谨湘。使用on-port,on-ip參數(shù),指定透明代理監(jiān)聽的socket紧阔;使用--tproxy-mark設(shè)置使用透明代理流量的fwmark坊罢。
- 配置高級(jí)路由,將使用透明代理的流量送到本地lo接口擅耽。
FAQ
Question1:已匹配iptables規(guī)則活孩,使用TPROXY target的報(bào)文,還會(huì)繼續(xù)匹配后續(xù)規(guī)則么乖仇?
Answer1:不會(huì)憾儒。TPROXY target返回NF_DROP(報(bào)文不能匹配找到本地已設(shè)置IP_TRANSPARENT option的established或者listening socket)或者NF_ACCEPT(報(bào)文可以匹配找到本地已設(shè)置IP_TRANSPARENT option的established或者listening socket)
Question2:TPROXY target對(duì)已建立連接的報(bào)文生效么?
Answer2:生效乃沙。TPROXY target checks whether there is an established socket for the incoming skb (using the tcp 4-tuples in the skb to search the established sockets), if there is a such socket and the socket has set IP_TRANSPARENT, it sets the fwmark for the skb and associates the skb with the socket and return NF_ACCEPT. If no established socket found, it looks for listening socket using laddr and lport. It checks the configured ip address with --on-ip option, the dev ip, and the destination ip of the skb one by one, and the first non-zero one will be used as the laddr. The port configured with --on-port option will be used as lport, if not configured, the destination port of the skb is used as lport. If a listening socket is matched, and the socket has enabled IP_TRANSPARENT, it sets the mark/mask for the skb and associates the skb with the socket and returns NF_ACCEPT. Otherwise it returns NF_DROP.
Question3:既然TPROXY target對(duì)已經(jīng)建立連接的報(bào)文也生效起趾,那么Linux kernel文檔https://www.kernel.org/doc/Documentation/networking/tproxy.txt中,下面這段配置是必須的么警儒?
iptables -t mangle -N DIVERT
//代表這個(gè)pacekt屬于本地的某個(gè)socket則直接路由到lo是完全正常的训裆,性能也高
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT
Answer3:不是必須的,但加些配置性能稍微會(huì)提高些蜀铲。對(duì)于可以匹配本地Established的報(bào)文缭保,設(shè)置fwmark,配合高級(jí)路由配置確保這些報(bào)文在路由結(jié)束時(shí)可以送到本地的lo接口蝙茶。這些報(bào)文不會(huì)再經(jīng)過TPROXY target處理艺骂。(MARK target性能會(huì)略高于TPROXY target)
另外這個(gè)配置在調(diào)試過程中也會(huì)更安全一些,對(duì)于報(bào)文屬于某個(gè)已經(jīng)建立的連接(包括到本地的連接隆夯,如ssh)將直接accept钳恕,不會(huì)走到TPROXY的規(guī)則。比如通過ssh登錄到一臺(tái)機(jī)器增加規(guī)則使用TPROXY target蹄衷,但是配置時(shí)忘記exclude本地流量忧额,這樣包括ssh到這臺(tái)機(jī)器的報(bào)文也將使用TPROXY target,而TPROXY target發(fā)現(xiàn)ssh的報(bào)文能匹配上本地established的socket愧口,但是該socket未設(shè)置IP_TRANSPARENT睦番,則會(huì)返回NF_DROP,導(dǎo)致ssh連接斷開。