上一篇完成了系统、补丁、时间和内核基线,本篇把“谁能登录、登录后能做什么”纳入控制。目标不是简单关闭 root,而是建立可追责的个人账号、受限的 sudo 权限与可验证的 SSH 密钥链,并保证切换过程中始终保留恢复通道。
一、痛点:共享 root 无法审计
多人共用 root 密码时,日志只能说明 root 做了操作,无法定位个人;密码泄漏后也很难只撤销一人。正确模型是每位管理员使用独立账号和密钥,需要特权时经 sudo 提升。服务进程使用不可交互的系统账号,文件归属与进程身份分离。人员离职时锁定个人账号即可,不必轮换所有机器的公共密码。
Linux 权限由用户、组和其他人的读写执行位组成。目录的执行位代表“可穿越”,不是运行程序;缺少它,即使文件可读也无法访问。umask 027 会让新文件默认只对所有者可读写、同组可读、其他人无权,但不能修正已有文件,也不能覆盖应用显式设置。更复杂的共享目录可使用 setgid 继承组,ACL 则适合少量例外;规则过多会让 ls -l 难以解释,应谨慎使用。
二、原理:认证、授权与主机身份
SSH 有两个容易混淆的方向。服务器用 authorized_keys 验证“客户端是谁”;客户端用 known_hosts 验证“服务器是谁”。只上传公钥却忽略主机指纹,仍可能把凭据交给中间人。首次连接应通过云控制台、资产平台或另一可信通道核对指纹,而不是看到提示就输入 yes。
私钥永远留在管理端。Ed25519 密钥短、性能好,是现代 OpenSSH 的常用选择;若受旧合规环境限制再选择 RSA。私钥应有口令,并交给 ssh-agent 临时解锁。Agent forwarding 会把签名能力暴露给远端 root,跳板场景优先 ProxyJump,不轻易开启转发。
sudo 的价值在于最小授权和记录命令,但规则若放行编辑器、Shell、包管理器或能指定任意输出路径的工具,往往等同完整 root。用 /etc/sudoers.d/ 拆分规则并通过 visudo -cf 校验,避免语法错误导致所有管理员失去提权能力。
三、实现:安全创建管理员并切换 SSH
先创建账号、安装公钥并校验格式。把实际公钥作为第一个参数传入,避免写死身份。脚本不会立刻禁用现有登录;执行后务必从另一个终端验证新账号能登录和 sudo,再进入第二阶段。
#!/usr/bin/env bash
set -euo pipefail
[[ "$EUID" -eq 0 ]] || { echo '请使用 root 运行' >&2; exit 1; }
[[ $# -eq 2 ]] || { echo "用法: $0 USER 'PUBLIC_KEY'" >&2; exit 2; }
user="$1"
public_key="$2"
[[ "$user" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] || { echo '用户名不合法' >&2; exit 2; }
[[ "$public_key" == ssh-ed25519\\ * || "$public_key" == ssh-rsa\\ * ]] || {
echo '仅接受 OpenSSH 公钥行' >&2; exit 2;
}
if ! id "$user" >/dev/null 2>&1; then
useradd –create-home –shell /bin/bash "$user"
fi
usermod -aG sudo "$user"
home="$(getent passwd "$user" | cut -d: -f6)"
install -d -m 0700 -o "$user" -g "$user" "$home/.ssh"
printf '%s\\n' "$public_key" > "$home/.ssh/authorized_keys"
chown "$user:$user" "$home/.ssh/authorized_keys"
chmod 0600 "$home/.ssh/authorized_keys"
printf '%s ALL=(ALL:ALL) ALL\\n' "$user" > "/etc/sudoers.d/90-$user"
chmod 0440 "/etc/sudoers.d/90-$user"
visudo -cf "/etc/sudoers.d/90-$user"
passwd -l "$user" >/dev/null
echo "created=$user home=$home sudo=enabled"
运行输出:
/etc/sudoers.d/90-alice:解析正确
created=alice home=/home/alice sudo=enabled
确认新会话成功后,再写 SSH 配置片段。sshd -t 先做静态校验,reload 让已有连接不被强制中断。不同发行版是否默认包含 sshd_config.d 要先用 sshd -T 和主配置确认。云镜像还可能用 cloud-init 写规则,必须检查最终生效值。
#!/usr/bin/env bash
set -euo pipefail
[[ "$EUID" -eq 0 ]] || { echo '请使用 root 运行' >&2; exit 1; }
config=/etc/ssh/sshd_config.d/60-hardening.conf
install -d -m 0755 /etc/ssh/sshd_config.d
cp -a "$config" "$config.bak" 2>/dev/null || true
cat > "$config" <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 4
LoginGraceTime 30
X11Forwarding no
AllowAgentForwarding no
EOF
if ! sshd -t; then
test -f "$config.bak" && mv "$config.bak" "$config"
echo '配置校验失败,已回滚' >&2
exit 1
fi
systemctl reload ssh || systemctl reload sshd
sshd -T | awk '/^(permitrootlogin|passwordauthentication|pubkeyauthentication) /'
运行输出:
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
四、踩坑:锁密码不等于禁止 SSH
passwd -l 锁定的是密码哈希,公钥认证仍可使用,这正适合密钥管理员账号。服务账号则应配 /usr/sbin/nologin、无授权密钥,并限制文件所有权。删除账号前先检查它拥有的进程、定时任务、文件和 systemd 单元;直接 userdel -r 可能删除仍需保留的数据。
不要给整个运维组配置无条件 NOPASSWD: ALL。密码提示能减少误操作和被盗会话直接提权,虽然它不是第二因素。自动化确需免密时,只授权固定绝对路径、固定参数和专用脚本;脚本本身必须由 root 拥有且不可被授权用户修改。
五、验证:从客户端到服务端闭环
客户端先运行 ssh-keygen -t ed25519 -a 64,再核对 ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub 的服务端指纹。服务端用 sshd -T 查看合并后的最终配置,因为同名指令、Match 块和发行版片段可能覆盖直觉。测试新账号登录、错误密钥拒绝、密码拒绝、root 拒绝以及 sudo 成功,并保存一个控制台恢复入口。
身份边界建立后,应用不应靠 nohup 或管理员的终端维持。下一篇会创建独立服务用户,用 systemd 描述依赖、重启、资源限制、日志和定时器。
参考来源
- OpenSSH:sshd_config 手册
- OpenSSH:ssh-keygen 手册
- Sudoers Manual
- Ubuntu Server:OpenSSH
👍 觉得有用就点个 赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。
🚀 本文属于 《Linux 服务器运维实战》 系列,持续更新,关注不迷路。
📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到 cj2664@qq.com,我免费发你。 如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。






