← 返回文章列表 专栏:Linux 与系统安全

更新于

Ubuntu 24.04 服务器初始化与 SSH 安全加固:Ed25519 密钥与双端口迁移

记录 Ubuntu 24.04 云主机初始化流程:创建普通 sudo 运维账号、配置 Ed25519 密钥登录、兼容 ssh.socket 的双端口平滑过渡与 UFW 防火墙收敛。

Ubuntu 24.04 SSH 密钥认证与双端口平滑切换架构图
Ubuntu 24.04 SSH 密钥认证与双端口平滑切换架构图

新创建的云服务器接入公网后,几分钟内就会收到针对 22 端口和 root 账户的扫描请求。但如果在修改 SSH 配置时直接改端口、关密码并重启服务,一旦云安全组或本地防火墙漏放行,当前管理会话就会被直接锁在服务器外。

更稳妥的操作顺序是:先创建普通运维账户并验证 Ed25519 公钥登录,再让 SSH 同时监听 22 与新端口,待新终端验证新端口连通后,最后关闭 22 端口与密码认证。

为什么采用双端口过渡加固 SSH

如果直接打开 /etc/ssh/sshd_config,把 Port 22 改成新端口、把 PasswordAuthentication 改成 no,然后执行 systemctl restart ssh,很容易遇到两类问题:

  1. Ubuntu 22.10+ 默认引入了 ssh.socket 套接字激活机制:在不少云镜像中,监听端口由 systemd 的 ssh.socket 单元接管,仅修改 sshd_config 里的 Port 可能不生效,或者在系统重启后出现监听冲突。
  2. 云厂商安全组与主机 UFW 两层防火墙只要漏放行一层,断开当前会话后就只能去云控制台走 VNC 救援终端。
Ubuntu SSH 连接入口与四步验证链结构图
客户端经由 ssh.socket 或 ssh.service 接入服务器;每次变更遵循「语法校验 → 监听核对 → 防火墙放行 → 独立新终端实测」四步流程。

下文命令统一使用四个占位符,执行时替换为实际参数即可:

占位符 含义说明 示例取值规范
<服务器地址> 云服务器公网 IPv4 或管理域名 203.0.113.10
<管理用户名> 新创建的日常运维普通账号(非 root) opsadmin
<新SSH端口> 迁移后的非标高位 TCP 端口 1024–65535 之间未占用端口(如 22022)
<公钥文件路径> 本地工作站生成的 Ed25519 公钥路径 ~/.ssh/id_ed25519_blog.pub

创建普通管理用户并部署 Ed25519 公钥

先在本地电脑终端生成一对用于管理服务器的 Ed25519 密钥(比 RSA 2048 签名更短、握手更快):

1
2
# 在本地电脑执行:生成专用 Ed25519 密钥对(-a 64 增加 KDF 迭代轮数保护私钥)
ssh-keygen -t ed25519 -a 64 -f ~/.ssh/id_ed25519_blog -C "blog-ops-key"

接着使用云主机初始账号登录服务器(保留此终端作为备用窗口 A,在全部验证完成前不要关闭),创建日常运维账号 <管理用户名> 并加入 sudo 组:

1
2
3
4
5
6
7
# 在服务器窗口 A 执行:创建普通运维用户并赋予 sudo 权限
adduser <管理用户名>
usermod -aG sudo <管理用户名>

# 创建 .ssh 目录并设置权限(权限过宽会导致 sshd StrictModes 拒绝读取公钥)
install -d -m 700 -o <管理用户名> -g <管理用户名> /home/<管理用户名>/.ssh
install -m 600 -o <管理用户名> -g <管理用户名> /dev/null /home/<管理用户名>/.ssh/authorized_keys

将本地电脑 ~/.ssh/id_ed25519_blog.pub 的公钥内容写入服务器 /home/<管理用户名>/.ssh/authorized_keys 后,在本地打开第二个终端(测试窗口 B),验证默认 22 端口下的公钥登录与 sudo 提权是否正常:

1
2
3
# 在本地窗口 B 执行:测试新用户公钥登录与 sudo 提权
ssh -i ~/.ssh/id_ed25519_blog <管理用户名>@<服务器地址>
sudo -v && id

