SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor

Rocky Linux如何更改SSH端口并配置SELinux和firewalld

在 Rocky Linux、AlmaLinux 上更改 SSH 端口,需依次配置 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 可绑定端口的配置文件。如果系统正在运行 ufw,则添加一条规则即可。区别仅此而已。RHEL 系列在全新安装后默认运行 firewalld,并启用 SELinux enforcing 模式;两者都会检查端口号。

按以下顺序操作,可以确保当前会话在每一步中都保持连接:

  1. 在 firewalld 中开放新端口,暂时保留端口 22 处于开放状态。
  2. 使用 semanage 为新端口添加 SELinux 标签。
  3. 在 sshd 配置中设置端口。
  4. 重启 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 守护进程在名为 sshd_t 的域中受限运行。策略允许 sshd_t 绑定标记为 ssh_port_t 的 TCP 端口。开箱即用时,只有端口 22 使用该标签。让守护进程绑定 2222 时,内核会检查该端口的标签,发现策略为该端口号分配的是某个通用类型,于是拒绝套接字上的 name_bind 权限。

因此,这类故障看起来不像防火墙问题。内核会在监听套接字创建之前拒绝操作,所以 sshd 会报告错误并退出。防火墙问题则相反:守护进程正常运行,数据包在进入途中被丢弃。

getenforce 会告诉您系统当前处于哪种模式。在 Permissive 模式下,拒绝操作会被记录但不会强制执行,因此端口修改看似生效,直到有人运行 setenforce 1,或系统重启并进入强制执行模式后才会出现故障。无论当前处于哪种模式,都应为端口设置正确的标签。服务器 SELinux 基础指南会详细介绍模式、上下文和布尔值。

步骤 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_configsshd -t 会解析完整配置,包括 drop-in 文件,并报告语法错误。重启前修复它报告的所有问题,因为配置解析失败会导致守护进程无法重新启动。

Port 可以出现多次,sshd 会监听列出的每个端口。第一天保留 Port 22Port 2222 是一种成本很低的安全保障,但请记得之后将其删除。

sshd 是否由 socket 单元启动?

某些镜像通过 systemd socket 激活启动 SSH,而不是将其作为长期运行的服务启动。在这种配置中,systemd 管理监听 socket,并将连接交给 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。养成这一习惯,可以避免必须使用服务商控制台处理问题,从而将一次五分钟的修改与耗费整个下午的故障处理区分开。

防火墙丢弃还是 SELinux 拒绝?如何区分

从笔记本上看,这两种故障几乎完全相同。在服务器上,它们的表现完全不同。

  • 如果 systemctl status sshd 显示单元启动失败,说明守护进程根本没有获取到套接字。这通常是配置错误或 SELinux 拒绝。
  • 如果单元处于 active 状态,并且 ss -tlnp 显示 sshd 已绑定到新端口,说明守护进程正常,问题出在网络路径上:可能是 firewalld、服务商单独提供的防火墙,或您连接时使用的地址和端口不正确。

对于 SELinux 情况,应读取审计记录,而不是猜测:

sudo ausearch -m AVC -ts recent
sudo journalctl -u sshd -n 50 --no-pager

name_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 标签。该标签来自基础策略;防火墙停止允许数据包进入后,它不会再授予任何权限。

然后修复客户端配置,因为所有默认使用端口 22 的工具现在都需要指定新端口。只需在您自己的计算机上的 ~/.ssh/config 中配置一次,无需一直输入 -p

Host myvps
  HostName 203.0.113.10
  Port 2222
  User youruser

scpsftprsync 和 Ansible 都会读取该文件。硬编码端口 22 的备份任务、监控检查和 cron 脚本不会读取,因此应趁着刚完成更改,立即找出并更新这些配置。

更改端口能做什么,不能做什么

它可以减少日志噪声。自动扫描器会持续攻击 22 端口。改用其他端口后,日志中这类记录会大幅减少,更容易查看真正的事件。这不是安全控制措施。任何扫描完整端口范围的扫描器都能找到您的 daemon,并读取其版本标识。应将更改端口视为日常维护。真正的保护措施应是仅使用密钥进行身份验证,并关闭密码登录。VPS 的 SSH 加固指南会逐步介绍这一过程。

上述内容在两种主流 RHEL 重构版上完全相同,因为它们基于相同的源代码构建。如果您仍在 Rocky Linux 和 AlmaLinux 之间选择,请参阅Rocky Linux 与 AlmaLinux 对比。在按照较旧的指南操作前,请先使用 cat /etc/os-release 检查实际安装的发行版版本。面向 Rocky Linux 8 编写的指南目前仍有较高参考价值,其中的 semanagefirewall-cmd 步骤仍然正确。但 Rocky 8 没有 sshd_config.d include 行,也没有需要考虑的 socket unit。因此,这些指南中关于 sshd 的部分不适用于当前系统。

需要告知 fail2ban 使用新端口

fail2ban 不在基础软件仓库中。它来自 EPEL(Enterprise Linux 的额外软件包):

sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-firewalld

fail2ban-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 = 3600

backend = 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 更新介绍如何配置定时器,以及如何选择仅下载更新还是直接应用更新。

FAQ

修改 Rocky Linux 上的端口后,为什么 sshd 无法启动?

几乎总是因为缺少 SELinux 端口标签。sshdsshd_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 一次,以后无需再次输入。