新创建的云服务器接入公网后,几分钟内就会收到针对 22 端口和 root 账户的扫描请求。但如果在修改 SSH 配置时直接改端口、关密码并重启服务,一旦云安全组或本地防火墙漏放行,当前管理会话就会被直接锁在服务器外。
更稳妥的操作顺序是:先创建普通运维账户并验证 Ed25519 公钥登录,再让 SSH 同时监听 22 与新端口,待新终端验证新端口连通后,最后关闭 22 端口与密码认证。
为什么采用双端口过渡加固 SSH
如果直接打开 /etc/ssh/sshd_config,把 Port 22 改成新端口、把 PasswordAuthentication 改成 no,然后执行 systemctl restart ssh,很容易遇到两类问题:
- Ubuntu 22.10+ 默认引入了
ssh.socket套接字激活机制:在不少云镜像中,监听端口由systemd的ssh.socket单元接管,仅修改sshd_config里的Port可能不生效,或者在系统重启后出现监听冲突。 - 云厂商安全组与主机 UFW 两层防火墙只要漏放行一层,断开当前会话后就只能去云控制台走 VNC 救援终端。
下文命令统一使用四个占位符,执行时替换为实际参数即可:
| 占位符 | 含义说明 | 示例取值规范 |
|---|---|---|
<服务器地址> |
云服务器公网 IPv4 或管理域名 | 203.0.113.10 |
<管理用户名> |
新创建的日常运维普通账号(非 root) |
opsadmin |
<新SSH端口> |
迁移后的非标高位 TCP 端口 | 1024–65535 之间未占用端口(如 22022) |
<公钥文件路径> |
本地工作站生成的 Ed25519 公钥路径 | ~/.ssh/id_ed25519_blog.pub |
创建普通管理用户并部署 Ed25519 公钥
先在本地电脑终端生成一对用于管理服务器的 Ed25519 密钥(比 RSA 2048 签名更短、握手更快):
1 | # 在本地电脑执行:生成专用 Ed25519 密钥对(-a 64 增加 KDF 迭代轮数保护私钥) |
接着使用云主机初始账号登录服务器(保留此终端作为备用窗口 A,在全部验证完成前不要关闭),创建日常运维账号 <管理用户名> 并加入 sudo 组:
1 | # 在服务器窗口 A 执行:创建普通运维用户并赋予 sudo 权限 |
将本地电脑 ~/.ssh/id_ed25519_blog.pub 的公钥内容写入服务器 /home/<管理用户名>/.ssh/authorized_keys 后,在本地打开第二个终端(测试窗口 B),验证默认 22 端口下的公钥登录与 sudo 提权是否正常:
1 | # 在本地窗口 B 执行:测试新用户公钥登录与 sudo 提权 |
确认 uid 为 <管理用户名>、所属组包含 sudo 且无需输入登录密码即可完成公钥登录后,再进入端口迁移阶段。
识别 ssh.socket 并配置双端口监听
从 Ubuntu 22.10 开始,OpenSSH 默认可能采用 systemd 套接字激活(ssh.socket)。先在服务器上运行诊断命令,确认当前负责监听端口的单元:
1 | sudo systemctl is-active ssh.socket ssh.service |
无论哪种模式,都按「先同时监听 22 与 <新SSH端口>,验证通过后再移除 22」的顺序操作。
情况 A:ssh.socket 处于 active 状态
当 ssh.socket 处于激活状态时,通过 systemd drop-in 覆盖文件声明双端口监听(第一行空值 ListenStream= 用于清空默认的 22 端口定义):
1 | sudo mkdir -p /etc/systemd/system/ssh.socket.d |
情况 B:ssh.service 直接监听(ssh.socket 为 inactive)
如果系统由 sshd 守护进程直接管理监听,使用 /etc/ssh/sshd_config.d/ 模块化配置目录,避免直接修改主配置文件引发升级冲突:
1 | sudo tee /etc/ssh/sshd_config.d/10-ports.conf >/dev/null <<'EOF' |
配置 UFW 防火墙并验证新端口连通性
确认 sudo ss -tlnp | grep <新SSH端口> 已经处于 LISTEN 状态后,依次在云厂商安全组与主机 UFW 防火墙放行 <新SSH端口>/tcp,同时预先放行后续 Web 服务所需的 80/tcp 与 443/tcp:
1 | sudo apt update && sudo apt install -y ufw |
回到本地电脑的窗口 B,指定 <新SSH端口> 发起新连接,同时可在 ~/.ssh/config 中配置主机别名简化日常输入:
1 | # 本地窗口 B 直接测试新端口连接 |
1 | # 本地 ~/.ssh/config 配置示例 |
关闭密码认证、禁止 root 登录并移除 22 端口
确认新端口在新窗口连接正常后,收紧认证策略,关闭密码登录与 root 直接 SSH 登录,并缩短未认证连接超时时间:
1 | sudo tee /etc/ssh/sshd_config.d/20-hardening.conf >/dev/null <<'EOF' |
接着移除过渡期的 22 端口监听与防火墙规则:
- 若为
ssh.socket模式:删除/etc/systemd/system/ssh.socket.d/listen.conf中的ListenStream=22行,执行sudo systemctl daemon-reload && sudo systemctl restart ssh.socket。 - 若为
ssh.service模式:删除/etc/ssh/sshd_config.d/10-ports.conf中的Port 22行,执行sudo sshd -t && sudo systemctl reload ssh。 - 最后删除 UFW 中的 22 端口放行规则:
sudo ufw delete allow 22/tcp。
基础工具安装与配置核对表
在同一次维护窗口中,一并安装后续网络诊断、容器托管与日志轮转所需的常用工具,并启用安全补丁自动更新:
1 | sudo apt update && sudo apt full-upgrade -y |
完成后可通过下表命令核对各项配置是否生效:
| 检查项目 | 验证命令 | 预期输出 |
|---|---|---|
| 有效认证配置 | sudo sshd -T | grep -E '^(port|permitrootlogin|passwordauthentication)' |
仅输出 <新SSH端口>、permitrootlogin no、passwordauthentication no |
| 实际套接字监听 | sudo ss -tlnp | grep -E ':(22|<新SSH端口>)\b' |
仅 <新SSH端口> 处于 LISTEN,22 端口无监听 |
| 防火墙状态 | sudo ufw status numbered |
仅放行 <新SSH端口>/tcp、80/tcp、443/tcp |
| 密码登录拦截实测 | ssh -p <新SSH端口> -o PubkeyAuthentication=no <管理用户名>@<服务器地址> |
直接返回 Permission denied (publickey),不出现密码输入提示 |