SSH 集中式公钥免密登录配置与权限排坑指南

在多用户或嵌入式边缘设备(如开发板、跳板机、内部测试服务器)管理中,传统的 SSH 免密登录通常将公钥放置在每个用户的家目录 ~/.ssh/authorized_keys 中。然而这种做法在集中运维和安全管控时存在显著痛点:

  1. 权限管控难:普通用户可以随时登入机器随意修改、清空或追加自己的 authorized_keys,管理员难以进行公钥生命周期管理;
  2. 初始化繁琐:新创建的用户尚未生成家目录或尚未初始化 .ssh 时,自动化运维脚本部署公钥较为别扭。

通过配置 OpenSSH 的 AuthorizedKeysFile,我们可以将公钥统一集中存放至 /etc/ssh/authorized_keys/%u,由 root 集中管控,同时支持回退兜底家目录配置。

但在配置集中公钥时,几乎所有人都会踩进一个因 OpenSSH 权限机制引发的隐蔽大坑。本文将从底层工作原理出发,提供完整配置方案、排障调试指南以及 AppArmor 拦截处理。


💡 核心原理与权限暗坑

配置的核心是 sshd_config 中的这一行指令:

1
AuthorizedKeysFile /etc/ssh/authorized_keys/%u .ssh/authorized_keys
  • **%u**:OpenSSH 的内置占位符,代表当前发起登录请求的用户名。当用户 neons 尝试登录时,路径自动展开为 /etc/ssh/authorized_keys/neons;
  • 双路径兜底机制:sshd 会优先查找集中目录 /etc/ssh/authorized_keys/neons,若该文件不存在,则自动 fallback 到用户家目录下的 ~neons/.ssh/authorized_keys。

⚠️ 致命陷阱:temporarily_use_uid 机制

很多运维习惯性地认为:“/etc/ssh 是管理员目录,权限越严格越好”,于是对集中目录执行了 chmod 700、密钥文件执行了 chmod 600。

结果导致:客户端提交公钥时服务端直接拒绝,静默回退要求输入密码!

底层原理解析:

OpenSSH 在进行公钥校验时,处于极度严格的安全设计考量,sshd 守护进程会临时降低权限,切换为目标登录用户的 UID(temporarily_use_uid)去打开并读取公钥文件!

  • 如果目录被设为 700(所有者 root),登录用户(普通用户 UID)根本无权进入该目录(无法 chdir);
  • 如果文件被设为 600(仅 root 可读写),普通用户 UID 同样无权读取该文件内容;
  • sshd 发现“文件读不到”,出于安全考虑既不给详细报错,直接当作“无可用公钥”,在日志中静默记录 ED25519 key is not allowed。

正确的权限设定法则:

存放位置目录推荐权限密钥文件推荐权限属主与所有权
集中目录方案(/etc/ssh/authorized_keys/%u)755 (rwxr-xr-x)644 (rw-r--r--)root:root(关键:防普通用户篡改,但允许读)
传统家目录方案(~/.ssh/authorized_keys)700 (rwx------)600 (rw-------)<user>:<user>(关键:严格防其他用户窥视)

📝 完整配置实战步骤

1. 客户端生成现代 Ed25519 密钥(本地 Mac / Linux)

推荐优先采用 Ed25519 算法(椭圆曲线加密,相比 RSA 体积更小、抗碰撞更强),并建议通过 -f 显式命名以隔离不同主机:

1
2
# 生成密钥,指定注释与保存路径
ssh-keygen -t ed25519 -C "admin-tspi" -f ~/.ssh/id_ed25519_tspi

生成产物:

  • ~/.ssh/id_ed25519_tspi:私钥(严禁外泄);
  • ~/.ssh/id_ed25519_tspi.pub:公钥(用于写入服务端)。

复制公钥内容:

1
cat ~/.ssh/id_ed25519_tspi.pub

2. 服务器创建集中公钥目录

以 root 或 sudo 权限在服务器上创建集中存储目录:

1
sudo mkdir -p /etc/ssh/authorized_keys

3. 写入用户公钥

以目标登录用户名 neons 为文件名,将客户端公钥单行完整写入:

1
2
3
sudo tee /etc/ssh/authorized_keys/neons <<'EOF'
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIClK7l3OUBXKMg9shQtgZi5TlgveIuqLMECvCdKD189x admin-tspi
EOF

注意:公钥必须保持为完整的一整行,中间不得包含多余的换行符或头尾截断。

4. 关键步骤:规范权限与属主

严格按集中式目录要求赋予权限:

1
2
3
4
5
6
7
8
# 目录 755:允许普通用户 uid 穿透目录查找文件,root 独占修改权
sudo chmod 755 /etc/ssh/authorized_keys

# 公钥文件 644:允许所有用户只读,仅 root 能够增删改
sudo chmod 644 /etc/ssh/authorized_keys/neons

# 属主必须锁定为 root:root
sudo chown root:root /etc/ssh/authorized_keys /etc/ssh/authorized_keys/neons

