如何利用iptables重定向已建立的连接并触发‘连接被拒绝’错误?
兄弟,我明白你遇到的问题了——NAT规则默认只对新连接的第一个SYN包生效,已经建立的连接因为被iptables的连接跟踪(conntrack)记录了NAT映射,后续的包会直接跳过PREROUTING链的DNAT规则,所以你的重定向对它们不起作用。咱们一步步解决这个问题:
核心问题分析
你用的DNAT规则只会在连接初始化阶段(NEW状态)被匹配一次,连接一旦建立,conntrack就会记住这个NAT映射关系,后续所有包都会直接套用这个记录,根本不会再走PREROUTING的DNAT规则。而你加的NOTRACK规则对已存在的连接完全无效,因为这些连接已经被conntrack跟踪了,NOTRACK只能影响还没被跟踪的新包。
解决方案一:直接拒绝所有流量(最直接)
如果你只是想要让所有发往1986端口的流量(包括已建立的连接)触发“连接被拒绝”,其实不用绕DNAT的弯路,直接用REJECT目标就能实现,而且对所有状态的包都生效:
- 先清除之前的相关规则:
iptables -t nat -D PREROUTING -p tcp -m tcp --dport 1986 -j DNAT --to-destination :9 iptables -t raw -D PREROUTING -p tcp --dport 1986 -m state --state ESTABLISHED,RELATED,NEW,INVALID -j NOTRACK
- 添加拒绝规则:
# 对所有发往1986端口的TCP包发送TCP RST,触发“连接被拒绝” iptables -I INPUT -p tcp --dport 1986 -j REJECT --reject-with tcp-reset
这个规则会匹配所有状态的TCP包(不管是NEW还是ESTABLISHED),直接返回RST包,对方立刻会收到“连接被拒绝”的错误提示。
解决方案二:强制DNAT对所有包生效(如果你坚持用重定向到9的方式)
如果你一定要通过DNAT到9端口来触发拒绝(比如模拟某个端口不可达的场景),需要做两件事:
- 清除已有的连接跟踪记录:
已建立的连接的NAT映射已经存在conntrack里,必须先删掉这些记录,让后续的包重新匹配规则:
# 安装conntrack工具(如果没装的话) # Debian/Ubuntu: apt install conntrack # RHEL/CentOS: yum install conntrack-tools # 删除所有发往1986端口的TCP连接跟踪条目 conntrack -D -p tcp --dport 1986
- 配置NOTRACK让所有包都走DNAT规则:
设置NOTRACK规则,让发往1986端口的所有TCP包都不被连接跟踪,这样每个包都会经过PREROUTING的DNAT规则:
# 清空raw表的PREROUTING链之前的规则 iptables -t raw -F PREROUTING # 对所有发往1986端口的TCP包禁用连接跟踪 iptables -t raw -I PREROUTING -p tcp --dport 1986 -j NOTRACK # 添加DNAT规则,把1986重定向到9 iptables -t nat -I PREROUTING -p tcp --dport 1986 -j DNAT --to-destination :9
这样一来,不管是新连接还是已建立连接的后续包,都会被DNAT到9端口,如果9端口没有服务监听,系统就会返回RST包,触发“连接被拒绝”的错误。
为什么你之前的NOTRACK规则没用?
你之前的规则包含了ESTABLISHED,RELATED等状态,但NOTRACK只能作用于还没被conntrack跟踪的包,已经建立的连接的包已经被标记为ESTABLISHED,conntrack已经在跟踪它们了,所以NOTRACK对这些包完全无效。必须先删除这些连接的跟踪记录,再设置NOTRACK规则才能生效。
备注:内容来源于stack exchange,提问作者William Holland

