Rocky Linux 如何更改 SSH 端口并配置 SELinux
在 Rocky Linux、AlmaLinux 上将 SSH 改到 2222 端口:按 firewalld、SELinux 标签、sshd_config 的顺序操作,避免 sshd 启动失败或当前会话中断。
为什么在这里更改 SSH 端口需要 3 个步骤
在 Rocky Linux、AlmaLinux、CentOS Stream 或 Fedora 上更改 SSH 端口时,只修改一处是不够的。3 个独立系统都会影响新端口上的连接是否可用。firewalld 决定数据包能否到达计算机。SELinux 决定 sshd 是否有权绑定该端口号。sshd_config 决定守护进程请求使用哪个端口。遗漏 SELinux 步骤时,守护进程会拒绝启动。遗漏 firewalld 步骤时,守护进程会启动并监听端口,但任何人都无法连接。
在 Ubuntu 上,同样的操作只需修改一处并重启。这是因为 Ubuntu 使用 AppArmor,而不是 SELinux,并且没有限制 sshd 可绑定端口的配置文件。如果 Ubuntu 上正在运行 ufw,则添加一条规则即可。区别仅此而已。RHEL 系列在全新安装时会启用 firewalld 并将 SELinux 设为 enforcing,而这两个系统都会检查端口号。
按以下顺序操作,当前会话就能在每一步中保持连接:
- 在 firewalld 中开放新端口,暂时保留端口 22。
- 使用
semanage为新端口添加 SELinux 标签。 - 在 sshd 配置中设置端口。
- 重启
sshd,然后从第二个终端使用新端口登录,再关闭第一个终端。
开始前,找到服务商提供的 Web 控制台(VNC 或串口),并确认可以通过它登录。如果更改出错,该控制台可以帮助您重新登录。更改端口是租户将刚购买的服务器锁在外面的最常见原因之一。
首先安装 semanage
semanage 是用于编辑 SELinux 策略设置的工具,Rocky Linux 或 AlmaLinux 的最小化安装不包含该工具。它位于 policycoreutils-python-utils 中。
sudo dnf install -y policycoreutils-python-utils在安装该软件包前运行此命令会得到 sudo: semanage: command not found。很多读者会在这里误以为未安装 SELinux,因而跳过这一步。实际上,SELinux 已安装,只是缺少管理工具。如果您不熟悉 dnf 语法,可以参考dnf 和 apt 命令对应关系,将其与您已了解的命令对应起来。
选择端口并确认未被占用
1024 到 65535 之间的任意空闲 TCP 端口都可以。确定端口前先执行以下两项检查:
sudo ss -tlnp | grep -w 2222
sudo semanage port -l | grep -w 2222第一项检查端口上是否已有进程监听。第二项检查 SELinux 策略是否已将该端口分配给其他服务类型。空闲端口在两项检查中都不会返回结果。如果策略已占用该端口,第 2 步中的 semanage port -a 会因 ValueError: Port tcp/2222 already defined 失败,此时应选择其他端口。
本指南后续示例均使用 2222。扫描器通常会在检查 22 后首先尝试该端口,因此在实际服务器上应选择不那么明显的端口。
第 1 步:在 firewalld 中开放端口
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports--permanent 会将规则写入磁盘上的区域文件,但不会修改正在运行的防火墙。--reload 会将磁盘上的配置加载到正在运行的防火墙中。如果跳过 reload,规则虽然存在,但在 firewalld 下次重启前不会生效。这是整个流程看似无故失败的最常见原因之一。
暂时不要修改 ssh 服务条目。该条目负责保持端口 22 开放,也是测试期间的回退方案。
同时检查服务提供商的控制面板。许多主机会在 VPS 前端运行网络防火墙,位于操作系统之外。因此,即使已在 firewalld 中开放端口,上游网络防火墙仍可能丢弃该端口的流量。如果您不熟悉区域以及运行时配置与永久配置的区别,请参阅VPS 的 firewalld 基础指南。
第 2 步:为端口设置 SELinux 标签
sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo semanage port -l | grep ssh_port_t-a 添加新的端口分配。-t ssh_port_t 是 SSH 端口使用的类型。第二条命令列出 ssh_port_t 当前涵盖的所有内容,因此您可以先确认指定的端口号已添加,再操作守护进程。
SELinux 为什么会阻止该端口
SELinux(安全增强型 Linux)会为系统中的每个对象分配标签,TCP 端口号也属于对象。SSH daemon 在名为 sshd_t 的域中受限运行。策略允许 sshd_t 绑定标记为 ssh_port_t 的 TCP 端口。默认情况下,只有端口 22 带有该标签。要求 daemon 绑定 2222 时,内核会检查标签,发现该端口被策略分配了某个通用类型,于是拒绝套接字的 name_bind 权限。
因此,这种故障看起来不像防火墙问题。内核会在监听套接字创建之前拒绝请求,所以 sshd 报错并退出。防火墙问题则相反:daemon 正常运行,数据包在进入途中被丢弃。
getenforce 会显示主机当前所处的模式。在 Permissive 下,拒绝操作会被记录但不会强制阻止,因此端口修改看似生效,直到有人运行 setenforce 1,或主机重启并进入 enforcing 模式后才会失败。无论当前使用哪种模式,都应为端口设置正确的标签。服务器 SELinux 基础指南会详细介绍模式、上下文和布尔值。SELinux 影响的不只有端口;在路径重新标记前,同一套策略也会阻止容器读取挂载的主机目录。因此,在 Rocky Linux 或 AlmaLinux 上安装 Docker会包含 SELinux 配置步骤,而 Ubuntu 指南通常不会提及。
第 3 步:在 sshd 配置中设置端口
在 Rocky Linux 9 和 10、AlmaLinux 9 和 10,以及当前版本的 Fedora 中,/etc/ssh/sshd_config 以 include 行开头,因此最适合进行修改的位置是 drop-in 文件。这样,软件包更新就不会覆盖您的修改。
grep -n '^Include' /etc/ssh/sshd_config
echo 'Port 2222' | sudo tee /etc/ssh/sshd_config.d/10-port.conf
sudo sshd -t如果 grep 找不到 Include 行(Rocky Linux 8 和其他较旧镜像通常属于这种情况),请直接将 Port 2222 写入 /etc/ssh/sshd_config。sshd -t 会解析整个配置,包括 drop-in 文件,并报告语法错误。重启前请修复它报告的所有问题,因为配置解析失败会导致 daemon 无法重新启动。
Port 可能出现多次,sshd 会监听列出的每个端口。第一天同时保留 Port 22 和 Port 2222 是一种成本很低的安全措施,但请记得之后将其删除。
您的 sshd 是否由 socket 单元启动?
某些镜像通过 systemd socket 激活启动 SSH,而不是将其作为长期运行的服务启动。使用这种配置时,systemd 负责监听套接字,并将连接交给 sshd,因此 sshd_config 中的 Port 行会被完全忽略。重启任何服务前,请先检查:
systemctl is-enabled sshd.socket如果返回 enabled,说明端口配置在 socket 单元中,而不是 sshd_config 中:
sudo systemctl edit sshd.socket[Socket]
ListenStream=
ListenStream=2222必须使用不带值的 ListenStream=。各 drop-in 文件中的值会累积。如果不先使用空赋值清除列表,socket 仍会同时监听 22 和 2222。使用 sudo systemctl daemon-reload 应用更改,然后执行 sudo systemctl restart sshd.socket。如果该单元在您的服务器上已禁用或不存在,则本节不适用。
第 4 步:重启,然后从第二个终端测试
sudo systemctl restart sshd
systemctl status sshd
sudo ss -tlnp | grep sshd保持此终端打开,不要退出登录。在您自己的计算机上打开第二个终端,并通过新端口连接:
ssh -p 2222 youruser@203.0.113.10只有在第二次登录成功后,才能关闭第一个会话。如果连接失败,您仍保留着一个可用于撤销所有更改的 shell。养成这个习惯,可以避免只能使用服务商控制台处理问题,将一次 5 分钟的更改变成耗时一下午的故障排查。
防火墙丢弃还是 SELinux 拒绝?如何区分
从笔记本上看,这两种故障几乎完全相同。在服务器上,它们的表现完全不同。
- 如果
systemctl status sshd显示单元失败,说明守护进程根本没有获取到其套接字。这是配置错误或 SELinux 拒绝。 - 如果单元处于 active 状态,且
ss -tlnp显示 sshd 已绑定到新端口,说明守护进程正常,问题出在网络路径上:firewalld、服务商单独提供的防火墙,或者您连接时使用的地址和端口。
对于 SELinux 情况,应读取审计记录,而不是猜测:
sudo ausearch -m AVC -ts recent
sudo journalctl -u sshd -n 50 --no-pagername_bind 中针对 class tcp_socket 的拒绝记录,会在 comm="sshd" 中列出进程,在 src= 中列出端口号,并在 tcontext= 中列出该端口实际携带的标签。最后一个字段就是答案。只要不是 ssh_port_t,就说明第 2 步没有应用到您使用的端口,通常是端口号输入错误或协议不正确。如果希望由 sealert 将记录转换为句子,请安装 setroubleshoot-server。
内核拒绝绑定时,sshd 自身写入的消息如下:
error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.在高于 1024 的端口上,绑定不需要 root 权限;此时出现 Permission denied 就是 SELinux 的特征。相同行中的 Address already in use 表示另一种故障:另一个进程正在占用该端口。从客户端看,连接被拒绝与连接超时的区别可以区分这两种网络情况,因为连接被拒绝表示数据包已到达主机,但没有进程在监听;连接超时则表示完全没有收到响应。
关闭 22 端口并更新客户端
确认新端口上的多次登录均已成功后,移除 22 端口:
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all不要修改 22 端口上的 SELinux 标签。该标签来自基础策略。防火墙停止允许数据包进入后,它不会再授予任何权限。
然后修复客户端。所有默认使用该端口的工具现在都需要指定新端口。将配置写入您自己计算机上的 ~/.ssh/config,这样只需设置一次,不必永远输入 -p:
Host myvps
HostName 203.0.113.10
Port 2222
User youruserscp、sftp、rsync 和 Ansible 都会读取该文件。备份任务、监控检查和将 22 端口硬编码的 cron 脚本不会读取该文件,因此应趁这次变更仍记忆清晰时找出并修改它们。
修改端口能解决什么,不能解决什么
这样可以减少日志噪声。自动扫描器会持续扫描 22 端口,改用其他端口后,journal 中的大部分此类记录都会消失,更容易发现真正的事件。这不是安全控制措施。任何扫描完整端口范围的扫描器都能找到您的 daemon,并读取其版本标识。应将修改端口视为日常维护,真正的防护应使用仅密钥认证并关闭密码登录,VPS 的 SSH 加固指南会逐步介绍具体操作。
以上内容在两个主要的 RHEL 重构发行版上完全相同,因为它们基于相同的源代码构建。如果您仍在 Rocky Linux 和 AlmaLinux 之间选择,请参阅Rocky Linux 与 AlmaLinux 对比。之所以现在有两个几乎相同的重构发行版可供选择,是因为 CentOS 在 2020 年不再保持原有定位,从 Red Hat 到 CentOS,再到 Rocky 和 AlmaLinux 的历史完整讲述了这一变化。在遵循任何较早的指南前,请先使用 cat /etc/os-release 检查实际获得的发行版版本。针对 Rocky Linux 8 编写的指南目前仍有较高参考价值,其中的 semanage 和 firewall-cmd 步骤仍然正确;但 Rocky 8 没有 sshd_config.d include 行,也没有需要考虑的 socket unit,因此这些指南中关于 sshd 的部分不适用于当前系统。
fail2ban 必须配置为使用新端口
fail2ban 不在基础软件仓库中。它来自 EPEL(适用于企业 Linux 的额外软件包):
sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-firewalldfail2ban-firewalld 子软件包会让 fail2ban 通过 firewalld 写入封禁规则。如果由 firewalld 管理规则集,应使用此子软件包。
默认的 sshd jail 将 port = ssh 设置为 /etc/services,该名称解析为 22。修改端口后,该 jail 监视的是无人攻击的端口,因此不会封禁任何地址,而针对 2222 的失败登录会不断累积。在 /etc/fail2ban/jail.local 中按端口号设置端口:
[sshd]
enabled = true
port = 2222
backend = systemd
maxretry = 5
bantime = 3600backend = systemd 从 journal 读取失败记录,而不是从 /var/log/secure 读取。这是在最小化安装中更安全的选择,因为此类安装可能没有 rsyslog。使用 sudo systemctl enable --now fail2ban 启动它,然后使用 sudo fail2ban-client status sshd 检查该 jail。jail 语法与 Ubuntu 24.04 上的 fail2ban SSH 配置相同。不同之处只有软件包来源和封禁操作。
补丁比端口更重要
一台更改了 SSH 端口、却有四个月未应用安全更新的服务器,其安全状况比一台使用 22 端口、每天夜间自动安装补丁的服务器更差。您已经是 root,因此应在同一会话中启用无人值守更新:Rocky Linux 和 AlmaLinux 上的自动 dnf 更新介绍如何配置定时器,以及选择仅下载更新还是直接应用更新。已安装的更新不会重启仍在运行旧代码的守护进程。因此,每当更新批次包含 openssh-server 或其链接的库时,都值得花一分钟检查仍需重启的服务或系统。
FAQ
Rocky Linux 上修改端口后,为什么 sshd 无法启动?
几乎总是因为缺少 SELinux 端口标签。sshd 在 sshd_t 域中受限运行,策略只允许它绑定标记为 ssh_port_t 的端口;默认情况下,该标签只应用于端口 22。内核拒绝绑定操作,因此守护进程退出,而不是继续监听,journalctl -u sshd 中会包含类似 error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. 的行。运行 sudo semanage port -a -t ssh_port_t -p tcp 2222,并替换为您自己的端口号,然后重启服务。如果找不到 semanage,请先安装 policycoreutils-python-utils。
SELinux 处于 permissive 模式时,还需要 semanage 吗?
需要。在 permissive 模式下,拒绝事件会被记录,但绑定操作仍会被允许,因此看起来修改已经生效。端口标签实际上仍然缺失。一旦有人运行 setenforce 1,或主机启动时 SELINUX=enforcing 位于 /etc/selinux/config 中,sshd 就会无法在该端口启动。添加标签只需执行一条命令,可以消除一个可能在数周后才出现且没有明显原因的故障。
端口已有标签且 sshd 正在运行,为什么连接仍然超时?
守护进程正在运行,说明 SELinux 没有阻止该端口,因此数据包是在进入主机的途中被丢弃的。检查 sudo firewall-cmd --list-ports 中是否允许您的端口,并确认在添加 --permanent 规则后运行了 firewall-cmd --reload,因为仅添加永久规则不会影响当前正在运行的防火墙。然后检查主机控制面板,确认 VPS 前方是否还有单独的网络防火墙。这是导致连接被阻断的第二个位置,操作系统内部不会显示相关信息。
应该使用哪个端口来替代 22?
使用 1024 到 65535 之间任意未占用的 TCP 端口。在真实服务器上应避免使用 2222 和 22222,因为扫描器通常会在扫描 22 后立即尝试这些端口。使用 sudo ss -tlnp 确认端口未被占用,使用 sudo semanage port -l 确认 SELinux 策略尚未声明该端口,并避开以后可能安装的服务所使用的端口。使用较高且不易记忆的端口也没有问题,因为只需将其写入 ~/.ssh/config 一次,以后无需再次手动输入。