最佳实践
概述
参考:
让服务器成为路由器
这里假设想作为路由器的服务器的 IP 为 172.38.180.211
一、首先需要保证路由器具有 IP 转发能力,开启 IP 转发
echo 1 > /proc/sys/net/ipv4/ip_forward
二、保证路由器在收到局域网内其他设备的数据包时,将收到的 IP SNAT 为本机 IP。这有两种方法
方法一:手动 SNAT
export LOCAL_IP="172.38.180.211"
export LAN_CIDR="172.38.180.0/24"
iptables -t nat -A POSTROUTING -s ${LAN_CIDR} ! -d ${LAN_CIDR} -j SNAT --to-source ${LOCAL_IP}
如果 LOCAL_IP 直接就是公网 IP 的话,也就是这台服务器本身就有公网 IP 的话,那就更完美了。
方法二:自动 SNAT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
这条命令表示将 eth0 接口的出站流量进行伪装,i.e. 使用 eth0 接口的 IP 地址作为源地址。这样,内网的主机就可以通过本服务器访问外网了。
三、将内网服务器的网关设为这台服务器即可
我可以 ping 别人,别人不能 ping 我
- iptables -A INPUT -p icmp –icmp-type 8 -s 0/0 -j DROP # 默认 INPUT 链的策略为 ACCEPT 的时候用
- iptables -A INPUT -p icmp –icmp-type 0 -s 0/0 -j ACCEPT # 默认 INPUT 链的策略为 DROP 的时候用
- iptables -A OUTPUT -p icmp –icmp-type 0 -s LOCALIP -j DROP # 默认 OUTPUT 链的策略为 ACCEPT 的时候用,注意把 Localip 改为本机 IP
- iptables -A OUTPUT -p icmp –icmp-type 8 -s LOCALIP -j ACCEPT # 默认 OUTPUT 链的策略为 DROP 的时候用,注意把 Localip 改为本机 IP
敲门机制 - 通过特定行为放通入站流量
参考: https://www.cnblogs.com/martinzhang/p/5348769.html
敲门机制是指,OS 记录访问者各种请求,达到某些条件(e.g. 在特定时间达到 N 次 TCP、ICMP、etc. 请求)后,允许该访问者访问 OS。
一、敲门(knocking)机制
- 监听 ICMP echo 请求(ping)
- 数据包长度必须正好是 1078 字节
- 将源 IP 记录到 sshKeyList 列表中
[!Tip] 可以在 sshKeyList 文件
cat /proc/net/xt_recent/sshKeyList中查看敲门成功的 IPICMP报文结构:IP头部(20 Bytes) + ICMP头部(8 Bytes) + 数据部分Data。所以 ping 的时候,指定数据大小为 1050 即可
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -m length --length 1078 -m recent --name sshKeyList --set -j ACCEPT
# 使用 TCP 代替 ICMP 实现敲门机制。将对 1000 端口得请求记录到 sshKeyList 中。然后下面只要在某时间内访问了 1000 端口 N 次,即可放通
# **注意**:这个 1000 端口哪怕没有任何程序监听也是可以的!
# iptables -A INPUT -p tcp --dport 1000 -m recent --name sshKeyList --set -j DROP
二、只有满足以下条件才能访问 10443 端口
- 源 IP 在过去 30 秒内至少敲门 5 次(hitcount 5),但是不能超过 5 次(第一条,达到 6 次及以上就会 DROP)
- IP 必须在 sshKeyList 中
iptables -A INPUT -p tcp -m tcp --dport 10443 -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 6 -j DROP
iptables -A INPUT -p tcp -m tcp --dport 10443 -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 5 -j ACCEPT
# 不限制放通的端口
# iptables -A INPUT -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 5 -j ACCEPT
[!Tip] –set, –name, –rcheck, –seconds, –hitcount 都是 recent 扩展中可用的选项。详见 iptables CLI
三、允许所有已经建立的连接继续通信
[!Attention] 这条规则只能放在敲门行为之后,如果放在开头将导致开门失效。原因未知
iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
四、拒绝所有
iptables -A INPUT -j DROP
五、实现效果
使用如下 ping 的方式,即可让设备放通本机
ping -s 1050 -c 5 ${DestIP}
随后在 30 秒内即可访问任意端口,由于第三条的存在,已建立的连接不会中断
常用命令组合
使用 ping -s 1050 -c 5 目标IP 即可放通
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -m length --length 1078 -m recent --name sshKeyList --set -j ACCEPT
iptables -A INPUT -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 6 -j DROP
iptables -A INPUT -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 5 -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -j DROP
使用 ping -s 1050 -c 5 目标IP 即可放通 10443 端口
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -m length --length 1078 -m recent --name sshKeyList --set -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 10443 --syn -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 6 -j DROP
iptables -A INPUT -p tcp -m tcp --dport 10443 --syn -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 5 -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -j DROP
使用 for i in {1..5}; do nc -w 1 ${DestIP} 1000; done 访问 1000 端口 5 次即可饭桶
iptables -A INPUT -p tcp --dport 1000 -m recent --name sshKeyList --set -j DROP
iptables -A INPUT -p tcp -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 6 -j DROP
iptables -A INPUT -p tcp -m recent --name sshKeyList --rcheck --seconds 30 --hitcount 5 -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -j DROP
其他
屏蔽 HTTP 服务 Flood×××,有时会有用户在某个服务,例如 HTTP 80 上发起大量连接请求,此时我们可以启用如下规则:
- iptables -A INPUT -p tcp –dport 80 -m limit –limit 100/minute –limit-burst 200 -j ACCEPT
- 上述命令会将连接限制到每分钟 100 个,上限设定为 200。
12、屏蔽指定 MAC 地址
使用如下规则可以屏蔽指定的 MAC 地址:
iptables -A INPUT -m mac –mac-source 00:00:00:00:00:00 -j DROP
13、限制并发连接数
如果你不希望来自特定端口的过多并发连接,可以使用如下规则:
iptables -A INPUT -p tcp –syn –dport 22 -m connlimit –connlimit-above 3 -j REJECT
以上规则限制每客户端不超过 3 个连接。
17、允许建立相关连接
随着网络流量的进出分离,要允许建立传入相关连接,可以使用如下规则:
iptables -A INPUT -m conntrack –ctstate ESTABLISHED,RELATED -j ACCEPT
允许建立传出相关连接的规则:
iptables -A OUTPUT -m conntrack –ctstate ESTABLISHED -j ACCEPT
18、丢弃无效数据包
很多网络 ××× 都会尝试用 ××× 自定义的非法数据包进行尝试,我们可以使用如下命令来丢弃无效数据包:
iptables -A INPUT -m conntrack –ctstate INVALID -j DROP
屏蔽邮件发送规则,可以在规则中屏蔽 SMTP 传出端口:
- iptables -A OUTPUT -p tcp –dports 25,465,587 -j REJECT
使用 iptables 对多租户环境中的 TCP 限速
原文链接:使用 iptables 对多租户环境中的 TCP 限速
我们有个服务以类似 SideCar 的方式和应用一起运行,SideCar 和应用通过 Unix Domain Socket 进行通讯。为了方便用户,在开发的时候不必在自己的开发环境中跑一个 SideCar,我用 socat 在一台开发环境的机器上 map UDS 到一个端口。这样用户在开发的时候就可以直接通过这个 TCP 端口测试服务,而不用自己开一个 SideCar 使用 UDS 了。
因为所有人都要用这一个地址做开发,所以就有互相影响的问题。虽然性能还可以,几十万 QPS 不成问题,但是总有憨憨拿来搞压测,把资源跑满,影响别人。我在使用说明文档里用红色大字写了这是开发测试用的,不能压测,还是有一些视力不好的同事会强行压测。隔三差五我就得去解释一番,礼貌地请同事不要再这样做了。
最近实在累了。研究了一下直接给这个端口加上 per IP 的 rate limit,效果还不错。方法是在 Per-IP rate limiting with iptables[1] 学习到的,这个公司是提供一个多租户的 SaaS 服务,也有类似的问题:有一些非正常用户 abuse 他们的服务,由于 abuse 发生在连接建立阶段,还没有进入到业务代码,所以无法从应用的层面进行限速,解决发现就是通过 iptables 实现的。详细的实现方法可以参考这篇文章。
iptables 本身是无状态的,每一个进入的 packet 都单独判断规则。rate limit 显然是一个有状态的规则,所以要用到 module: hashlimit。(原文中还用到了 conntrack,他是想只针对新建连接做限制,已经建立的连接不限制速度了。因为这个应用内部就可以控制了,但是我这里是想对所有的 packet 进行限速,所以就不需要用到这个 module)
完整的命令如下:
$ iptables --new-chain SOCAT-RATE-LIMIT
$ iptables --append SOCAT-RATE-LIMIT \
--match hashlimit \
--hashlimit-mode srcip \
--hashlimit-upto 50/sec \
--hashlimit-burst 100 \
--hashlimit-name conn_rate_limit \
--jump ACCEPT
$ iptables --append SOCAT-RATE-LIMIT --jump DROP
$ iptables -I INPUT -p tcp --dport 1234 --jump SOCAT-RATE-LIMIT
第一行是新建一个 iptables Chain,做 rate limit;
第二行处理如果在 rate limit 限额内,就接受包;否则跳到第三行,直接将包 DROP;
最后将新的 Chain 加入到 INPUT 中,对此端口的流量进行限制。
有关 rate limit 的算法,主要是两个参数:
--hashlimit-upto其实本质上是 1s 内可以进入多少 packet,50/sec就是20ms一个 packet;- 那如何在
10ms发来 10 个 packet,后面一直没发送,怎么办?这个在测试情景下也比较常见,不能要求用户一直匀速地发送。所以就要用到--hashlimit-burst。字面意思是瞬间可以发送多少 packet,但实际上,可以理解这个参数就是可用的 credit。
两个指标配合起来理解,就是每个 ip 刚开始都会有 burst 个 credit,每个 ip 发送来的 packet 都会占用 burst 里面的 credit,用完了之后再发来的包就会被直接 DROP。这个 credit 会以 upto 的速度一直增加,但是最多增加到 burst(初始值),之后就 use it or lost it.
举个例子,假如 --hashlimit-upto 50/sec --hashlimit-burst 20 的话,某个 IP 以匀速每 ms 一个 packet 的速度发送,最终会有多少 packets 被接受?答案是 70. 最初的 20ms,所有的 packet 都会被接受,因为 --hashlimit-burst 是 20,所以最初的 credit 是 20. 这个用完之后就要依赖 --hashlimit--upto 50/sec 来每 20ms 获得一个 packet credit 了。所以每 20ms 可以接受一个。
这是限速之后的效果,非常明显:

参考资料
[1]Per-IP rate limiting with iptables: https://making.pusher.com/per-ip-rate-limiting-with-iptables/index.html
反馈
此页是否对你有帮助?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.