SSH 密钥是日常运维 / git 协作的基础。新人常踩的坑:
- 一个 key 用到所有地方
- 私钥没加密
- 失去机器后忘了删 key 的服务端授权
- 多个机器多个 key 没区分
下面是一套实用的 SSH 密钥管理流程。
1. 生成密钥
ssh-keygen -t ed25519 -C 'name@laptop' -f ~/.ssh/id_ed25519
# 提示 passphrase:建议设一个强密码
-t ed25519 推荐;rsa 4096 兼容性最广但比 ed25519 慢。
-C 是注释,写"哪个用户在哪台机器",方便服务端管理识别。
-f 指定文件名。我习惯:
~/.ssh/id_ed25519个人主密钥(用得最多)~/.ssh/id_ed25519_work工作账号~/.ssh/id_ed25519_githubGitHub 专用
2. 一个 key 不要到处用
理由:
- 一处泄露 → 所有地方都泄露
- 不同身份混淆(个人 git / 公司 git / VPS)
- 撤销时全部更新很麻烦
按维度分:
- 个人 vs 公司
- 永久机器(笔记本)vs 临时机器(VPS / 开发容器)
- GitHub / GitLab 各一把(很多 git host 拒绝重复 key)
3. ssh-agent:缓存解锁的私钥
# 启 agent(GNOME / KDE 默认已经起好)
eval $(ssh-agent)
# 加 key(要输 passphrase 一次)
ssh-add ~/.ssh/id_ed25519
ssh-add ~/.ssh/id_ed25519_work
# 看已加载的 key
ssh-add -l
# 256 SHA256:xxx... name@laptop (ED25519)
# 删某个
ssh-add -d ~/.ssh/id_ed25519_work
# 全删(退出前清干净)
ssh-add -D
之后所有 ssh / scp / git 不再要密码。
4. agent 自动启动 + key 自动加载
~/.zshrc 或 ~/.bashrc:
# 启 ssh-agent 如果没运行
if [ -z "$SSH_AUTH_SOCK" ]; then
eval $(ssh-agent -s) >/dev/null
fi
# 自动加载尚未加载的 key
if ! ssh-add -l 2>/dev/null | grep -q "$HOME/.ssh/id_ed25519"; then
ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi
macOS 用 keychain 集成更优雅:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
密码存系统 keychain,重启后自动加载。
5. authorized_keys 服务端
每台机器的 ~/.ssh/authorized_keys 是允许登录的公钥列表。
推送公钥的标准方式:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# 自动追加到 ~user/.ssh/authorized_keys + 设权限
手动方式(如果 ssh-copy-id 不可用):
cat ~/.ssh/id_ed25519.pub | ssh user@server \
'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
6. 给授权 key 加限制
服务端 authorized_keys 每行前面可以加 options:
# 只允许从某 IP
from="203.0.113.5" ssh-ed25519 AAAA... user@laptop
# 不允许 shell / 端口转发 / agent forward;只能跑特定命令
command="/usr/local/bin/backup.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAA... backup-runner
# 过期时间
expiry-time="20251231" ssh-ed25519 AAAA... contractor
command 限制是给 backup / 监控这种"只跑一个脚本"的服务账号用的。
登录的人开 shell 也只跑这个命令,比开 shell 安全得多。
7. 定期审计
服务端列所有授权 key:
for u in $(cut -d: -f1 /etc/passwd); do
if [ -f /home/$u/.ssh/authorized_keys ] || [ -f /root/.ssh/authorized_keys ]; then
echo "=== $u ==="
cat /home/$u/.ssh/authorized_keys 2>/dev/null
[ "$u" = "root" ] && cat /root/.ssh/authorized_keys 2>/dev/null
fi
done
发现陌生 key / 离职同事 key → 立刻删。
8. 服务端 sshd 加固
/etc/ssh/sshd_config:
# 禁密码登录(强制只用密钥)
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
# 禁 root 直接登录
PermitRootLogin prohibit-password
# 或 no(彻底禁)
# 限制允许登录的用户
AllowUsers alice bob deploy
# 或:AllowGroups sshusers
# 减少协议噪音
ListenAddress 0.0.0.0
Port 22
# 客户端不活动时断开
ClientAliveInterval 300
ClientAliveCountMax 2
sudo sshd -t # 语法检查
sudo systemctl reload ssh
9. 硬件 key(YubiKey / 谷歌 Titan)
最强方案:私钥永远不在磁盘 / 内存上,存硬件芯片:
# 生成 FIDO2 / U2F 密钥(保存在 YubiKey)
ssh-keygen -t ed25519-sk -f ~/.ssh/id_ed25519_yubi
# resident key(可以从其它机器恢复)
ssh-keygen -t ed25519-sk -O resident -f ~/.ssh/id_ed25519_yubi
之后每次 ssh 都需要触摸 YubiKey 金属片。
恢复 resident key 到新机器:
ssh-keygen -K
# 把 YubiKey 上所有 resident key 提取到当前目录
私钥本质上是在 YubiKey 里,磁盘文件只是"指针"。丢硬件 = 数字身份废掉
(但攻击者拿不到密钥)。
10. SSH CA(企业规模时)
机器多了,每次给新人加 key 改几百台机器 authorized_keys 不现实。
SSH CA 系统:
- 一个 CA 私钥签发用户证书(包含 username + 有效期 + 权限)
- 每台机器
TrustedUserCAKeys = /etc/ssh/ca.pub - 用户拿到带签名的临时证书登录,到期失效
# 签发
ssh-keygen -s ca-key -I alice@2026-05 -n alice -V +1d \
alice_id_ed25519.pub
# 输出 alice_id_ed25519-cert.pub
# 登录时自动用 cert
ssh server # ssh 会读 cert + key
工具:Vault SSH backend / Smallstep / Teleport / sssd。
踩过的坑
- 公钥 vs 私钥:
*.pub是公钥可以随便发;没后缀那个是私钥 永远不发。
新手把id_ed25519而不是id_ed25519.pub贴 GitHub 是经典错误。 - 私钥权限:必须 600;775 / 644 sshd 会拒绝使用。
chmod 600 ~/.ssh/id_*。 .ssh/authorized_keys权限:必须 600;目录必须 700。否则 sshd 静默
忽略整个文件,登录失败查不到原因(auth log 才会有提示)。- ssh-agent forwarding(
ForwardAgent yes):跳板机 root 用户能拿你的
key 用,相当于把信任传递给跳板机。除非完全可信,否则用 ProxyJump
代替 agent forward。
登录后参与评论。