5. 修改 sshd 服务端配置

编辑主配置文件 /etc/ssh/sshd_config(或放入 /etc/ssh/sshd_config.d/):

1
sudo nano /etc/ssh/sshd_config

确保开启公钥认证并指定双路径:

1
2
PubkeyAuthentication yes
AuthorizedKeysFile /etc/ssh/authorized_keys/%u .ssh/authorized_keys

6. 配置语法校验与平滑重载

修改完成后,切记不要盲目直接重启服务,先做语法验证:

1
2
3
4
5
6
# 验证配置语法,若无任何输出表示语法正常
sudo sshd -t

# 平滑重载 sshd(不会中断当前已连接的 SSH 会话)
sudo systemctl reload ssh
# 部分 CentOS/RHEL 发行版命令为:sudo systemctl reload sshd

7. 本地客户端 ~/.ssh/config 快捷配置

在本地电脑上配置 Host 别名,免去每次输入 IP 与参数:

1
2
3
4
Host tspi
HostName 10.0.0.123
User neons
IdentityFile ~/.ssh/id_ed25519_tspi

8. 测试登录

1
ssh tspi

无需输入用户密码即可直接登入。


🚨 深度故障排查与调试(调试神器)

如果配置后客户端 ssh -v tspi 显示发送了公钥(Offering public key),但服务器依然提示输入密码,说明服务端判定该密钥不可用。

1. 启动独立临时端口调试(推荐,不影响现存服务)

千万不要在生产机器上盲目调高主进程日志。在服务器上开一个新终端,运行独立调试端口:

1
sudo /usr/sbin/sshd -ddd -p 2222
  • -ddd:最高详细级别的调试输出,日志直接实时打在终端屏幕上;
  • -p 2222:指定独立测试端口,原 22 端口服务不受任何干扰。

本地客户端指定端口连接测试:

1
ssh -p 2222 -v tspi

2. 核心报错特征与对照排查

观察服务器调试终端的输出,若出现以下错误:

1
2
3
4
5
6
debug1: temporarily_use_uid: 1000/1000 (e=0/0)
debug1: trying public key file /etc/ssh/authorized_keys/neons
debug3: secure_filename: checking '/etc/ssh/authorized_keys'
...
mm_answer_keyallowed: publickey authentication test: ED25519 key is not allowed
Failed publickey for neons from 10.0.0.50 port 54321 ssh2

排查清单:

  1. 检查目录权限:再次确认 /etc/ssh/authorized_keys 是否为 755,误设为 700 会直接导致 Permission denied;
  2. 检查文件权限:再次确认公钥文件是否为 644;
  3. 检查公钥格式:检查文件内是否有多余空行、被拆分成多行、或结尾被截断。

3. Ubuntu 24.04+ AppArmor 拦截排查

在新版 Ubuntu(如 Ubuntu 24.04 LTS / 26.04)上,OpenSSH 默认受到 AppArmor 安全沙箱保护。默认规则可能仅允许 sshd 读取 /etc/ssh/* 下的常规文件,而阻止深入读取子目录。

若调试日志中明确打印底层 Permission denied,可为 AppArmor 添加局部放行规则:

1
2
# 编辑本地补充规则
sudo nano /etc/apparmor.d/local/usr.sbin.sshd

追加允许规则:

1
/etc/ssh/authorized_keys/** r,

重新加载 AppArmor 策略:

1
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.sshd

📌 进阶选型对比:三种公钥管理方案

方案配置方式优点缺点 / 适用场景
集中目录静态文件(本文方案)AuthorizedKeysFile /etc/ssh/authorized_keys/%u简单易懂,纯原生无外部依赖;由管理员统一下发,普通用户无法篡改。公钥文件需设为 644,所有系统用户均可读取公钥内容(公钥本身是公开的,无泄密风险)。适合小微集群与开发板运维。
传统家目录方案AuthorizedKeysFile .ssh/authorized_keys符合个人使用习惯,权限严格隔离(700/600)。管理员无法防范用户私自增删;新用户无家目录时无法预置公钥。
动态集中认证(企业级)AuthorizedKeysCommand /usr/local/bin/get_keys.sh权限最高。由 root 进程调用脚本从 LDAP、Vault 或数据库实时查询公钥,本地不落盘。部署与维护复杂度高,外部查询服务若宕机可能导致全网无法登录。适合大规模云原生机房。

总结

配置 OpenSSH 集中公钥认证的核心关键在于理解 temporarily_use_uid 的权限回退机制:

  • 集中目录需打破惯性思维,目录采用 755、公钥文件采用 644;
  • 利用 AuthorizedKeysFile /etc/ssh/authorized_keys/%u .ssh/authorized_keys 建立“集中管理 + 家目录兜底”的双保险;
  • 善用 sshd -ddd -p 2222 独立调试端口,秒级定位公钥协商失败的根本诱因。

SSH 集中式公钥免密登录配置与权限排坑指南
https://bubao.github.io/posts/fe62447c.html
作者
一念
发布于
2026年11月1日
许可协议