CentOS服务器暴露在公网后,端口安全管控的优先级远高于安装防火墙本身。很多运维人员以为开了firewalld或者iptables就万事大吉,实际上端口管控的真正难点在于:哪些端口必须开放、哪些端口看似无害实则高危、如何对异常连接做动态封禁。直接讲结论:端口安全的核心不是“拒绝所有”,而是“最小化开放+行为触发封禁”。下面按实际运维场景拆解具体做法。

先做端口资产清点,别凭感觉开放

登录服务器第一件事不是敲命令,而是先列出当前监听的端口和对应进程。执行以下命令:

ss -tulnp | sort -t: -k2 -n

这条命令会按端口号排序显示所有TCP/UDP监听。重点看0.0.0.0和:::开头的行,这些是对所有网卡开放的端口。如果某个服务只应该本机访问,比如Redis、MySQL、Docker API,却监听在0.0.0.0上,这就是直接暴露面。修改服务配置文件把bind地址改成127.0.0.1,比任何防火墙规则都更彻底。对于确实需要对外服务的端口,记录用途、进程名、配置文件路径,形成一份端口清单。没有清单的防火墙策略,后期一定会出现“这个端口是谁开的”这种事故。

用firewalld做基础白名单,不用iptables裸写

CentOS 7及以上默认用firewalld,它底层调用iptables但管理逻辑更清晰。基础策略是:默认zone设为drop,只对需要的服务或端口放行。不要用public zone直接开放端口然后逐条拒绝,那样容易漏。正确做法:

firewall-cmd --set-default-zone=drop
firewall-cmd --permanent --zone=drop --add-port=22/tcp
firewall-cmd --permanent --zone=drop --add-port=80/tcp
firewall-cmd --permanent --zone=drop --add-port=443/tcp
firewall-cmd --reload

这样设置后,任何未明确允许的端口默认全部丢弃。注意,修改默认zone前务必先放行22端口,否则SSH会立即断开。如果服务器有多个网卡或需要针对来源IP做限制,可以在drop zone里用rich rule,比如只允许某个办公网IP访问22端口:

firewall-cmd --permanent --zone=drop --add-rich-rule='rule family="ipv4" source address="203.0.113.5/32" port protocol="tcp" port="22" accept'

这种写法比单纯开放22端口安全得多。很多SSH暴力破解就是扫描全网22端口,限制来源IP能直接消除大部分噪音。

高危端口必须主动封禁,不要等被扫了再反应

有些端口即使业务不用,也建议在防火墙层面显式拒绝并记录日志。常见高危端口包括:23(Telnet)、135/139/445(SMB相关)、1433(SQL Server)、3306(MySQL对外)、5432(PostgreSQL对外)、6379(Redis无认证)、27017(MongoDB无认证)、2375(Docker API明文)。这些端口一旦暴露,轻则被扫描器标记,重则直接被打穿。在firewalld里可以直接加拒绝规则:

firewall-cmd --permanent --zone=drop --add-port=3306/tcp
firewall-cmd --permanent --zone=drop --add-port=6379/tcp
firewall-cmd --permanent --zone=drop --add-port=2375/tcp

这里要注意,drop zone默认就是拒绝,所以加不加这些规则效果一样。但显式添加的意义在于:当你审计防火墙规则时,能清楚看到哪些端口是被主动封禁的,而不是“默认丢弃所以没写”。另外,如果某个服务误配置导致监听在这些端口上,显式拒绝规则不会改变结果,但配合日志规则可以快速发现异常。建议对高危端口加日志:

firewall-cmd --permanent --zone=drop --add-rich-rule='rule port="3306" protocol="tcp" log prefix="DROP_MYSQL " level="info" drop'

这样任何尝试连接3306的请求都会被记录到/var/log/messages,方便溯源。

动态封禁:fail2ban是必备工具,不是可选项

静态防火墙只能解决“开不开端口”,解决不了“有人持续尝试登录”。fail2ban通过监控日志中的失败尝试,自动将来源IP加入临时封禁列表。CentOS上安装:

yum install epel-release -y
yum install fail2ban -y

默认配置只对SSH生效,但实际生产环境需要自定义。创建/etc/fail2ban/jail.local:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
ignoreip = 127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16

[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/secure
maxretry = 3
bantime = 7200

这里把SSH的失败阈值设为3次,封禁2小时。对于Web服务,如果使用了Nginx或Apache,可以启用对应jail监控401/403错误,防止有人爆破后台登录。更进阶的做法是自定义filter,监控应用日志中的特定关键字。比如某个PHP应用登录失败会记录“login failed from IP”,就可以写一个正则匹配并封禁。fail2ban的封禁动作默认用iptables,在firewalld环境下需要确认action设置为firewallcmd-ipset,否则规则可能不生效。

端口敲门和单包授权:适合高敏感场景

如果服务器只允许少数管理员访问SSH,但管理员的IP不固定,可以用端口敲门(Port Knocking)。原理是:先按特定顺序连接几个关闭的端口,服务器检测到正确序列后临时放行22端口。CentOS上可以用knockd实现:

yum install knock-server -y

配置/etc/knockd.conf:

[options]
UseSyslog

[openSSH]
sequence = 7000,8000,9000
seq_timeout = 10
command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn

[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 10
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn

使用时管理员先按顺序敲门,再连接SSH。这种方式隐蔽性很强,但缺点是敲门包可能被中间设备丢弃,而且如果忘记关门规则,端口会一直开着。单包授权(SPA)是更现代的方案,用fwknop实现,加密的单包携带授权信息,比端口敲门更安全。不过对于大多数业务服务器,fail2ban加来源IP限制已经足够,端口敲门属于额外加固手段。

定期审计端口变化,防止服务偷偷监听

服务器运行一段时间后,可能因为安装新软件、升级依赖、误操作等原因,出现新的监听端口。建议每周执行一次端口快照对比。把当前监听端口保存为文件:

ss -tulnp > /root/port_snapshot_$(date +%F).txt

下次对比时用diff查看差异。如果出现未知端口,先查进程再决定是否封禁。另外,用nmap从外部扫描自己的服务器,验证防火墙实际生效的端口和预期是否一致:

nmap -sT -p- --min-rate 1000 服务器公网IP

外部扫描结果和内部监听列表的差异,就是防火墙规则的实际效果。很多运维只在内网自查,看不到公网视角的暴露面,这是常见盲区。

封禁策略的运维细节:避免把自己锁在外面

端口封禁操作中最常见的事故是误封SSH端口导致无法登录。有几个保命技巧:第一,任何防火墙规则修改前,先开一个备用SSH会话保持连接,修改后在新会话里测试,确认没问题再关闭旧会话。第二,使用at命令设置定时回滚:

echo "systemctl stop firewalld" | at now + 10 minutes

如果10分钟内确认规则无误,用atrm取消这个回滚任务。第三,对于云服务器,确保控制台的VNC或串口登录可用,这是最后的救援通道。第四,fail2ban的ignoreip一定要包含自己的常用IP段,否则连续输错几次密码可能把自己封禁。

端口安全管控本质上是一个持续过程,不是一次性配置。最小化开放端口、显式封禁高危端口、动态封禁异常行为、定期审计变化,这四个环节缺一不可。CentOS上的工具链足够成熟,关键是把这些动作固化成运维流程,而不是出了问题才临时处理。