CentOS服务器暴露在公网时,端口扫描几乎是每分钟都在发生的事。攻击者用自动化工具批量探测22、3306、6379、8080等常见端口,一旦发现服务版本过旧或未授权访问,就会直接尝试爆破或利用漏洞。防端口扫描的核心思路不是让扫描完全“看不见”,而是让扫描行为变得成本极高、信息极难获取,同时保证正常业务连接不受影响。
下面从系统内核参数、防火墙策略、服务伪装、入侵检测和自动化封禁五个层面,给出一套可以直接落地的配置方案。以下操作以CentOS 7/8为主,部分命令在Rocky Linux和AlmaLinux同样适用。
一、先收紧内核网络参数,降低被扫描时的响应特征扫描器判断端口是否开放,依赖的是目标主机返回的TCP响应包。如果系统内核默认响应过于“配合”,扫描器就能快速确认端口状态。修改/etc/sysctl.conf中的相关参数,可以显著增加扫描难度。
# 开启SYN Cookie保护,防止半连接队列被填满 net.ipv4.tcp_syncookies = 1 # 减少SYN-ACK重试次数,让扫描器更难判断端口是否真正开放 net.ipv4.tcp_synack_retries = 1 # 缩短TIME_WAIT状态时间,降低被用于指纹识别的连接残留 net.ipv4.tcp_fin_timeout = 15 net.ipv4.tcp_tw_reuse = 1 # 禁止响应ICMP重定向,防止被用于路由探测 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 # 开启反向路径过滤,丢弃伪造源地址的数据包 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # 记录并丢弃martian包(源地址不合法的包) net.ipv4.conf.all.log_martians = 1
执行sysctl -p让参数立即生效。这些参数不会影响正常TCP连接,但会让nmap等工具的探测结果变得不稳定,尤其是SYN扫描和FIN扫描的准确率会明显下降。
二、用firewalld或iptables建立“白名单+限速”的访问控制很多人防扫描的第一反应是“关闭所有不用的端口”,但更有效的做法是:对必须对外开放的端口做访问来源限制,对不需要对外开放的端口直接DROP而不是REJECT。DROP会让扫描器等待超时,大幅拖慢扫描速度。
如果使用firewalld,先移除所有默认放行规则,只开放必要服务,并绑定来源IP。
# 启动并启用firewalld systemctl enable --now firewalld # 默认区域设为drop,所有未明确允许的流量全部丢弃 firewall-cmd --set-default-zone=drop # 仅允许特定IP访问SSH,例如办公室固定IP firewall-cmd --permanent --zone=drop --add-rich-rule='rule family="ipv4" source address="203.0.113.50" port protocol="tcp" port="22" accept' # 允许所有来源访问Web服务(80和443) firewall-cmd --permanent --zone=drop --add-port=80/tcp firewall-cmd --permanent --zone=drop --add-port=443/tcp # 重载规则 firewall-cmd --reload
如果服务器必须对全网开放SSH端口,至少要用firewalld的限速功能,限制单个IP在单位时间内的新建连接数。
# 对22端口新建连接限速:每10秒最多3个新连接 firewall-cmd --permanent --zone=drop --add-rich-rule='rule family="ipv4" port port="22" protocol="tcp" accept limit value="3/m"'
使用iptables的服务器,可以这样配置:
# 默认策略:INPUT链DROP,OUTPUT链ACCEPT iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # 允许已建立的连接 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 允许回环接口 iptables -A INPUT -i lo -j ACCEPT # 对SSH端口做连接频率限制:每60秒最多5个新连接,超出则丢弃 iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name sshscan iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 --name sshscan -j DROP iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT # 开放Web端口 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT
注意:DROP策略会让扫描器认为端口被过滤,而不是关闭。这比REJECT更有效,因为REJECT会立即返回ICMP不可达,等于直接告诉扫描器“这个IP存在,但端口关了”。
三、修改默认端口与服务指纹,增加扫描结果误判率扫描器识别服务类型,靠的是端口号和服务返回的banner信息。把SSH从22改到高位端口(如22022),同时修改SSH服务的banner,可以让自动化扫描工具直接跳过你的服务器。
修改SSH端口:
# 编辑/etc/ssh/sshd_config Port 22022 # 同时禁止root直接登录 PermitRootLogin no # 关闭密码登录,改用密钥 PasswordAuthentication no
修改后执行systemctl restart sshd。如果服务器上有MySQL或Redis,同样建议修改默认端口。Redis尤其危险,默认6379端口经常被扫描后直接写入SSH公钥或挖矿脚本。
对于必须保留默认端口的服务,可以修改banner信息。以Apache为例,在httpd.conf中添加:
ServerTokens Prod ServerSignature Off
这样Apache返回的响应头中不会包含详细版本号。Nginx则在nginx.conf的http块中设置:
server_tokens off;
对于SSH,可以在sshd_config中通过Banner指令指定一个自定义的登录提示文件,但更关键的是确保OpenSSH版本保持最新,因为旧版本的指纹特征非常明显,扫描器可以直接通过TCP握手特征判断出具体版本。
四、部署fail2ban实现自动化动态封禁手动配置防火墙规则只能应对已知的固定来源IP,对于大规模分布式扫描,必须用fail2ban这类工具动态识别并封禁扫描源。fail2ban通过监控日志文件,匹配预设的失败模式,自动调用防火墙规则封禁来源IP一段时间。
安装fail2ban:
yum install -y epel-release yum install -y fail2ban systemctl enable --now fail2ban
创建自定义配置文件/etc/fail2ban/jail.local:
[DEFAULT] # 封禁时间:24小时 bantime = 86400 # 查找时间窗口:10分钟 findtime = 600 # 最大失败次数:3次 maxretry = 3 # 忽略本机IP ignoreip = 127.0.0.1/8 ::1 [sshd] enabled = true port = 22022 logpath = /var/log/secure backend = systemd # 自定义端口扫描检测规则 [port-scan] enabled = true filter = port-scan action = iptables-multiport[name=port-scan, port="1:65535", protocol=tcp] logpath = /var/log/messages maxretry = 5 findtime = 60 bantime = 86400
创建对应的过滤器文件/etc/fail2ban/filter.d/port-scan.conf:
[Definition] failregex = ^.*TCP_IN Block.*SRC=.*$ ignoreregex =
这个过滤器会匹配内核日志中记录的TCP_IN Block事件。当内核的rp_filter或防火墙规则丢弃了大量来自同一IP的异常包时,fail2ban就会自动封禁该IP。重启fail2ban后,可以用fail2ban-client status port-scan查看封禁情况。
fail2ban的优势在于动态响应,不需要人工干预。对于持续扫描的IP,封禁24小时后自动解封,如果对方继续扫描,会再次被封禁。这种方式比永久封禁更灵活,避免误伤动态IP池中的正常用户。
五、利用psad检测端口扫描并主动告警psad(Port Scan Attack Detector)是专门用于检测端口扫描的轻量级工具。它通过分析iptables日志,识别SYN扫描、FIN扫描、UDP扫描等多种扫描模式,并可以通过邮件发送告警。
安装psad:
yum install -y psad
编辑/etc/psad/psad.conf,设置告警邮箱和检测灵敏度:
EMAIL_ADDRESSES root@localhost; HOSTNAME yourserver.example.com; ENABLE_AUTO_IDS Y; AUTO_IDS_DANGER_LEVEL 3;
同时需要确保iptables规则中加入了日志记录,psad才能读取到扫描日志。在INPUT链的DROP规则之前添加:
iptables -A INPUT -j LOG --log-prefix "TCP_IN Block: " iptables -A INPUT -j LOG --log-prefix "UDP_IN Block: " -p udp
注意日志规则要放在DROP规则之前,否则被丢弃的包不会进入日志。psad运行后,会持续监控日志,当检测到某个IP在短时间内触发了大量DROP记录时,会根据危险等级自动将该IP加入临时封锁列表,或发送邮件告警。
psad和fail2ban可以同时使用,两者侧重点不同:fail2ban关注应用层认证失败,psad关注网络层的扫描行为。两者结合能覆盖大部分攻击场景。
六、关闭不必要的系统服务与ICMP响应CentOS默认安装后,可能有一些不必要的服务在监听端口。用ss -tulnp命令查看当前所有监听端口,逐个确认哪些是业务必需的。
# 查看所有监听端口及对应进程 ss -tulnp # 关闭并禁用不需要的服务,例如avahi-daemon systemctl disable --now avahi-daemon # 关闭rpcbind(如果不需要NFS) systemctl disable --now rpcbind # 关闭postfix(如果不需要本地邮件投递) systemctl disable --now postfix
对于ICMP协议,建议直接丢弃所有外部ICMP请求。虽然这会让ping不通,但能有效隐藏主机存活状态。在/etc/sysctl.conf中添加:
net.ipv4.icmp_echo_ignore_all = 1
如果业务需要允许ping,至少限制ICMP速率:
# 每秒最多处理1个ICMP echo请求 iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 2 -j ACCEPT iptables -A INPUT -p icmp --icmp-type echo-request -j DROP七、定期审计与日志监控
安全配置不是一次性的工作。建议每周检查一次防火墙规则、fail2ban封禁列表和系统日志,确认是否有新的扫描来源或异常连接。
# 查看当前防火墙规则
firewall-cmd --list-all
# 查看fail2ban封禁状态
fail2ban-client status
# 查看最近被封禁的IP
fail2ban-client status sshd | grep "Banned IP"
# 查看系统认证日志中失败的登录尝试
grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -20
对于高频扫描IP,可以进一步分析其来源ASN和地理位置,如果确认是恶意扫描源,可以在防火墙中永久封禁整个IP段。但要注意不要误封CDN节点或云服务商的正常IP段。
最后强调一点:防端口扫描的本质是提高攻击者的时间成本和信息获取难度。没有任何单一方案能完全阻止扫描,但通过内核参数调整、防火墙DROP策略、服务端口修改、fail2ban动态封禁和psad检测告警的组合,可以让绝大多数自动化扫描工具在探测你的服务器时无功而返。正常用户通过白名单或正确的端口和密钥认证,完全不会感受到这些防护措施的存在。
