authorizedkeyscommand 需满足四前提:绝对可执行路径、显式指定运行用户(如 ssh-keymgr)、输出严格为每行一个无空格/空行的公钥、脚本权限与selinux标签合规;配置后需禁用 authorizedkeysfile 并重启 sshd。

AuthorizedKeysCommand 是 OpenSSH 提供的机制,用于在用户登录时动态获取其公钥,而不是从固定文件读取。它适合集中化密钥管理场景,比如把所有公钥统一存进数据库、LDAP 或 API 服务。但直接启用容易失败——关键不在“能不能用”,而在于“怎么配才不掉坑”。
配置 AuthorizedKeysCommand 的核心前提
必须满足以下四点,否则 SSH 登录会静默拒绝或报错“Authentication refused”:
-
命令路径必须绝对且可执行:sshd 要求脚本路径为完整绝对路径(如
/usr/local/bin/ssh-key-lookup),不能是相对路径或带空格的路径 -
运行用户必须显式指定:默认以 root 运行,但多数数据库查询逻辑依赖登录用户名(如查
SELECT key FROM ssh_keys WHERE username = ?)。若不设AuthorizedKeysCommandUser,脚本拿不到 $1(用户名参数)或查不到数据 -
输出格式必须严格合规:每行一个完整公钥,开头是
ssh-rsa、ssh-ed25519等类型标识,末尾不能有空格、制表符或空行;OpenSSH 8.8+ 遇到空行会跳过后续所有密钥 -
权限与 SELinux 必须放行:脚本本身需有
x权限,且属主不能是 root(除非明确允许);SELinux 下需打上ssh_exec_t标签,否则被拦截
一个安全可用的 lookup 脚本示例
假设用 SQLite 存密钥(生产环境建议换 PostgreSQL/MySQL),脚本 /usr/local/bin/ssh-key-lookup 内容如下:
#!/bin/bash
# 参数 $1 是登录用户名,由 sshd 自动传入
USER=$1
DB="/var/lib/ssh-keys/keys.db"
<h1>检查用户是否存在且密钥未过期(created_at 和 last_used 可审计)</h1><p>sqlite3 -batch "$DB" datetime('now'));
EOF
</p>
注意:
- 不做输入过滤($1 由 sshd 保证是合法用户名,无需额外 sanitize)
- 输出只有一列(key 字段),无列名、无空行、无多余空格
- 数据库路径需提前创建并设好权限(如 chown ssh-keymgr:ssh-keymgr /var/lib/ssh-keys/keys.db)
sshd_config 关键配置项
在 /etc/ssh/sshd_config 中添加或修改这几行:
AuthorizedKeysCommand /usr/local/bin/ssh-key-lookup AuthorizedKeysCommandUser ssh-keymgr PubkeyAuthentication yes # 禁用传统文件方式,避免绕过动态查询 AuthorizedKeysFile none
然后重启服务:sudo systemctl restart sshd
⚠️ 别忘了先用 sshd -t 检查语法是否正确
审计与排错要点
动态密钥不是“设完就完”,得能追踪、能验证、能应急:
-
日志要开细粒度:在
sshd_config加SyslogFacility AUTHPRIV和LogLevel VERBOSE,登录失败时能看到 “Failed publickey for …” 及具体哪步出错 -
数据库字段必须含时间戳:至少存
created_at和last_used。没last_used就无法识别长期未用的僵尸密钥,清理风险高 -
测试不能只测成功路径:手动执行
sudo -u ssh-keymgr /usr/local/bin/ssh-key-lookup testuser,确认返回值和格式;再用错误用户名测试是否静默无输出(不该报错,只该空返回) -
禁用共享 authorized_keys 文件:一旦启用了 AuthorizedKeysCommand,就别再往任何用户的
~/.ssh/authorized_keys里写东西,否则权限模型混乱,审计失效











