SSH 集中式公钥免密登录配置与权限排坑指南
在多用户或嵌入式边缘设备(如开发板、跳板机、内部测试服务器)管理中,传统的 SSH 免密登录通常将公钥放置在每个用户的家目录 ~/.ssh/authorized_keys 中。然而这种做法在集中运维和安全管控时存在显著痛点:
- 权限管控难:普通用户可以随时登入机器随意修改、清空或追加自己的
authorized_keys,管理员难以进行公钥生命周期管理; - 初始化繁琐:新创建的用户尚未生成家目录或尚未初始化
.ssh时,自动化运维脚本部署公钥较为别扭。
通过配置 OpenSSH 的 AuthorizedKeysFile,我们可以将公钥统一集中存放至 /etc/ssh/authorized_keys/%u,由 root 集中管控,同时支持回退兜底家目录配置。
但在配置集中公钥时,几乎所有人都会踩进一个因 OpenSSH 权限机制引发的隐蔽大坑。本文将从底层工作原理出发,提供完整配置方案、排障调试指南以及 AppArmor 拦截处理。
💡 核心原理与权限暗坑
配置的核心是 sshd_config 中的这一行指令:
1 | |
- **
%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 | |
生成产物:
~/.ssh/id_ed25519_tspi:私钥(严禁外泄);~/.ssh/id_ed25519_tspi.pub:公钥(用于写入服务端)。
复制公钥内容:
1 | |
2. 服务器创建集中公钥目录
以 root 或 sudo 权限在服务器上创建集中存储目录:
1 | |
3. 写入用户公钥
以目标登录用户名 neons 为文件名,将客户端公钥单行完整写入:
1 | |
注意:公钥必须保持为完整的一整行,中间不得包含多余的换行符或头尾截断。
4. 关键步骤:规范权限与属主
严格按集中式目录要求赋予权限:
1 | |
5. 修改 sshd 服务端配置
编辑主配置文件 /etc/ssh/sshd_config(或放入 /etc/ssh/sshd_config.d/):
1 | |
确保开启公钥认证并指定双路径:
1 | |
6. 配置语法校验与平滑重载
修改完成后,切记不要盲目直接重启服务,先做语法验证:
1 | |
7. 本地客户端 ~/.ssh/config 快捷配置
在本地电脑上配置 Host 别名,免去每次输入 IP 与参数:
1 | |
8. 测试登录
1 | |
无需输入用户密码即可直接登入。
🚨 深度故障排查与调试(调试神器)
如果配置后客户端 ssh -v tspi 显示发送了公钥(Offering public key),但服务器依然提示输入密码,说明服务端判定该密钥不可用。
1. 启动独立临时端口调试(推荐,不影响现存服务)
千万不要在生产机器上盲目调高主进程日志。在服务器上开一个新终端,运行独立调试端口:
1 | |
-ddd:最高详细级别的调试输出,日志直接实时打在终端屏幕上;-p 2222:指定独立测试端口,原 22 端口服务不受任何干扰。
本地客户端指定端口连接测试:
1 | |
2. 核心报错特征与对照排查
观察服务器调试终端的输出,若出现以下错误:
1 | |
排查清单:
- 检查目录权限:再次确认
/etc/ssh/authorized_keys是否为755,误设为700会直接导致Permission denied; - 检查文件权限:再次确认公钥文件是否为
644; - 检查公钥格式:检查文件内是否有多余空行、被拆分成多行、或结尾被截断。
3. Ubuntu 24.04+ AppArmor 拦截排查
在新版 Ubuntu(如 Ubuntu 24.04 LTS / 26.04)上,OpenSSH 默认受到 AppArmor 安全沙箱保护。默认规则可能仅允许 sshd 读取 /etc/ssh/* 下的常规文件,而阻止深入读取子目录。
若调试日志中明确打印底层 Permission denied,可为 AppArmor 添加局部放行规则:
1 | |
追加允许规则:
1 | |
重新加载 AppArmor 策略:
1 | |
📌 进阶选型对比:三种公钥管理方案
| 方案 | 配置方式 | 优点 | 缺点 / 适用场景 |
|---|---|---|---|
| 集中目录静态文件(本文方案) | 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独立调试端口,秒级定位公钥协商失败的根本诱因。
