Ubuntu服务器默认开放着不少端口,但真正危险的不是端口本身,而是端口背后运行的服务没有经过严格管控。很多运维人员习惯用“扫一下端口列表然后关掉几个”的方式来做安全加固,这种做法只能挡住最低级的扫描,对于持续性、定向的攻击几乎无效。端口安全管控的核心逻辑,是把“谁能连、从哪连、什么时候连、连上来能干什么”这四个维度全部控制住,而不是简单地开放或关闭。

先搞清楚当前端口真实状况

不要相信自己的记忆,也别只看配置文件。最可靠的方式是用系统工具把当前监听的端口全部拉出来,然后逐一核对。执行以下命令可以拿到完整的监听列表:

ss -tulnp

这条命令输出的每一行都包含协议、本地地址和端口、进程名和PID。重点关注“0.0.0.0”和“::”这类监听地址,它们意味着端口对所有网络接口开放,包括公网。如果某个服务只需要本地访问,却监听在所有接口上,这就是第一个要解决的问题。再执行一遍netstat的等效命令做交叉验证:

netstat -tulpn

两个工具的输出对照着看,能发现一些隐藏的监听。有些恶意进程会替换系统工具来隐藏自己,如果ss和netstat的输出不一致,说明系统可能已经被动了手脚。拿到这份清单后,立即用iptables或nftables的规则列表做对比:

iptables -L -n -v
nft list ruleset

防火墙规则和实际监听端口之间不应该存在“意料之外”的差异。任何在监听但防火墙里没有明确放行的端口,都要立刻标记出来排查。

把服务绑定到正确的网络接口

很多服务默认配置会监听0.0.0.0,这是最容易被忽略的安全隐患。MySQL、Redis、MongoDB这类数据库,如果只需要本地应用访问,就应该只监听127.0.0.1。以MySQL为例,检查配置文件:

grep bind-address /etc/mysql/mysql.conf.d/mysqld.cnf

确保值为127.0.0.1而非0.0.0.0。Redis同样需要检查:

grep bind /etc/redis/redis.conf

如果业务确实需要跨服务器访问数据库,不要直接开放数据库端口,而是在应用层做API封装,或者至少用防火墙把来源IP限制到具体的应用服务器地址。对于SSH这类管理端口,如果有多张网卡,建议只监听在内网管理网段对应的接口上,修改/etc/ssh/sshd_config中的ListenAddress参数即可。改完配置重启服务后,再用ss命令确认监听地址已经变更。

用UFW建立默认拒绝的白名单体系

Ubuntu自带的UFW(Uncomplicated Firewall)足够应对大多数场景。核心原则就一条:默认拒绝所有入站连接,只开放明确需要的端口。执行以下命令建立基线:

ufw default deny incoming
ufw default allow outgoing

然后逐一放行必要的服务端口。注意放行时要尽可能限制来源IP范围,而不是对全世界开放。比如SSH端口:

ufw allow from 192.168.1.0/24 to any port 22 proto tcp

Web服务如果前面有负载均衡或CDN,应该只放行负载均衡节点的IP段:

ufw allow from 10.0.0.0/8 to any port 443 proto tcp

启用UFW前务必确认已经放行了当前SSH会话的IP,否则会把自己锁在外面:

ufw enable
ufw status verbose

status verbose会显示每条规则的详细信息和命中计数,定期查看命中计数能帮助判断哪些规则实际在用,哪些可以清理。对于多网卡服务器,UFW还支持按网络接口设置规则,比如只允许来自内网eth1接口的数据库连接,外网eth0接口完全不监听数据库端口。

用iptables实现更精细的端口访问控制

UFW底层就是iptables,当需要更复杂的控制逻辑时,直接写iptables规则会更灵活。典型的场景包括限制单个IP的并发连接数、防止端口扫描、设置连接速率限制。以下规则限制SSH端口每个IP每分钟最多3个新连接,超过就丢弃:

iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

对于Web服务端口,可以限制单个IP的并发连接数防止CC攻击:

iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 --connlimit-mask 32 -j DROP

端口扫描是攻击者信息收集的常用手段,用以下规则检测并封禁扫描行为:

iptables -A INPUT -p tcp -m multiport --dports 1:65535 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp -m multiport --dports 1:65535 -m state --state NEW -m recent --update --seconds 10 --hitcount 15 -j DROP

这条规则的意思是:如果某个IP在10秒内对任意TCP端口发起了15次以上的新连接尝试,就判定为端口扫描并直接丢弃。iptables规则重启后会丢失,务必保存:

iptables-save > /etc/iptables/rules.v4

或者安装iptables-persistent包来自动保存和恢复规则。

用nftables替代iptables应对复杂场景

Ubuntu较新版本已经将nftables作为默认防火墙框架。nftables语法更简洁,性能更好,规则管理也更方便。如果服务器是新部署的,建议直接使用nftables。创建一个基础的端口安全规则集:

nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }
nft add rule inet filter input iif lo accept
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input tcp dport 22 ip saddr 192.168.1.0/24 accept
nft add rule inet filter input tcp dport { 80, 443 } accept

这个规则集实现了:只允许本地回环接口、已建立连接的回包、来自内网网段的SSH连接、以及HTTP和HTTPS端口。其他所有入站流量全部丢弃。nftables支持集合(set)功能,可以把需要放行的端口和IP地址定义成集合,方便批量管理:

nft add set inet filter allowed_ports { type inet_service \; }
nft add element inet filter allowed_ports { 22, 80, 443, 3306 }
nft add rule inet filter input tcp dport @allowed_ports accept

nftables的规则是原子性加载的,修改规则时不会出现iptables那种短暂的空窗期,这对于生产环境非常重要。