确认 uid 为 <管理用户名>、所属组包含 sudo 且无需输入登录密码即可完成公钥登录后,再进入端口迁移阶段。

识别 ssh.socket 并配置双端口监听

从 Ubuntu 22.10 开始,OpenSSH 默认可能采用 systemd 套接字激活(ssh.socket)。先在服务器上运行诊断命令,确认当前负责监听端口的单元:

1
2
sudo systemctl is-active ssh.socket ssh.service
sudo ss -tlnp | grep -E 'sshd|systemd'

无论哪种模式,都按「先同时监听 22 与 <新SSH端口>,验证通过后再移除 22」的顺序操作。

情况 A:ssh.socket 处于 active 状态

当 ssh.socket 处于激活状态时,通过 systemd drop-in 覆盖文件声明双端口监听(第一行空值 ListenStream= 用于清空默认的 22 端口定义):

1
2
3
4
5
6
7
8
9
10
sudo mkdir -p /etc/systemd/system/ssh.socket.d
sudo tee /etc/systemd/system/ssh.socket.d/listen.conf >/dev/null <<'EOF'
[Socket]
ListenStream=
ListenStream=22
ListenStream=<新SSH端口>
EOF

sudo systemctl daemon-reload
sudo systemctl restart ssh.socket

情况 B:ssh.service 直接监听(ssh.socket 为 inactive)

如果系统由 sshd 守护进程直接管理监听,使用 /etc/ssh/sshd_config.d/ 模块化配置目录,避免直接修改主配置文件引发升级冲突:

1
2
3
4
5
6
7
8
9
sudo tee /etc/ssh/sshd_config.d/10-ports.conf >/dev/null <<'EOF'
Port 22
Port <新SSH端口>
EOF

# 先做配置语法与生效值校验,无误后再重载
sudo sshd -t
sudo sshd -T | grep -E '^port '
sudo systemctl reload ssh

配置 UFW 防火墙并验证新端口连通性

确认 sudo ss -tlnp | grep <新SSH端口> 已经处于 LISTEN 状态后,依次在云厂商安全组与主机 UFW 防火墙放行 <新SSH端口>/tcp,同时预先放行后续 Web 服务所需的 80/tcp 与 443/tcp:

1
2
3
4
5
6
7
8
9
10
11
12
sudo apt update && sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing

# 过渡期同时保留 22 与新 SSH 端口,并放行 HTTP/HTTPS 端口
sudo ufw allow 22/tcp comment 'SSH transitional'
sudo ufw allow <新SSH端口>/tcp comment 'SSH primary'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'

sudo ufw enable
sudo ufw status verbose

回到本地电脑的窗口 B,指定 <新SSH端口> 发起新连接,同时可在 ~/.ssh/config 中配置主机别名简化日常输入:

1
2
# 本地窗口 B 直接测试新端口连接
ssh -i ~/.ssh/id_ed25519_blog -p <新SSH端口> <管理用户名>@<服务器地址>
1
2
3
4
5
6
7
8
9
# 本地 ~/.ssh/config 配置示例
Host blog-prod
HostName <服务器地址>
Port <新SSH端口>
User <管理用户名>
IdentityFile ~/.ssh/id_ed25519_blog
IdentitiesOnly yes
ServerAliveInterval 30
ServerAliveCountMax 3

关闭密码认证、禁止 root 登录并移除 22 端口

确认新端口在新窗口连接正常后,收紧认证策略,关闭密码登录与 root 直接 SSH 登录,并缩短未认证连接超时时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sudo tee /etc/ssh/sshd_config.d/20-hardening.conf >/dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 20
ClientAliveInterval 60
ClientAliveCountMax 3
EOF

sudo sshd -t && sudo systemctl reload ssh

接着移除过渡期的 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
2
sudo apt update && sudo apt full-upgrade -y
sudo apt install -y curl git rsync ca-certificates gnupg dnsutils lsof logrotate unattended-upgrades

完成后可通过下表命令核对各项配置是否生效:

检查项目 验证命令 预期输出
有效认证配置 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),不出现密码输入提示