中小型服务器做DDoS防护,最大的误区是一上来就堆高防IP或者上云清洗,结果钱花了不少,实际被打的时候还是秒封端口。真正实用的配置思路是先把自己能控制的层面做扎实,再考虑外部清洗。中小型服务器的特点很明确:带宽通常在100Mbps到1Gbps之间,单机部署,预算有限,没有专职安全团队。攻击者也很清楚这一点,所以针对中小型服务器的攻击往往不是几百G的大流量,而是几G到几十G的混合型攻击,包括SYN Flood、UDP反射、CC攻击和慢速连接耗尽。这类攻击恰恰是可以通过系统层、网络层和Web层配置大幅削弱的。

先搞清楚攻击类型再谈防护

很多运维一看到服务器卡顿、SSH连不上,第一反应就是“被打了”,然后盲目重启或者加钱买防护。实际上中小型服务器遇到的DDoS大致分三类:带宽耗尽型、协议栈耗尽型和应用层耗尽型。带宽耗尽型是指攻击流量直接塞满你的服务器上行带宽,比如UDP Flood或者伪造源地址的SYN Flood,这种在服务器内部做再多优化也没用,因为流量根本没到你的协议栈处理就已经把带宽占满了。协议栈耗尽型是指流量不大,但利用了大量半开连接或者异常包让内核忙于处理无效请求,比如SYN Flood打到几十万pps,CPU直接飙满。应用层耗尽型就是CC攻击,模拟真实用户请求打你的HTTP服务,带宽和连接数都不算高,但后端处理不过来。

所以第一步不是配置,而是判断。用iftop或者nload看实时带宽,用netstat统计连接状态,用top看CPU和软中断占用。如果带宽跑满但CPU不高,基本是带宽耗尽型,这种情况只能上运营商级清洗或者高防IP,本地配置无效。如果带宽不高但SYN_RECV状态连接数异常多,CPU软中断很高,那就是协议栈攻击,内核参数优化非常有效。如果HTTP服务响应慢但网络和CPU都还好,多半是CC攻击,需要在Web层做限速和行为识别。

内核参数优化是性价比最高的第一步

Linux内核默认参数是为普通服务器场景设计的,面对DDoS时很多默认值反而会放大攻击效果。对于中小型服务器,以下参数调整可以直接提升抗SYN Flood和连接耗尽的能力。编辑/etc/sysctl.conf或者直接在命令行用sysctl -w临时生效,确认有效后写入配置文件。

# 开启SYN Cookie,防止半开连接耗尽
net.ipv4.tcp_syncookies = 1

# 降低SYN_RECV状态的重试次数,默认5次,改为2次
net.ipv4.tcp_synack_retries = 2

# 减少TIME_WAIT状态的保持时间,加快连接回收
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1

# 扩大半连接队列长度,但不要无限放大,避免内存耗尽
net.ipv4.tcp_max_syn_backlog = 8192

# 限制每个源IP的SYN队列长度,防止单IP占满队列
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 8192

# 开启TCP时间戳,帮助识别伪造包
net.ipv4.tcp_timestamps = 1

# 降低孤儿连接回收阈值,防止内存被无效连接占满
net.ipv4.tcp_max_orphans = 65536

# 加快孤儿连接回收
net.ipv4.tcp_orphan_retries = 1

这些参数的核心逻辑是:让内核更快地放弃无效连接,同时用SYN Cookie机制在SYN队列满的时候依然能够响应合法的新连接。SYN Cookie的原理是不再为每个SYN包分配完整的连接状态,而是把连接信息编码进SYN-ACK包的序列号里,等收到客户端的ACK后再还原连接。这样即使攻击者发送海量SYN包,也不会耗尽内存和队列。代价是SYN Cookie会消耗少量CPU做加解密运算,但对于中小型服务器来说这点开销完全可以接受。

iptables限速与包过滤是第二道防线