端口敲门技术隐藏敏感端口

对于SSH这类管理端口,最安全的做法是让它从公网上完全不可见,只有知道“暗号”的人才能打开。端口敲门(Port Knocking)就是实现这个目的的技术。原理很简单:服务器默认不开放SSH端口,客户端按照预定顺序访问几个指定端口后,服务器临时放行该IP的SSH连接。Ubuntu上可以用knockd实现:

apt install knockd

编辑/etc/knockd.conf配置文件:

[options]
UseSyslog
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 10
command = /sbin/iptables -A 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

这个配置定义了:客户端在10秒内依次访问7000、8000、9000端口后,服务器会临时放行该IP的SSH连接。要关闭SSH访问,按相反顺序敲门即可。启用knockd并设置开机自启:

systemctl enable knockd
systemctl start knockd

客户端连接前需要用敲门工具发送序列,比如用nmap:

nmap -Pn --max-retries=0 -p 7000,8000,9000 服务器IP

端口敲门不是万能的,序列可以被嗅探,但对于自动化扫描和随机攻击来说,这层防护已经足够让攻击者完全感知不到SSH端口的存在。

持续监控端口变化和异常连接

端口安全不是一次配置就完事的工作。服务器运行过程中,新安装的软件可能自动开放端口,被入侵后攻击者可能开启后门监听。需要建立持续监控机制。最简单的做法是用脚本定期采集监听端口列表并做差异对比:

#!/bin/bash
CURRENT=$(ss -tulnp | sort)
BASELINE="/var/lib/port-monitor/baseline.txt"
if [ ! -f "$BASELINE" ]; then
    echo "$CURRENT" > "$BASELINE"
    exit 0
fi
DIFF=$(diff <(cat "$BASELINE") <(echo "$CURRENT"))
if [ -n "$DIFF" ]; then
    echo "端口变化检测到差异:" | mail -s "端口监控告警" admin@example.com
    echo "$DIFF"
fi

把这个脚本加入crontab每小时执行一次。更专业的做法是用auditd监控涉及端口监听的系统调用:

auditctl -a always,exit -F arch=b64 -S bind -k port-bind

这样任何程序尝试绑定端口都会被记录到审计日志中。同时定期检查当前活跃连接中是否存在可疑的外部IP:

ss -tnp | grep ESTAB

重点关注那些连接了非业务端口、或者连接了境外IP的进程。结合whois查询确认IP归属,发现异常立即断网排查。

内核参数加固端口行为

除了防火墙和应用层配置,Linux内核本身提供了一些参数可以增强端口层面的安全性。编辑/etc/sysctl.conf或/etc/sysctl.d/下的配置文件:

# 禁止响应ICMP重定向,防止路由劫持
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# 禁止发送ICMP重定向
net.ipv4.conf.all.send_redirects = 0

# 启用TCP SYN Cookie防护,防止SYN Flood
net.tcp_syncookies = 1

# 减少TIME_WAIT状态的端口占用,防止端口耗尽
net.ipv4.tcp_tw_reuse = 1

# 缩短TIME_WAIT超时时间
net.ipv4.tcp_fin_timeout = 30

# 限制未完成连接队列的大小
net.ipv4.tcp_max_syn_backlog = 2048

# 启用反向路径过滤,防止IP欺骗
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

这些参数从不同层面增强了端口的安全性。SYN Cookie能有效缓解针对端口的SYN Flood攻击;反向路径过滤可以防止攻击者伪造源IP地址来绕过基于IP的访问控制;TIME_WAIT相关参数则保证了高并发场景下端口资源不会被耗尽。修改后执行sysctl -p使其生效。

服务级别的端口访问控制

防火墙是网络层的控制,但有些访问控制更适合在服务层面实现。比如Nginx可以基于IP限制特定路径的访问,这比在iptables里写规则更灵活:

location /admin {
    allow 192.168.1.0/24;
    deny all;
}

SSH服务本身也支持基于用户和来源IP的访问控制,在/etc/ssh/sshd_config中配置:

AllowUsers admin@192.168.1.* devuser@10.0.0.*
Match Address 0.0.0.0/0
    PermitRootLogin no
    PasswordAuthentication no

这样即使攻击者突破了防火墙,SSH服务本身也会拒绝不符合条件的连接请求。数据库服务如PostgreSQL的pg_hba.conf文件可以精确控制哪些IP能以什么身份连接哪个数据库:

host    all    all    192.168.1.0/24    md5
host    all    all    0.0.0.0/0         reject

多层防护的意义在于:即使某一层被绕过,后续层级仍然能阻止非法访问。单靠防火墙一条防线是不够的,服务自身的访问控制、系统审计、入侵检测系统应该形成纵深防御体系。

端口安全管控的日常运维规范

把端口安全纳入日常运维流程才能保持长期有效。每次部署新应用或更新系统后,必须重新执行端口审计。建立端口开放审批流程,任何新的端口开放需求都要经过安全评估。使用配置管理工具如Ansible或Puppet来统一管理防火墙规则,避免各服务器配置不一致。定期对公网IP进行外部端口扫描,用攻击者的视角检查暴露面:

nmap -sS -p- 服务器公网IP

这条命令会扫描全部65535个TCP端口,耗时较长但能发现所有暴露的服务。结合nmap的版本检测功能可以进一步识别端口背后运行的服务版本是否存在已知漏洞。把扫描结果和内部记录的端口清单做对比,任何差异都要追溯到具体原因。端口安全管控本质上是一个持续的、动态的过程,而不是一次性的配置工作。只有把端口管控融入日常运维的每个环节,才能真正杜绝非法访问。