一个典型的HTTP服务器的网络扑朔大概长这样
很典型的LNMP配置,对外提供HTTP服务
可以看出,流量的第一站是BGP路由网络(公网)->再到机房路由->再到区域子网(同掩码)->再到服务器kernel,kernel取出对应端口tcp数据包。重组为tcp流。然后发给上级application层(如nginx,apache等等的recv())
流量的第一站【BGP路由网络(公网)】只有运营商可以控制,我们也配置不了。略过。
现代厂商生产的路由器基本CVE-2016-3116都用不了。有些直接开的telnetd,没加密,只有IP白名单限制(这个安全限制其实很傻逼,如果控制了相邻BPG路由器,源IP可以完全伪造。横向日过去),如果只开console ttl,暂时没法(前提是ttl没入公网)。总之这里暂时略过。运营商的路由器基本都是买的(思科,华为之类的),系统一般不是Linux,像bsd之类的。这是真的路由器(路由器的定义),不是tplink的那种家庭nat“路由器”
流量的第二站【上级机房路由网关】这是很重要的,如果Server一被打,就直接被机房上级路由空路由。那么服务器再怎么配置都是没有的。因为流量根本就不会到达子网。服务端根本收不到任何数据。所以要选不拔线的“高防机房”。
如果你能进去的话,这个就暂时不提。这里讲的是正常用户能配置的地方,哈哈哈
内核部分,流量到达服务器首先会被内核处理。然后才到应用层。所以内核过滤一份,会明显减小应用层处理的压力。
在应用层处理的话,会涉及明显的用户态与内核态之间切换。已以及数据频繁的从用户态与内核态的拷贝。造成的明显的后果是占用大量内存。
下面就简单的说一下,内核是正常用户能配置最外层地方(openvz,lxc等等没法)
下面的抗DD配置是针对http server优化的,如果你的服务器跑的不是http服务,下面配置会有负面效果。
部分参考了经典的C1000K问题。
禁用udp连接,仅仅允许udp53(dns),udp123(时间同步)端口的通信。
http1.1,http2主要是tcp连接
http3是udp连接,QUIC现在依然会发生反射放大攻击。现在并没有完全解决。建议暂时不开。
禁用udp流量,udp洪水确实不好防,加上udp是无状态的。而且源IP可以伪造
禁用udp新连接入站
iptables -A INPUT -p udp -m state –state NEW -j DROP
仅允许dns和时间同步ntp数据包出站
iptables -A OUTPUT -p udp –dport 53 -j ACCEPT
iptables -A OUTPUT -p udp –dport 123 -j ACCEPT
iptables -A OUTPUT -p udp -j DROP
如果服务器是dhcp网络,记得把67,68端口打开
iptables -A OUTPUT -p udp –dport 67 -j ACCEPT
iptables -A OUTPUT -p udp –dport 68 -j ACCEPT
icmp洪水攻击,很简单,禁用ping就行。或者限制icmp包的频率。这个iptables防火墙可以配置
然后应对tcp层面的攻击
tcp syn攻击防御, tcp syn攻击就不解释了,自己去查。
注意syn是tcp连接前的数据包,IP可以伪造。
通过调整内核参数,来减小攻击的影响
/proc/sys/net/ipv4/tcp_syn_retries TCP SYN 连接超时重传次数
/proc/sys/net/ipv4/tcp_synack_retries 连接超时回复超时次数
/proc/sys/net/ipv4/tcp_retries2 TCP SYN 连接重传次数限制
这些太大,服务器会被大量空syn占用资源,增加内核负担
TCP队列等待参数
/proc/sys/net/ipv4/tcp_ecn 每个IP头部会被设置ECN位,服务器会检查发送端和接收端,动态调整网络拥塞。
ECN正常使用没什么问题,但是这在被ddos的情况下,反而会成为一直负担。ddos会有大量无效流量。
/proc/sys/net/ipv4/tcp_abort_on_overflow 当某IP的TCP请求连接队列溢出时,丢弃新的连接请求,避免请求无限排队卡死。
/proc/sys/net/ipv4/tcp_keepalive_probes 用于检测处于空闲状态的 TCP 连接是否仍然有效。防止大量空连接攻击
/proc/sys/net/ipv4/tcp_fin_timeout FIN-WAIT结束时间,默认太长。对于被恶意攻击,会被占用大量资源。
/proc/sys/net/ipv4/tcp_keepalive_intvl 用于设置TCP keepalive探测包的发送间隔时间。具体来说,这个参数定义了在发送两个连续的TCP keepalive探测包之间的时间间隔, 30就够了
/proc/sys/net/ipv4/tcp_keepalive_time 当一个TCP连接处于空闲状态(即没有数据传输)时,系统会定期发送TCP keepalive探测包以检测连接的状态。如果远程端未响应这些探测包,系统将认为连接已经断开,并关闭连接,释放资源。
/proc/sys/net/ipv4/tcp_notsent_lowat TCP发送缓冲区的低水位标记,1G当未发送数据达到该值时,触发发送。防止数据堆积,tcp慢读取攻击。默认太高,这个也不能设置的太低。
tcp_max_tw_buckets 用于限制处于TIME-WAIT状态的TCP连接的数量。TIME-WAIT状态是指在TCP连接关闭后,等待一段时间以确保远端接收到最后的ACK确认消息的状态,可以限制系统中允许处于TIME-WAIT状态的TCP连接数量,避免消耗过多的系统资源。
开启bbr
echo bbr > /proc/sys/net/ipv4/tcp_congestion_control 避免DDOS高宽带拥塞导致服务器速度过低
文件描述符fd
Linux的文件描述符fd是什么?,自己去查。
简单来说linux的每个打开的socket和accept都是fd
cat /proc/sys/fs/file-max 最大接收连接,大于100万就没事。
/proc/sys/fs/inotify/max_user_instances 每个用户能够创建的inotify实例的最大数量。这个限制是为了防止某个用户或应用程序过度使用系统资源。但是对于http服务器来说,基本只有www-data会占用网络文件描述符。如果太低会出现too many open files。拒绝服务
/proc/sys/fs/inotify/max_user_watches 决定了某个用户的内核事件的监听fd的最大文件数量,与epoll事件有关。如果没用epoll可以忽略。
cat /proc/sys/fs/nr_open 单用户进程最大打开文件数量 大于65536就行
另外,tcp_syncookies 要开启,防止大量syn洪水。
/proc/sys/net/ipv4/ip_local_port_range 反代设置的13270 - 65100就行,如果不反代,正常使用不用管。如果范围太小,连接后端会出现连接数不够用。把502错误。
这里说一下,Linux最大连接tcp连接数不是65536,是connect()函数的单IP的tcp限制为65536,加一个sub就能突破65536了。而对于server端的accept()函数是没有限制的,除非系统限制。
connect()函数只和客户端请求和服务器反代后端会用到,具体自己查,就不说了
cat /proc/sys/net/ipv4/tcp_tw_reuse 大于1 减少time wait的时间
/proc/sys/net/core/somaxconn 设置系统上accept套接字的最大连接数。和accept()函数有关
/proc/sys/net/ipv4/tcp_max_syn_backlog 设置TCP半连接队列的最大长度,用于存放未完成连接的请求。
/proc/sys/net/core/netdev_max_backlog 设置网络设备接收队列的最大长度。
描述符设置
iptables 链表设置,如果没用iptables可以忽略。
net.nf_conntrack_max
net.netfilter.nf_conntrack_max
net.nf_conntrack_max 和 net.netfilter.nf_conntrack_max:这两个参数通常被设置为相同的值,它们设置了nf_conntrack模块可以跟踪的最大连接数。此值设置为8388608意味着你的系统将能够跟踪多达多个并发连接。如果这个值设置得太低,则在高流量的情况下可能导致跟踪表满,从而阻止新的连接建立。
net.netfilter.nf_conntrack_tcp_timeout_fin_wait
net.netfilter.nf_conntrack_tcp_timeout_time_wait
此参数设置了在FIN-WAIT状态下的TCP连接在被系统自动删除之前可以保持打开状态的时间,此处设置为60秒。
这个参数指定了TIME-WAIT状态下的TCP连接在系统中保持的时间,60秒。
net.netfilter.nf_conntrack_tcp_timeout_close_wait
设置CLOSE-WAIT状态下的TCP连接保持的时间,设置为30秒。
端口内核态限制速率
内核态的速率限制比用户态的效率高很多
80 端口 IP在60秒内只允许新建80个连接
iptables -A INPUT -i eth0 -p tcp -m tcp –dport 80 -m state –state NEW -m recent –set –name DEFAULT –rsource
第一个匹配到NEW状态连接在端口80的规则,它用来记录新的连接。
iptables -A INPUT -i eth0 -p tcp -m tcp –dport 80 -m state –state NEW -m recent –update –seconds 60 –hitcount 100 –name DEFAULT –rsource -j DROP
基于第一条规则收集的数据,如果一个IP地址在60秒内尝试了超过100次新连接,则阻止(DROP)这个IP的新连接请求。
控制单个IP的最大并发连接数为80
iptables -I INPUT -p tcp –dport 80 -m connlimit –connlimit-above 80 -j REJECT
每个IP最多120个初始syn
iptables -A INPUT -p tcp –syn -m connlimit –connlimit-above 120 -j DROP
配置完后,可以尽可能减小应用层服务处理的压力。应用层再进行进一步处理
webserver -> php-runtime - > *.php -> SQL,尽量不用让流量到达后面。越到后面处理的花费的性能越大。
如果能再上级网关路由处理,上硬件防火墙当然是最好的。运营商路由拦截也是很好的,不过正常人是不可能的操作的。
下次再写应用层处理的思路吧
说过题外话,论坛的cpu占用过高。