内核参数优化解决的是协议栈层面的资源耗尽,但攻击流量还是会到达服务器。如果攻击流量不是特别大,可以通过iptables在进入协议栈之前做限速和过滤。中小型服务器最实用的几条iptables规则如下。

限制单个IP的SYN包速率,超过阈值的直接丢弃。这条规则对SYN Flood非常有效,因为正常用户的SYN包速率很低,攻击者的SYN包速率会异常高。

iptables -A INPUT -p tcp --syn -m limit --limit 50/s --limit-burst 100 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP

限制单个IP的并发连接数,防止连接耗尽。注意这个规则要放在允许SSH和Web服务的规则之后,否则可能把自己也锁在外面。

iptables -A INPUT -p tcp -m connlimit --connlimit-above 50 -j DROP

丢弃明显的异常包,比如源地址是内网地址或者组播地址的包,这些包一定是伪造的。

iptables -A INPUT -s 10.0.0.0/8 -j DROP
iptables -A INPUT -s 172.16.0.0/12 -j DROP
iptables -A INPUT -s 192.168.0.0/16 -j DROP
iptables -A INPUT -s 224.0.0.0/4 -j DROP
iptables -A INPUT -s 240.0.0.0/5 -j DROP
iptables -A INPUT -s 127.0.0.0/8 -j DROP

对于UDP反射攻击,如果服务器不对外提供UDP服务,直接丢弃所有入站UDP包是最简单有效的做法。如果必须开放UDP,比如DNS服务,那么需要限制UDP包速率并且只允许特定端口的UDP流量。

# 如果不需要UDP服务,直接丢弃所有UDP入站包
iptables -A INPUT -p udp -j DROP

# 如果需要开放DNS,只允许53端口并且限速
iptables -A INPUT -p udp --dport 53 -m limit --limit 1000/s --limit-burst 2000 -j ACCEPT
iptables -A INPUT -p udp --dport 53 -j DROP

iptables限速的优点是零成本、响应快,缺点是规则太多会消耗CPU,而且对于大流量攻击无能为力。对于100Mbps以下带宽的中小型服务器,iptables配合内核参数优化通常可以扛住大部分协议栈攻击。

Web服务层的CC攻击防护

CC攻击的目标是HTTP服务,攻击者通过大量真实HTTP请求消耗服务器的CPU、内存或者数据库连接。对于Nginx或者Apache部署的中小型服务器,Web层防护的核心是限速和行为识别。

Nginx有两个模块非常适合做CC防护:limit_req和limit_conn。limit_req基于漏桶算法限制请求速率,limit_conn限制并发连接数。在nginx.conf的http块中定义限速区域,然后在server或者location中应用。

http {
    # 定义一个共享内存区域,按客户端IP限速,每秒10个请求,突发20个
    limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=10r/s;
    
    # 定义并发连接限制,每个IP最多20个并发连接
    limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
    
    server {
        listen 80;
        
        # 应用请求速率限制
        limit_req zone=req_per_ip burst=20 nodelay;
        
        # 应用并发连接限制
        limit_conn conn_per_ip 20;
        
        location / {
            proxy_pass http://backend;
        }
    }
}

这里的rate=10r/s意味着每个IP平均每秒最多10个请求,burst=20允许突发20个请求,nodelay表示突发请求不排队直接处理。对于大多数中小型网站,这个阈值足够正常用户使用,同时能有效拦截CC攻击。如果攻击者使用大量代理IP,单IP限速的效果会打折扣,这时需要结合其他手段。

另一个有效的方法是识别攻击特征。CC攻击的请求往往集中在某个特定URL或者带有特定的User-Agent。通过分析Nginx访问日志,找出攻击请求的共同特征,然后直接在Nginx层拒绝。

# 拒绝特定User-Agent的请求
if ($http_user_agent ~* "sqlmap|nikto|fimap|nessus") {
    return 403;
}

