在CentOS系统的日常运维中,端口扫描是外部攻击者探测服务漏洞的第一步,也是内部安全审计必须直面的风险。很多运维人员习惯在发现问题后被动封堵,但真正有效的防护应当建立主动拦截和实时感知能力。下面直接记录几种高发端口扫描的拦截方法,以及在实际生产环境中踩过的坑和最终落地的配置细节。
利用iptables recent模块限制连接频率CentOS 7及以下版本默认防火墙仍然是iptables,它的recent模块能基于源IP记录连接行为,非常适合拦截快速扫描。攻击者通常使用masscan或Zmap在极短时间内发起大量SYN包,recent模块可以在内核层面直接丢弃这些包,比应用层拦截高效得多。具体配置思路是:先允许正常流量白名单,再对触发阈值的IP执行临时或永久拒绝。
# 创建一条自定义链用于处理扫描检测 iptables -N SCAN_CHECK # 将新进的SYN包送入检测链(仅针对NEW状态的TCP连接) iptables -A INPUT -p tcp -m state --state NEW -j SCAN_CHECK # 在检测链中记录源IP,60秒内新连接超过5个则触发日志并丢弃 iptables -A SCAN_CHECK -m recent --name portscan --set iptables -A SCAN_CHECK -m recent --name portscan --rcheck --seconds 60 --hitcount 5 -j LOG --log-prefix "PORT SCAN DETECTED: " --log-level 4 iptables -A SCAN_CHECK -m recent --name portscan --rcheck --seconds 60 --hitcount 5 -j DROP # 对已经触发拦截的IP,后续所有连接直接拒绝 iptables -A SCAN_CHECK -m recent --name blacklist --rcheck --seconds 3600 -j DROP iptables -A SCAN_CHECK -m recent --name portscan --update --seconds 60 --hitcount 10 -j SET --name blacklist --add
这套规则的核心在于分层处理:第一层基于60秒内5次新连接触发临时丢弃,第二层将10次以上的高频扫描IP自动加入黑名单并封锁3600秒。实际部署时需要特别注意,如果服务器前面有反向代理或负载均衡,源IP可能被伪装,这时应当取X-Forwarded-For头部,但iptables无法直接解析HTTP头,需要在应用层配合处理。另外,recent模块的默认表大小是100条记录,可以通过修改/proc/net/xt_recent/portscan的max_entries参数调大,高并发场景建议设为10000。
使用firewalld的rich rule实现动态黑名单CentOS 8及更新版本默认使用firewalld,它底层仍然调用nftables,但配置语法更灵活。对于端口扫描拦截,可以直接用rich rule配合ipset实现高效封禁。ipset是内核级IP集合,匹配速度远快于逐条iptables规则,当黑名单IP数量达到数千个时性能优势尤为明显。
# 创建ipset集合,指定哈希类型和超时时间 firewall-cmd --permanent --new-ipset=scanner_blacklist --type=hash:ip --option=timeout=3600 # 添加rich rule,对命中ipset的流量直接拒绝 firewall-cmd --permanent --add-rich-rule='rule source ipset=scanner_blacklist drop' # 重新加载使配置生效 firewall-cmd --reload
ipset只是存储黑名单的容器,还需要配合检测机制才能自动填充。比较实用的方案是编写一个systemd服务,定时分析系统日志中的认证失败记录或连接异常,将符合条件的IP动态加入ipset。下面是一个简化版的检测脚本,每分钟执行一次,从/var/log/secure中提取暴力破解SSH的IP并封禁。
#!/bin/bash
# 扫描secure日志,提取1分钟内SSH认证失败超过5次的IP
journalctl -u sshd --since "1 min ago" | grep "Failed password" | awk '{print $(NF-3)}' | sort | uniq -c | while read count ip; do
if [ $count -gt 5 ]; then
firewall-cmd --ipset=scanner_blacklist --add-entry="$ip" 2>/dev/null
echo "$(date) Blocked $ip after $count failed SSH attempts" >> /var/log/portscan_block.log
fi
done
这个脚本虽然简单,但在生产环境中非常有效。需要注意的是,journalctl的--since参数依赖系统时间准确,如果服务器时间同步出现问题,可能导致漏报或误报。建议配合chronyd服务确保时间精度。另外,脚本中的阈值和检测间隔需要根据实际业务调整,对于跳板机这类本身就有大量合法登录的服务器,阈值应当适当调高,避免误封正常用户。
部署PSAD实现端口扫描深度检测iptables和firewalld的规则主要针对连接频率,但高级攻击者会采用慢速扫描,将探测包分散在数小时内,频率限制很难生效。PSAD(Port Scan Attack Detector)通过分析iptables日志中的TCP标志位组合,能够识别出SYN扫描、FIN扫描、NULL扫描、XMAS扫描等多种隐蔽手法。它工作在日志分析层,不依赖频率阈值,而是基于包的特征指纹判断扫描行为。
安装PSAD在CentOS 7上非常简单,EPEL仓库已经包含:
yum install epel-release -y yum install psad -y
配置的关键在于/etc/psad/psad.conf中的几个参数。EMAIL_ADDRESSES用于接收告警,但在纯自动化运维场景中,更推荐将告警发送到SIEM或日志平台。ENABLE_AUTO_IDS建议设为Y,这样PSAD检测到扫描后会自动添加iptables规则阻断源IP,实现检测与响应的闭环。DANGER_LEVEL阈值需要根据网络环境调整,默认值5比较激进,在内网环境可以保持,公网服务器建议降到3以减少误报。
ENABLE_AUTO_IDS Y; AUTO_IDS_DANGER_LEVEL 3; ENABLE_SYSLOG_FILE Y; IPT_SYSLOG_FILE /var/log/iptables.log; IGNORE_PORTS 80,443,8080;
特别注意IGNORE_PORTS配置,必须把业务端口排除在检测范围之外,否则正常的高并发访问会被误判为端口扫描。曾经遇到过一台Nginx反向代理服务器,因为未排除80和443端口,PSAD在业务高峰期将数十个合法客户端IP全部封禁,导致大面积服务中断。这个教训说明,任何自动化拦截工具上线前,都需要先在旁路模式运行至少一周,观察告警日志确认无误后再开启自动封禁。
结合Fail2ban实现服务级防护端口扫描往往只是攻击的前奏,后续通常跟随针对具体服务的暴力破解或漏洞利用。Fail2ban是应用层防护的优秀工具,它能解析各种服务的日志,对多次认证失败的IP执行自定义动作。虽然Fail2ban主要用于防暴力破解,但通过自定义filter和jail,同样可以拦截端口扫描行为。
下面创建一个专门检测端口扫描的jail,监控iptables的日志输出。首先需要在/etc/fail2ban/filter.d/下新建portscan.conf:
[Definition] failregex = ^.*PORT SCAN DETECTED: .* SRC=.*$ ignoreregex =
然后在/etc/fail2ban/jail.local中添加对应的jail配置:
[portscan] enabled = true filter = portscan logpath = /var/log/kern.log maxretry = 1 findtime = 300 bantime = 7200 action = iptables-multiport[name=portscan, port="1:65535", protocol=tcp]
这个配置会监控内核日志中由iptables LOG规则输出的扫描告警,一旦发现立即封禁整个TCP端口范围,封禁时间2小时。Fail2ban的优势在于动作可定制,除了iptables封禁,还可以执行自定义脚本,比如调用云服务商的API将IP加入安全组黑名单,或者向内部威胁情报平台推送数据。但Fail2ban本身依赖日志解析,存在一定的延迟,对于高频扫描无法做到实时阻断,因此需要与前文的内核级拦截形成纵深防御。
使用nftables原生set实现高性能拦截CentOS 8开始nftables取代iptables成为默认防火墙框架,它的set机制天然支持动态更新,且匹配效率极高。对于需要维护大规模黑名单的场景,nftables的set配合timeout参数可以完全替代ipset+iptables的组合方案,配置更简洁。
# 创建nftables表
nft add table inet portscan_filter
# 创建set用于存储黑名单IP,支持自动过期
nft add set inet portscan_filter blacklist { type ipv4_addr \; timeout 1h \; }
# 添加规则:命中黑名单的直接丢弃
nft add chain inet portscan_filter input { type filter hook input priority 0 \; }
nft add rule inet portscan_filter input ip saddr @blacklist drop
# 手动添加IP到黑名单
nft add element inet portscan_filter blacklist { 192.168.1.100 }
nftables的set支持原子操作,在高并发添加删除元素时不会出现竞态条件,这是相比iptables+ipset方案的重要优势。实际运维中,可以编写一个轻量级的API服务,接收来自IDS、HIDS或SIEM的封禁指令,通过nft命令动态操作set。这种架构下,威胁情报可以实时生效,从检测到阻断的延迟控制在毫秒级。
日志监控与告警联动拦截只是手段,感知才是目的。所有拦截动作都必须产生可审计的日志,并接入集中式日志平台。CentOS系统默认的rsyslog可以将iptables日志、PSAD日志、Fail2ban日志统一转发到Elasticsearch或Loki。建议对端口扫描日志建立专门的仪表盘,监控指标包括:每小时扫描源IP数量、被扫描端口分布、拦截规则触发次数、黑名单容量变化等。
告警规则方面,需要区分不同严重级别。单次触发频率限制的IP可能只是误报或短暂异常,适合记录但不告警。同一IP在24小时内多次被不同规则拦截,或者短时间内有大量IP同时触发拦截规则,则可能是分布式扫描或DDoS前兆,应当立即通过企业微信、钉钉或邮件通知值班人员。告警内容必须包含源IP、扫描类型、触发规则名称、时间戳,方便后续溯源。
还有一个容易被忽视的细节:定期清理过期黑名单。无论使用iptables recent、ipset还是nftables set,都需要确认超时机制正常工作。曾经遇到过因系统时间跳变导致ipset timeout失效,黑名单持续膨胀最终耗尽内存的情况。建议在crontab中增加每周一次的清理任务,并监控黑名单条目数量,设置容量上限告警。
实战中的误判处理与白名单机制任何自动化拦截都可能误伤合法流量,白名单机制是最后的保险。在配置拦截规则时,必须将内网管理IP、监控系统IP、CDN回源IP段、合作伙伴API服务器IP等加入永久白名单。白名单应当在规则链的最顶端匹配,确保不会进入任何检测逻辑。
对于CDN场景需要特别注意,因为用户请求经过CDN节点转发,源站看到的源IP是CDN节点的IP而非真实用户IP。如果CDN节点IP被误封,会导致大范围用户无法访问。解决方案是获取CDN厂商的IP段列表,通过脚本定期更新白名单。以Cloudflare为例,虽然题目要求不涉及具体厂商,但思路是通用的:从官方API或文档获取IP段,转换为防火墙规则。
当误封发生时,快速恢复能力同样重要。建议在防火墙管理脚本中预留紧急回滚功能,一键清空所有非永久规则,或者提供一个临时白名单接口,允许受影响的用户通过备用通道(如带外管理网络)提交解封申请。运维团队内部应当有清晰的误封处理SOP,明确响应时间和操作步骤。
以上这些拦截手段在实际部署时,需要根据服务器的角色和负载情况做裁剪。数据库服务器通常只需保留SSH端口,可以配置非常严格的扫描检测策略。而对外提供Web服务的服务器,必须精细区分正常爬虫和恶意扫描,避免因过度防护影响SEO收录或合作伙伴的API调用。端口扫描拦截没有一劳永逸的配置,持续监控、定期复盘、动态调整才是运维的核心能力。
