IPTables允许入站连接但不允许入站流量

时间:2017-01-24 20:02:30

标签: tcp iptables

目前正在尝试设置 iptables 以允许客户端连接到服务器以通过 TCP 收听消息流。问题是,我们希望阻止客户端在连接后发送任何消息(如果客户端在此情况下 DROP ,则可以)。

是否有办法允许客户端连接并强制执行从服务器到客户端的单向通信?

要求它完全在 iptables 内工作(没有类似软件代理的解决方案)。

1 个答案:

答案 0 :(得分:0)

这里要解决的主要问题是,我们不能在建立连接后关闭所有来自客户端的流量,因为TCP是肯定的确认协议。如果服务器没有从客户端接收到ack,它将重新传输,并最终超时。在下文中,我将假设我们正在使用IPV4。

所以我们要做的是允许建立连接,然后只允许来自客户端的确认,即不包含TCP有效负载的数据包。

不幸的是,TCP有效负载的长度未在TCP header中明确表示。我们可以尝试使用IP header中的总长度,但由于IP头和TCP头都包含可变长度选项字段这一事实很复杂,因此有许多可能的总长度没有有效载荷

由于IP选项很少被使用并且通常被过滤,所以让我们首先丢弃包含IP头中选项的所有数据包(如果你的防火墙还没有这样做)。这样做的含义将详细讨论here

为此,我们将丢弃所有流量到我们的服务器(此处为10.2.3.4:1234),其中IP头长度(IP头中字节0的4-7位)不是5:

iptables -A INPUT -p tcp -d 10.2.3.4 --dport 1234 \
  -m u32 --u32 "0>>24&0xF=6:0xF" -j DROP

这使用iptables u32模块从字节0开始从数据包中获取4个字节,右移24位,屏蔽较低的半字节,然后丢弃数据包,如果这是范围6-15。请注意,5实际上是IP标头的最小大小。

TCP选项的情况有点复杂。在建立连接时,可以使用许多不同的选项,例如,以协商窗口缩放。但是,一旦建立连接,我们唯一需要担心的是TCP时间戳和选择性确认。那么让我们建立联系:

iptables -A INPUT -p tcp -d 10.2.3.4 --dport 1234 \
  --tcp-flags SYN SYN -j ACCEPT

请注意,可以在SYN数据包中发送有效负载,因此我们不能完全满足您的要求。尽管TCP fast open确实如此,但大多数普通的TCP实现都不会这样做。如果你想减轻这种影响,你可以丢弃作为片段的SYN数据包(可以重新组合成非常大的片段),并将非片段化SYN数据包的总长度限制在合理的范围内,以允许通常的选项出现在TCP三次握手。请注意,上述规则已添加到INPUT链中,该链在 IP片段重组后处理。

好的,我们可以建立TCP连接,IP头限制为5个字(20个字节)。

但是,TCP标头可能包含选择性acks,tcp时间戳,两者或两者都不包含。让我们从没有选项的TCP标头开始。没有选项且没有有效负载的ack将包含一个5字的IP标头,后跟一个5字的TCP标头,后跟没有数据。因此,IP报头中的总长度为40.如果数据包是一个片段,它可能会隐藏后续片段中的有效负载,但由于我们正在处理在IP分段重组后处理的INPUT链,我们不会不用担心这个。

iptables -A INPUT -p tcp -d 10.2.3.4 --dport 1234 \
  -m u32 --u32 "32>>28=5 && 0&0xFFFF=40" -j ACCEPT

IP头是20个字节,数据偏移半字节是字节12,所以从字节32 = 20 + 12开始取4个字节,我们将半字节向下移动并将其比较为5,然后确保IP报头的字0的字节2和3的总长度为40。

如果TCP标头中有TCP时间戳,则TCP标头中将有另外12个字节(3个字)。我们可以以类似的方式接受:

iptables -A INPUT -p tcp -d 10.2.3.4 --dport 1234 \
  -m u32 --u32 "32>>28=8 && 0&0xFFFF=52" -j ACCEPT

我会把它作为练习让读者找出其他组合。 (注意,处理选择性ack是几种情况,因为可以有1-4个选择性ack块,或1-3个带时间戳的选择性ack块。)

免责声明:我实际上并没有尝试过,所以如果有错字或者我忽视某些事情,我会道歉。我相信策略是合理的,如果有任何错误或遗漏,请告诉我,我会纠正。