# 拒绝特定URL的请求
location /wp-login.php {
    deny all;
}

对于更复杂的CC攻击,可以考虑使用Nginx的lua模块或者OpenResty来做动态行为分析,比如检测请求间隔、请求序列是否符合人类行为模式。但这对于中小型服务器来说可能过于复杂,性价比不高。更实际的做法是使用fail2ban自动封禁异常IP。

fail2ban自动封禁异常IP

fail2ban是一个日志分析工具,可以监控SSH、Nginx、Apache等服务的日志,当发现某个IP在短时间内有大量失败尝试或者异常请求时,自动调用iptables封禁该IP一段时间。对于中小型服务器,fail2ban是自动化防护中最实用的工具之一。

安装fail2ban后,创建一个自定义的jail配置来监控Nginx的访问日志,封禁请求频率过高的IP。

# /etc/fail2ban/jail.local
[nginx-cc]
enabled = true
filter = nginx-cc
action = iptables-multiport[name=nginx-cc, port="http,https"]
logpath = /var/log/nginx/access.log
maxretry = 100
findtime = 60
bantime = 600

对应的filter配置在/etc/fail2ban/filter.d/nginx-cc.conf中定义,匹配请求频率异常的行。

[Definition]
failregex = ^<HOST> -.*"(GET|POST).*
ignoreregex =

这个配置的含义是:如果某个IP在60秒内出现100次以上的GET或POST请求,就封禁10分钟。对于中小型网站,这个阈值可以根据实际流量调整。fail2ban的优点是自动化程度高,缺点是对于分布式CC攻击效果有限,因为攻击IP太多太分散,封禁不过来。

CDN与反向代理的合理使用

对于中小型服务器,使用CDN是性价比最高的DDoS防护手段之一。CDN服务商通常自带大带宽和基础DDoS清洗能力,可以把攻击流量挡在源站之外。但使用CDN有一个关键点:必须隐藏源站的真实IP,否则攻击者直接绕过CDN打源站,CDN就形同虚设。

隐藏源站IP的方法包括:源站只允许CDN节点的IP访问80和443端口,其他IP一律拒绝;不要在DNS记录中暴露源站IP;不要用源站IP直接访问网站;邮件服务使用独立的IP。如果源站IP已经泄露,更换IP是必要的。

对于不想使用商业CDN的中小型服务器,可以自建一层反向代理。比如用一台便宜的VPS作为前置代理,只开放80和443端口,后端源站只允许这台代理的IP访问。这样即使攻击者打代理,源站也不会直接暴露。但自建代理的带宽有限,面对大流量攻击时代理本身会被打挂,所以这只适用于中小规模的攻击防护。

监控与应急响应

配置做得再好,没有监控和应急响应机制也是不完整的。中小型服务器至少需要监控三个指标:带宽使用率、CPU负载、连接数。可以使用Prometheus配合node_exporter采集系统指标,用Grafana展示,也可以简单地用Zabbix或者Nagios。当带宽或者连接数异常升高时,第一时间收到告警,然后根据前面说的判断方法确定攻击类型,再决定是调整配置还是联系上游清洗。

应急响应中最重要的一条原则是:不要慌乱重启服务器。重启会丢失当前的连接状态和日志,反而可能让攻击者更容易得手。正确的做法是先采集证据,包括netstat -an的输出、iftop的流量截图、Nginx的访问日志片段,然后根据攻击类型逐步启用防护措施。如果攻击流量已经超过服务器带宽,本地配置无法解决,立即联系机房或者云服务商请求黑洞或者清洗。

中小型服务器的DDoS防护不是一劳永逸的事情,攻击手法在变,防护配置也需要持续调整。把内核参数、iptables、Web层限速、fail2ban和CDN这五层结合起来,可以在预算有限的情况下大幅提升服务器的抗攻击能力。关键是要理解每一层防护的原理和适用场景,而不是盲目堆砌配置。