Iptables:将传出流量与 conntrack 和所有者相匹配。适用于奇怪的水滴

Iptables:将传出流量与 conntrack 和所有者相匹配。适用于奇怪的水滴

在我的 iptables 脚本中,我一直在尝试编写尽可能细粒度的规则。我限制哪些用户可以使用哪些服务,部分是为了安全,部分是作为学习练习。

在运行 3.6.2 内核的 D​​ebian 6.0.6 上使用 iptables v1.4.16.2。

然而我遇到了一个我不太明白的问题......

所有用户的传出端口

这工作得很好。我没有任何通用的状态跟踪规则。

## 传出端口 81
$IPTABLES -A 输出 -p tcp --dport 81 -m conntrack --ctstate 新,已建立 -j 接受
$IPTABLES -A 输入 -p tcp --sport 81 -s $MYIP -m conntrack --ctstate 已建立 -j 接受

与用户匹配的传出端口

## 用户帐户的传出端口 80
$IPTABLES -A 输出 --match 所有者 --uid-owner useraccount -p tcp --dport 80 -m conntrack --ctstate 新,已建立 --sport 1024:65535 -j 接受
$IPTABLES -A 输入 -p tcp --sport 80 --dport 1024:65535 -d $MYIP -m conntrack --ctstate 已建立 -j 接受

这仅允许帐户“useraccount”使用端口 80,但 TCP 流量的此类规则存在问题。

## 默认传出日志+阻止规则
$IPTABLES -A 输出 -j 日志 --log-prefix "BAD OUTGOING" --log-ip-options --log-tcp-options --log-uid
$IPTABLES -A 输出 -j 丢弃

问题

上面的工作,用户“useraccount”可以完美地获取文件。系统上的任何其他用户都无法与端口 80 建立传出连接。

useraccount@host:$ wget http://cachefly.cachefly.net/10mb.test

但上面的 wget 在我的系统日志中留下了 x7 删除的条目:

10 月 18 日 02:00:35 xxxx 内核:错误传出 IN= OUT=eth0 SRC=xx.xx.xx.xx DST=205.234.175.175 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=12170 DF PROTO=TCP SPT=37792 DPT=80 SEQ=164520678 ACK=3997126942 WINDOW=979 RES=0x00 ACK URGP=0  

对于 UDP 流量的类似规则,我没有得到这些丢弃信息。我已经制定了限制哪些用户可以发出 DNS 请求的规则。

丢弃的传出 ACK 数据包似乎来自 root 帐户(URGP=0),我不明白。即使我将用户帐户交换为root。

我相信 ACK 数据包被归类为新数据包,因为 conntrack 在 3 次握手的第 3 步之后开始跟踪连接,但为什么会被丢弃?

可以安全地忽略这些掉落吗?

编辑

所以我经常看到这样的规则,这些规则对我来说很有效:

$IPTABLES -A 输出 -s $MYIP -p tcp -m tcp --dport 80 -m state --state 新,已建立 -j 接受
$IPTABLES -A 输入 -p tcp -m tcp --sport 80 -d $MYIP -m 状态 --state 已建立 -j 接受

我将“-m state --state”替换为“-m conntrack --ctstate”,因为状态匹配显然已过时。

拥有通用状态跟踪规则是最佳实践吗?上述规则不被认为是正确的吗?

为了严格控制传出用户连接,这样的东西会更好吗?

$IPTABLES -A 输入 -m conntrack --ctstate 已建立 -j 接受
$IPTABLES -A 输出 -m conntrack --ctstate 已建立 -j 接受

$IPTABLES -A 输出 -p tcp --dport 80 -s $SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m 所有者 --uid-owner useraccount -j ACCEPT

$IPTABLES -A 输出 -p tcp --dport 80 -s $SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m 所有者 --uid-owner otheraccount -j ACCEPT

答案1

长话短说,ACK 是在套接字不属于任何人时发送的。允许属于用户套接字x发起的连接的数据包,而不是允许属于用户的套接字的数据包x

故事比较长。

要理解这个问题,有助于理解wgetHTTP 请求的一般工作方式。

wget http://cachefly.cachefly.net/10mb.test

wget建立到 的 TCP 连接,一旦建立,就会在 HTTP 协议中发送一个请求,内容为:“请将( )cachefly.cachefly.net的内容发送给我,顺便说一下,完成后请不要关闭连接 ( )。原因这样做是因为,如果服务器回复同一 IP 地址上的 URL 重定向,它可以重用该连接。/10mb.testGET /10mb.test HTTP/1.1Connection: Keep-alive

现在服务器可以回复“您请求的数据来了,注意它有 10MB 大 ( Content-Length: 10485760),是的,好的,我将保持连接打开”。或者,如果它不知道数据的大小,“这是数据,抱歉,我无法保持连接打开,但我会告诉您何时可以通过关闭我的连接端来停止下载数据”。

在上面的 URL 中,我们处于第一种情况。

因此,一旦wget获得响应的标头,它就知道一旦下载了 10MB 的数据,它的工作就完成了。

基本上,wget就是读取数据直到收到 10MB 并退出。但到那时,还有更多工作要做。服务器呢?已被告知保持连接打开。

退出之前,wget关闭(close系统调用)套接字的文件描述符。此时,close系统完成对服务器发送的数据的确认,并发送一条消息FIN表示:“我不会再发送任何数据”。然后close返回并wget退出。不再有与 TCP 连接关联的套接字(至少不再由任何用户拥有)。然而它还没有完成。收到该消息后FIN,HTTP 服务器会看到文件结尾当读取来自客户端的下一个请求时。在 HTTP 中,这意味着“不再请求,我将结束”。因此它也发送 FIN,表示“我也不会发送任何内容,该连接即将消失”。

收到 FIN 后,客户端发送“ACK”。但是,那时,wget它早已不复存在,因此 ACK 不是来自任何用户。这就是它被防火墙阻止的原因。因为服务器没有收到 ACK,所以它会一遍又一遍地发送 FIN,直到放弃,您会看到更多丢弃的 ACK。这也意味着通过丢弃这些 ACK,您将不必要地使用服务器的资源(需要将套接字维持在 LAST-ACK 状态)相当长的一段时间。

如果客户端未请求“Keep-alive”或服务器未回复“Keep-alive”,则行为会有所不同。

正如已经提到的,如果您使用连接跟踪器,您要做的就是让处于 ESTABLISHED 和 RELATED 状态的每个数据包通过,并且只担心数据NEW包。

如果您允许NEW来自 user 的数据包x但不允许来自 user 的数据包y,则用户建立的连接的其他数据包x将通过,并且因为用户无法建立连接y(因为我们阻止了NEW将建立连接的数据包),不会有任何用户y连接数据包经过。

答案2

这仅允许帐户“useraccount”使用端口 80

- 好吧,至少你所展示的规则实际上并不意味着这一点。

还有一个建议空间 - 不要对 ESTABLISHED 流进行用户检查,而只对 NEW 进行检查。在检查传入 ESTABLISHED 时,我也没有看到检查源端口的意义,它的端口有什么区别,它已经​​从 conntrack 的 PoV 处于 ESTABLISHED 状态。防火墙应该尽可能简单但高效,所以奥卡姆剃刀方法是最合适的。

相关内容