arc*_*rcX 11 linux security iptables
在我的 iptables 脚本中,我一直在尝试编写尽可能细粒度的规则。我限制哪些用户可以使用哪些服务,部分是为了安全,部分是作为学习练习。
在运行 3.6.2 内核的 Debian 6.0.6 上使用 iptables v1.4.16.2。
但是我遇到了一个我还不太明白的问题......
这工作得很好。我没有任何通用的状态跟踪规则。
## 传出端口 81 $IPTABLES -A 输出 -p tcp --dport 81 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p tcp --sport 81 -s $MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT
## 用户帐户的传出端口 80 $IPTABLES -A OUTPUT --match owner --uid-owner useraccount -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED --sport 1024:65535 -j ACCEPT $IPTABLES -A INPUT -p tcp --sport 80 --dport 1024:65535 -d $MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT
这仅允许帐户“useraccount”使用端口 80,但 TCP 流量的此类规则存在问题。
##默认传出日志+阻止规则 $IPTABLES -A OUTPUT -j LOG --log-prefix "BAD OUTGOING " --log-ip-options --log-tcp-options --log-uid $IPTABLES -A 输出 -j DROP
以上工作,用户“useraccount”可以完美地获取文件。系统上的任何其他用户都不能与端口 80 建立传出连接。
useraccount@host:$ wget http://cachefly.cachefly.net/10mb.test
但是上面的 wget 在我的系统日志中留下了 x7 丢弃的条目:
Oct 18 02:00:35 xxxx 内核:BAD OUTGOING 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 OUTPUT -s $MYIP -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p tcp -m tcp --sport 80 -d $MYIP -m state --state ESTABLISHED -j ACCEPT
我将“-m state --state”换成了“-m conntrack --ctstate”,因为状态匹配显然已经过时了。
拥有通用状态跟踪规则是最佳实践吗?是否认为上述规则不正确?
为了严格控制传出用户的连接,这样的事情会更好吗?
$IPTABLES -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT $IPTABLES -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT $IPTABLES -A 输出 -p tcp --dport 80 -s $SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner useraccount -j ACCEPT $IPTABLES -A OUTPUT -p tcp --dport 80 -s $SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner otheraccount -j ACCEPT
Sté*_*las 17
长话短说,当套接字不属于任何人时发送该 ACK。允许属于用户的套接字的数据包,而不是允许属于用户的套接字的x数据包x。
更长的故事。
要了解这个问题,了解wgetHTTP 请求的一般工作方式会有所帮助。
在
wget http://cachefly.cachefly.net/10mb.test
Run Code Online (Sandbox Code Playgroud)
wget建立到 的 TCP 连接cachefly.cachefly.net,一旦建立,就在 HTTP 协议中发送一个请求,内容为:“请将/10mb.test( GET /10mb.test HTTP/1.1)的内容发给我,顺便说一下,完成后请不要关闭连接 ( Connection: Keep-alive)。原因这样做是因为万一服务器回复重定向到同一 IP 地址上的 URL,它可以重用连接。
现在服务器可以回复“您请求的数据来了,当心它有 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连接通过的数据包。