SSH 密钥
SSH 密钥可以用作使用公钥加密和挑战-应答身份验证向 SSH 服务器标识您身份的一种方式。基于密钥的身份验证的主要优点是,与密码身份验证相比,它不易受到暴力攻击,并且如果服务器已被入侵,您不会泄露有效的凭据(请参阅RFC 4251 9.4.4)。
此外,SSH 密钥身份验证可能比更传统的密码身份验证更方便。当与称为 SSH 代理的程序一起使用时,SSH 密钥允许您连接到一台或多台服务器,而无需记住或为每个系统输入密码。
基于密钥的身份验证并非没有缺点,可能不适用于所有环境,但在许多情况下,它可以提供一些强大的优势。对 SSH 密钥工作原理的一般理解将帮助您决定如何以及何时使用它们来满足您的需求。
本文假设您已经对安全外壳协议有基本的了解,并且已经安装了openssh软件包。
背景
SSH 密钥始终成对生成,一个称为私钥,另一个称为公钥。私钥仅您知道,应安全保管。相比之下,公钥可以与您希望连接的任何 SSH 服务器自由共享。
如果 SSH 服务器在文件中有您的公钥,并且看到您请求连接,它会使用您的公钥构建并向您发送一个质询。此质询是一条加密消息,必须使用适当的响应来满足此质询,服务器才会授予您访问权限。使此编码消息特别安全的原因在于,它只能被私钥持有者理解。虽然公钥可用于加密消息,但它不能用于解密同一消息。只有您,私钥的持有者,才能正确理解质询并生成正确的响应。
这个挑战-应答阶段发生在幕后,用户不可见。只要您持有私钥(通常存储在 ~/.ssh/
目录中),您的 SSH 客户端应该能够向服务器回复适当的响应。
私钥是受保护的秘密,因此建议以加密形式将其存储在磁盘上。当需要加密的私钥时,必须首先输入密码短语才能解密它。虽然这表面上看起来像是您正在向 SSH 服务器提供登录密码,但密码短语仅用于解密本地系统上的私钥。密码短语不会通过网络传输。
生成 SSH 密钥对
可以通过运行 ssh-keygen
命令来生成 SSH 密钥对,有关“通常被认为是足够的”以及应与几乎所有客户端和服务器兼容的内容,请参阅ssh-keygen(1) 手册页
$ ssh-keygen
Generating public/private ed25519 key pair. Enter file in which to save the key (/home/username/.ssh/id_ed25519): Created directory '/home/username/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/username/.ssh/id_ed25519 Your public key has been saved in /home/username/.ssh/id_ed25519.pub The key fingerprint is: SHA256:RLy4JBv7jMK5qYhRKwHB3af0rpMKYwE2PBhALCBV3G8 username@hostname The key's randomart image is: +--[ED25519 256]--+ |%oooo. .. | |== ..o.o. | |== . +o.. | |+ o o.ooE | |... *.oS | | o..o .. | |o=.. +o | |+o*..+o | |+.o+. . | +----[SHA256]-----+
randomart 图像在 OpenSSH 5.1 中引入,作为一种更简单地以可视方式识别密钥指纹的方法。
-a
开关来指定密码加密的 KDF 轮数。您还可以使用 -C
开关向公钥添加可选的注释字段,以便在 ~/.ssh/known_hosts
、~/.ssh/authorized_keys
和 ssh-add -L
输出等位置更轻松地识别它。例如
$ ssh-keygen -C "$(whoami)@$(uname -n)-$(date -I)"
将添加一条注释,说明哪个用户在哪个机器上以及何时创建了密钥。
选择身份验证密钥类型
OpenSSH 支持几种签名算法(用于身份验证密钥),可以根据它们利用的数学特性分为两组
椭圆曲线密码学 (ECC) 算法是公钥密码系统中较新的补充。它们的主要优势之一是它们能够以更小的密钥提供相同的安全级别,这使得计算密集型操作更少(即更快的密钥创建、加密和解密),并降低了存储和传输要求。
DSA 密钥由于其安全漏洞而已被弃用,并且 SSH 实现正在删除对它们的支持。OpenSSH 9.8 在构建时默认不包含对 DSA 密钥的支持,Dropbear 2022.83 禁用了 DSA 密钥支持,libssh 0.11.0 完全删除了对 DSA 密钥的支持。因此,密码系统的选择在于 RSA 或两种 ECC 类型之一。
默认的 Ed25519 将为您提供最佳的安全性和良好的性能。ECDSA 比 Ed25519 慢,但比 RSA 快;对其安全性存在担忧(见下文)。RSA 密钥将为您提供与旧服务器的最佳兼容性,但它需要更大的密钥大小才能提供足够的安全性。
- Ed25519 和 ECDSA 密钥可以通过在使用特殊“安全密钥”密钥类型生成密钥时存储在 FIDO/U2F 硬件身份验证器中。请参阅#FIDO/U2F。
- 可信平台模块支持 ECDSA 和 RSA,这使得可以将 SSH 密钥密封在 TPM 内部。请参阅可信平台模块#SSH。
Ed25519
Ed25519 在 OpenSSH 6.5(2014 年 1 月)中引入:“Ed25519 是一种椭圆曲线签名方案,它提供比 ECDSA 和 DSA 更好的安全性和良好的性能”。它的主要优势在于其速度、其恒定时间运行时间(以及对侧信道攻击的抵抗力)以及其缺乏模糊的硬编码常量。[1] 另请参阅 Mozilla 开发人员关于其工作原理的这篇博客文章。
它在许多应用程序和库中实现,并且是 OpenSSH 中的默认密钥交换算法(这与密钥签名不同)。
ssh-keygen(1) 默认为 Ed25519,因此无需使用 -t ed25519
选项指定它。密钥对可以简单地使用以下命令生成
$ ssh-keygen
无需设置密钥大小,因为所有 Ed25519 密钥均为 256 位。
请记住,旧的 SSH 客户端和服务器可能不支持这些密钥。
ECDSA
椭圆曲线数字签名算法 (ECDSA) 是从 OpenSSH 5.7 (2011-01-24) 到 OpenSSH 6.5 (2014-01-30) 的首选身份验证算法(密钥交换算法)。
关于它有两种担忧
- 政治担忧,在揭露 NSA 愿意将后门插入软件、硬件组件和已发布标准之后,NIST 生产的曲线的可靠性受到质疑;著名的密码学家 已经 表达了 对 NIST 曲线的设计方式的疑虑,并且自愿污染已经在过去被证明。
- 技术担忧,关于正确实现标准的难度以及速度慢和设计缺陷,这会降低预防措施不足的实现中的安全性。
这两个担忧最好在 libssh curve25519 介绍中总结。尽管政治担忧仍然存在争议,但#Ed25519 在技术上更优越,因此应该首选,这已成为明确的共识。
ECDSA 密钥对可以使用以下命令生成
$ ssh-keygen -t ecdsa
ECDSA 密钥支持三种椭圆曲线大小:256、384 和 521 位。默认值为 256 位。如果您希望生成更强的 ECDSA 密钥对,只需指定 -b
选项
$ ssh-keygen -t ecdsa -b 384
RSA
RSA 提供所有算法中最佳的兼容性,但需要更大的密钥大小才能提供足够的安全性。最小密钥大小为 1024 位,默认值为 3072(请参阅ssh-keygen(1)),最大值为 16384。
RSA 密钥对可以使用以下命令生成
$ ssh-keygen -t rsa
如果您希望生成更强的 RSA 密钥对(例如,为了防范尖端或未知攻击以及更复杂的攻击者),只需使用比默认值更高的位值指定 -b
选项
$ ssh-keygen -t rsa -b 4096
但请注意,使用更长的密钥会产生收益递减。[2][3] GnuPG FAQ 中写道:“如果您需要的安全性高于 RSA-2048 提供的安全性,那么应该转向椭圆曲线密码学,而不是继续使用 RSA。”[4]
另一方面,NSA Fact Sheet Suite B Cryptography 的最新迭代建议 RSA 的最小 3072 位模数,同时“[准备]迎接即将到来的抗量子算法过渡”。[5]
FIDO/U2F
FIDO/U2F 硬件身份验证器 支持在 OpenSSH 版本 8.2 中为上述两种椭圆曲线签名方案添加。它允许通过 USB 或其他方式连接的硬件令牌充当私钥的第二因素。
硬件令牌支持需要 libfido2。
- 客户端和服务器都必须支持
ed25519-sk
和ecdsa-sk
密钥类型。 - OpenSSH 使用中间件库与硬件令牌通信,并附带一个支持 USB 令牌的内部中间件。其他中间件可以通过 sshd_config(5) § SecurityKeyProvider 指令或
SSH_SK_PROVIDER
环境变量为ssh-keygen
和ssh-add
指定。
连接兼容的 FIDO 密钥后,可以使用以下命令生成密钥对
$ ssh-keygen -t ed25519-sk
通常,您需要输入 PIN 和/或轻触令牌以确认生成。连接到服务器通常需要轻触令牌,除非在生成期间使用了 -O no-touch-required
命令行选项,并且在服务器上设置了 sshd(8) § no-touch-required authorized_keys
选项。
要创建不需要触摸事件的密钥,请使用 no-touch-required
选项生成密钥对。例如
$ ssh-keygen -O no-touch-required -t ed25519-sk
此外,sshd
默认拒绝 no-touch-required
密钥。要允许使用此选项生成的密钥,请在 authorized_keys
文件中为单个密钥启用它,
no-touch-required sk-ssh-ed25519@openssh.com AAAAInN... user@example.com
或通过编辑 /etc/ssh/sshd_config
为整个系统启用它
PubkeyAuthOptions none
也可以使用 ecdsa-sk
密钥类型生成基于 ECDSA 的密钥对,但 #ECDSA 章节中的相关担忧仍然适用。
$ ssh-keygen -t ecdsa-sk
选择密钥位置和密码
发出 ssh-keygen
命令后,系统将提示您输入期望的私钥名称和位置。默认情况下,密钥存储在 ~/.ssh/
目录中,并根据使用的加密类型命名。建议您接受默认名称和位置,以便本文后面的代码示例能够正常工作。
当提示输入密码短语时,如果您考虑到私钥的安全性,请选择一些难以猜测的内容。更长、更随机的密码通常会更强大,并且如果落入坏人之手,则更难破解。
也可以创建没有密码短语的私钥。虽然这可能很方便,但您需要意识到相关的风险。如果没有密码短语,您的私钥将以未加密的形式存储在磁盘上。任何获得对您的私钥文件访问权限的人都将能够在使用基于密钥的身份验证连接的任何 SSH 服务器上冒充您的身份。此外,如果没有密码短语,您还必须信任 root 用户,因为他们可以绕过文件权限,并且可以随时访问您的未加密私钥文件。
在不更改密钥的情况下更改私钥的密码
如果最初选择的 SSH 密钥密码短语不理想或必须更改,则可以使用 ssh-keygen
命令来更改密码短语,而无需更改实际密钥。这也可以用于将密码编码格式更改为新标准。
$ ssh-keygen -f ~/.ssh/id_rsa -p
管理多个密钥
如果您有多个 SSH 身份,则可以使用配置中的 Host
和 IdentityFile
指令为不同的主机或远程用户设置不同的密钥
~/.ssh/config
Host SERVER1 IdentitiesOnly yes IdentityFile ~/.ssh/id_rsa_IDENTITY1 Host SERVER2 SERVER3 IdentitiesOnly yes IdentityFile ~/.ssh/id_ed25519_IDENTITY2
有关这些选项的完整描述,请参阅 ssh_config(5)。
在硬件令牌上存储 SSH 密钥
SSH 密钥也可以存储在安全令牌上,例如智能卡或 USB 令牌。这样做的好处是,私钥安全地存储在令牌上,而不是存储在磁盘上。当使用安全令牌时,敏感的私钥也永远不会出现在 PC 的 RAM 中;加密操作在令牌本身上执行。加密令牌的另一个优点是它不绑定到单台计算机;它可以轻松地从计算机上移除并随身携带,以便在其他计算机上使用。
硬件令牌的示例在以下位置描述
- #FIDO/U2F
- YubiKey#SSH 备注 本地 OpenSSH 支持 FIDO/U2F 密钥
- YubiKey#SSH 密钥
- 可信平台模块#SSH
将公钥复制到远程服务器
生成密钥对后,您需要将公钥复制到远程服务器,以便它将使用 SSH 密钥身份验证。公钥文件与私钥文件共享相同的名称,只是附加了 .pub
扩展名。请注意,私钥不共享,并保留在本地计算机上。
简单方法
如果您的密钥文件是 ~/.ssh/id_rsa.pub
,您可以简单地输入以下命令。
$ ssh-copy-id remote-server.org
如果您的用户名在远程计算机上有所不同,请务必在服务器名称前加上用户名和 @
。
$ ssh-copy-id username@remote-server.org
如果您的公钥文件名不是默认的 ~/.ssh/id_rsa.pub
,您将收到错误消息 /usr/bin/ssh-copy-id: ERROR: No identities found
。在这种情况下,您必须显式提供公钥的位置。
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub username@remote-server.org
如果 ssh 服务器正在侦听端口 22 以外的端口,请务必将其包含在主机参数中。
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 221 username@remote-server.org
手动方法
默认情况下,对于 OpenSSH,公钥需要与 ~/.ssh/authorized_keys
连接。首先将公钥复制到远程服务器。
$ scp ~/.ssh/id_ecdsa.pub username@remote-server.org:
上面的示例通过 scp
将公钥 (id_ecdsa.pub
) 复制到远程服务器上的主目录。不要忘记在服务器地址的末尾包含 :
。另请注意,您的公钥名称可能与给出的示例不同。
在远程服务器上,如果 ~/.ssh
目录尚不存在,您需要创建它,并将您的公钥追加到 authorized_keys
文件。
$ ssh username@remote-server.org username@remote-server.org's password: $ install -dm700 ~/.ssh $ cat ~/id_ecdsa.pub >> ~/.ssh/authorized_keys $ rm ~/id_ecdsa.pub $ chmod 600 ~/.ssh/authorized_keys
最后两个命令从服务器中删除公钥文件,并设置 authorized_keys
文件的权限,使其仅对您(所有者)可读写。
SSH 代理
如果您的私钥使用密码短语加密,则每次您尝试使用公钥身份验证连接到 SSH 服务器时,都必须输入此密码短语。每次调用 ssh
或 scp
都需要密码短语才能解密您的私钥,然后才能进行身份验证。
SSH 代理是一个程序,它缓存您解密的私钥,并代表您将其提供给 SSH 客户端程序。在这种安排中,您只需在将私钥添加到代理的缓存时提供一次密码短语。在进行频繁的 SSH 连接时,此功能非常方便。
代理通常配置为在登录时自动运行,并在您的登录会话期间持续存在。存在各种代理、前端和配置来实现此效果。本节概述了许多不同的解决方案,这些解决方案可以进行调整以满足您的特定需求。
ssh-agent
ssh-agent
是 OpenSSH 中包含的默认代理。它可以直接使用,也可以用作本节稍后提到的几个前端解决方案的后端。当 ssh-agent
运行时,它会 fork 到后台并打印必要的环境变量。例如
$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-vEGjCM2147/agent.2147; export SSH_AUTH_SOCK; SSH_AGENT_PID=2148; export SSH_AGENT_PID; echo Agent pid 2148;
要使用这些变量,请通过 eval
命令运行该命令。如果使用 fish
shell,请改用 ssh-agent -c
。
$ eval $(ssh-agent)
Agent pid 2157
一旦 ssh-agent
运行,您需要将您的私钥添加到其缓存中
$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/user/.ssh/id_ed25519: Identity added: /home/user/.ssh/id_ed25519 (/home/user/.ssh/id_ed25519)
如果您的私钥已加密,ssh-add
将提示您输入密码短语。一旦您的私钥已成功添加到代理,您将能够在无需输入密码短语的情况下进行 SSH 连接。
ssh
客户端(包括 git
)在首次使用时将密钥存储在代理中,请将配置设置 AddKeysToAgent yes
添加到 ~/.ssh/config
。其他可能的值是 confirm
、ask
和 no
(默认)。为了自动启动代理并确保一次只运行一个 ssh-agent
进程,请将以下内容添加到您的 ~/.bashrc
中
if ! pgrep -u "$USER" ssh-agent > /dev/null; then ssh-agent -t 1h > "$XDG_RUNTIME_DIR/ssh-agent.env" fi if [ ! -f "$SSH_AUTH_SOCK" ]; then source "$XDG_RUNTIME_DIR/ssh-agent.env" >/dev/null fi
如果尚不存在 ssh-agent
进程,这将运行一个 ssh-agent
进程,并保存其输出。如果已有一个正在运行,我们将检索缓存的 ssh-agent
输出并对其求值,这将设置必要的环境变量。解锁密钥的生命周期设置为 1 小时。
还存在许多 ssh-agent
的前端和本节稍后描述的替代代理,它们避免了这个问题。
使用 systemd 用户启动 ssh-agent
如果您希望您的 ssh 代理在您登录时运行,无论 X 是否正在运行,ssh-agent.service
实用程序已包含在 openssh 软件包中,版本为 9.4p1-3 或更高版本,可以启用为 用户单元。
然后设置环境变量 SSH_AUTH_SOCK
为 $XDG_RUNTIME_DIR/ssh-agent.socket
。
转发 ssh-agent
当转发本地 ssh-agent
到远程机器时(例如,通过命令行参数 ssh -A remote
或通过配置文件中的 ForwardAgent yes
),重要的是远程机器不要覆盖环境变量 SSH_AUTH_SOCK
。因此,如果远程机器使用先前显示的 systemd 单元来启动代理,则当用户通过 SSH 登录时,SSH_AUTH_SOCK
必须不在环境中设置。否则,转发可能会失败,并且当在远程机器上使用 ssh-add -l
检查现有密钥时,您可能会看到错误(例如:The agent has no identities
)。
例如,如果使用 bash,.bashrc
可能如下所示
~/.bashrc
... if [[ -z "${SSH_CONNECTION}" ]]; then export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket" fi ...
通过这种方式,SSH_AUTH_SOCK
仅在当前会话不是 SSH 登录时设置。当这是一个 SSH 会话时,远程机器上的 SSH_AUTH_SOCK
然后由本地机器设置,以使转发工作。
ssh-agent 作为包装程序
启动 ssh-agent 的另一种方法(例如,在每个 X 会话中)在 UC Berkeley Labs 的 ssh-agent 教程中描述。一个基本用例是,如果您通常使用 startx
命令启动 X,您可以改为在其前面加上 ssh-agent
,如下所示
$ ssh-agent startx
因此,您甚至无需考虑它,您可以将别名放在您的 .bash_aliases
文件或等效文件中
alias startx='ssh-agent startx'
这样做避免了在登录会话之间存在多余的 ssh-agent
实例的问题。只有一个实例会与整个 X 会话一起存在和消亡。
ssh-askpass
需要 DISPLAY
或 WAYLAND_DISPLAY
环境变量才能工作,因此您可能希望在 ~/.xinitrc
中运行 ssh-agent
而不是 ssh-agent startx
,在 ~/.xinitrc
中 DISPLAY
已设置。例如,您可以将 exec ssh-agent dbus-launch i3
添加到 ~/.xinitrc
。或者,作为使用 ssh-agent
作为包装程序的替代方法,您可以将 eval $(ssh-agent)
添加到 ~/.xinitrc
。有关如何立即将您的密钥添加到代理的想法,请参阅 关于将 x11-ssh-askpass 与 ssh-add 一起使用的注释。
OpenPGP 卡 ssh-agent
此 ssh-agent 专注于 OpenPGP 卡集成。它使用存储在 OpenPGP 卡身份验证槽中的私钥。
安装 openpgp-card-ssh-agent 并启用和 启动 openpgp-card-ssh-agent.socket
用户单元。
之后,为此代理添加相关的环境变量
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/openpgp-card/ssh-agent.sock"
用户 PIN 码处理
OpenPGP 卡的用户 PIN 码默认通过 org.freedesktop.secrets 提供程序(例如 GNOME Keyring、 KeePassXC 或 KDE Wallet)持久化。 PIN 码存储后端是可配置和可扩展的。
用户 PIN 码只需为每张 OpenPGP 卡持久化一次。在第一次使用此代理的 SSH 连接之前,列出可用的 SSH 公钥并添加它们各自的卡标识符
$ ssh-add -L ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJUz6VnFprMe33G88Pq8NLw3wnIKOsBg0CDrwFeUVrU6 FFFE:01234567 $ ssh-add -s FFFE:01234567 Enter passphrase for PKCS#11:
GnuPG 代理
gpg-agent 具有 OpenSSH 代理协议模拟功能。有关必要的配置,请参阅 GnuPG#SSH 代理。
Keychain
Keychain 是一个旨在帮助您轻松管理 SSH 密钥的程序,用户交互最少。它作为一个 shell 脚本实现,驱动 ssh-agent 和 ssh-add。Keychain 的一个显著特点是它可以跨多个登录会话维护单个 ssh-agent 进程。这意味着您只需在每次本地机器启动时输入一次密码。
安装
配置
-Q, --quick
选项具有意外的副作用,即在重新登录时(至少在使用 GNOME 的系统上),keychain 会切换到新生成的 ssh-agent,迫使您重新添加所有先前注册的密钥。在您的 shell 配置文件中添加类似于以下行的行,例如 如果使用 Bash
~/.bashrc
eval $(keychain --eval --quiet id_ed25519 id_rsa ~/.keys/my_custom_key)
~/.bashrc
而不是上游建议的 ~/.bash_profile
,因为在 Arch 上,它由登录和非登录 shell 均会读取,使其适用于文本和图形环境。有关两者之间差异的更多信息,请参阅 Bash#调用。在上面的例子中,
--eval
开关输出要由打开的eval
命令评估的行;这为 SSH 客户端设置必要的环境变量,使其能够找到您的代理。--quiet
将输出限制为警告、错误和用户提示。
可以在命令行中指定多个密钥,如示例所示。默认情况下,keychain 将在 ~/.ssh/
目录中查找密钥对,但绝对路径可用于非标准位置的密钥。您也可以使用 --confhost
选项来告知 keychain 在 ~/.ssh/config
中查找为特定主机定义的 IdentityFile
设置,并使用这些路径来定位密钥。
有关为其他 shell 设置 keychain 的详细信息,请参阅 keychain --help
或 keychain(1)。
要测试 Keychain,只需打开一个新的终端模拟器或注销并重新登录您的会话。它应该提示您输入指定的私钥的密码(如果适用),可以使用 $SSH_ASKPASS
中设置的程序或在终端上。
由于 Keychain 在连续登录时重用相同的 ssh-agent 进程,因此您下次登录或打开新终端时应该不必输入密码。您只需在每次机器重启时输入一次密码。
提示
- keychain 期望公钥文件与其私钥副本位于同一目录中,并带有
.pub
扩展名。如果私钥是符号链接,则可以在符号链接旁边或与符号链接目标相同的目录中找到公钥(此功能需要系统上提供readlink
命令)。 - 要禁用图形提示并始终在终端上输入密码,请使用
--nogui
选项。这允许例如从密码管理器复制粘贴长密码。 - 如果您不想立即提示解锁密钥,而是等到需要它们时,请使用
--noask
选项。
- Keychain 能够以相同的方式管理 GPG 密钥。默认情况下,它仅尝试启动 ssh-agent,但您可以使用
--agents
选项修改此行为,例如--agents ssh,gpg
。请参阅 keychain(1)。 - 如果您在 Wayland 上,您可能需要按照 keychain issue 148 中的说明添加
--inherit any-once
。
x11-ssh-askpass
x11-ssh-askpass 软件包提供了一个图形对话框,用于在运行 X 会话时输入您的密码。x11-ssh-askpass 仅依赖于 libx11 和 libxt 库,并且 x11-ssh-askpass 的外观是可定制的。虽然它可以由 ssh-add 程序调用,然后将您的解密密钥加载到 ssh-agent 中,但以下说明将配置 x11-ssh-askpass 以由上述 Keychain 脚本调用。
安装 keychain 和 x11-ssh-askpass 软件包。
编辑您的 ~/.xinitrc
文件以包含以下行,如果需要,替换您的私钥的名称和位置。请务必将这些命令放在调用您的窗口管理器的行之前。
~/.xinitrc
keychain ~/.ssh/id_ecdsa [ -f ~/.keychain/$HOSTNAME-sh ] && . ~/.keychain/$HOSTNAME-sh 2>/dev/null [ -f ~/.keychain/$HOSTNAME-sh-gpg ] && . ~/.keychain/$HOSTNAME-sh-gpg 2>/dev/null ... exec openbox-session
在上面的示例中,第一行调用 keychain 并传递您的私钥的名称和位置。如果这不是第一次调用 keychain,则以下两行加载 $HOSTNAME-sh
和 $HOSTNAME-sh-gpg
的内容(如果它们存在)。这些文件存储了先前 keychain 实例的环境变量。
使用 x11-ssh-askpass 调用 ssh-add
ssh-add 手册页指定,除了需要定义 DISPLAY
或 WAYLAND_DISPLAY
变量外,您还需要将 SSH_ASKPASS
设置为您的 askpass 程序的名称(在本例中为 x11-ssh-askpass)。请记住,默认的 Arch Linux 安装将 x11-ssh-askpass 二进制文件放在 /usr/lib/ssh/
中,这不会在大多数人的 PATH
中。这有点烦人,不仅在声明 SSH_ASKPASS
变量时,而且在主题化时也是如此。您必须在任何地方指定完整路径。通过符号链接可以同时解决这两个不便之处
$ ln -sv /usr/lib/ssh/x11-ssh-askpass ~/bin/ssh-askpass
这是假设 ~/bin
在您的 PATH
中。因此,现在在您的 .xinitrc
中,在调用您的窗口管理器之前,只需导出 SSH_ASKPASS
环境变量
$ export SSH_ASKPASS=ssh-askpass
并且您的 X 资源 将包含类似以下内容
ssh-askpass*background: #000000
这样做与 上述使用 ssh-agent 作为包装程序的方法 配合良好。您使用 ssh-agent startx
启动 X,然后将 ssh-add 添加到窗口管理器的启动程序列表中。
主题化
可以通过设置其关联的 X 资源来自定义 x11-ssh-askpass 对话框的外观。一些示例是在 https://github.com/sigmavirus24/x11-ssh-askpass 上的 .ad 文件。有关完整详细信息,请参阅 x11-ssh-askpass(1)。
替代密码对话框
还有其他密码对话框程序可以代替 x11-ssh-askpass 使用。以下列表提供了一些替代解决方案。
- seahorse(提供
/usr/lib/seahorse/ssh-askpass
)使用 GTK 库。 - gnome-ssh-askpass3AUR 使用 GTK 库。
- ksshaskpass 使用 KDE Wallet。
- openssh-askpassAUR 使用 Qt5 库。
- lxqt-openssh-askpass
pam_ssh
pam_ssh 项目旨在为 SSH 私钥提供 可插拔身份验证模块 (PAM)。此模块可以为您的 SSH 连接提供单点登录行为。登录时,可以输入您的 SSH 私钥密码短语来代替或补充您的传统系统密码。一旦您通过身份验证,pam_ssh 模块将生成 ssh-agent 以在会话期间存储您的解密私钥。
要在 tty 登录提示符下启用单点登录行为,请安装非官方的 pam_sshAUR 软件包。
~/.ssh/login-keys.d/
下。创建一个指向您的私钥文件的符号链接,并将其放在 ~/.ssh/login-keys.d/
中。将以下示例中的 id_rsa
替换为您自己的私钥文件的名称。
$ mkdir ~/.ssh/login-keys.d/ $ cd ~/.ssh/login-keys.d/ $ ln -s ../id_rsa
编辑 /etc/pam.d/login
配置文件以包含以下示例中以粗体突出显示文本。这些行出现的顺序很重要,可能会影响登录行为。
/etc/pam.d/login
#%PAM-1.0 auth required pam_securetty.so auth requisite pam_nologin.so auth include system-local-login auth optional pam_ssh.so try_first_pass account include system-local-login session include system-local-login session optional pam_ssh.so
在上面的示例中,登录身份验证最初像往常一样进行,用户被提示输入其用户密码。添加到身份验证堆栈末尾的附加 auth
身份验证规则然后指示 pam_ssh 模块尝试解密在 ~/.ssh/login-keys.d
目录中找到的任何私钥。try_first_pass
选项传递给 pam_ssh 模块,指示它首先尝试使用先前输入的用户密码解密任何 SSH 私钥。如果用户的私钥密码短语和用户密码相同,则这应该会成功,并且用户不会被提示输入相同的密码两次。如果用户的私钥密码短语和用户密码不同,则 pam_ssh 模块将在用户密码输入后提示用户输入 SSH 密码短语。optional
控制值确保没有 SSH 私钥的用户仍然能够登录。通过这种方式,pam_ssh 的使用对于没有 SSH 私钥的用户将是透明的。
如果您使用另一种登录方式,例如 X11 显示管理器(如 SLiM 或 XDM),并且您希望它提供类似的功能,则必须以类似的方式编辑其关联的 PAM 配置文件。提供 PAM 支持的软件包通常在 /etc/pam.d/
目录中放置默认配置文件。
有关如何使用 pam_ssh 及其选项列表的更多详细信息,请参见 pam_ssh(8) 手册页。
使用不同的密码来解锁 SSH 密钥
如果您想根据您是使用密钥的密码短语还是(不同的!)登录密码来解锁 SSH 密钥,您可以修改 /etc/pam.d/system-auth
为
/etc/pam.d/system-auth
#%PAM-1.0 auth [success=1 new_authtok_reqd=1 ignore=ignore default=ignore] pam_unix.so try_first_pass nullok auth required pam_ssh.so use_first_pass auth optional pam_permit.so auth required pam_env.so account required pam_unix.so account optional pam_permit.so account required pam_time.so password required pam_unix.so try_first_pass nullok sha512 shadow password optional pam_permit.so session required pam_limits.so session required pam_unix.so session optional pam_permit.so session optional pam_ssh.so
有关解释,请参阅 [7]。
pam_ssh 的已知问题
pam_ssh 项目的工作不频繁,提供的文档也很稀疏。您应该意识到其一些在软件包本身中未提及的限制。
- 版本 2.0 之前的 pam_ssh 版本不支持采用较新的 ECDSA(椭圆曲线)加密选项的 SSH 密钥。如果您使用的是早期版本的 pam_ssh,则必须使用 RSA 密钥。
- pam_ssh 生成的
ssh-agent
进程不会在用户登录之间持久存在。如果您想在登录之间保持 GNU Screen 会话处于活动状态,您可能会注意到,当重新连接到您的 screen 会话时,它无法再与 ssh-agent 通信。这是因为 GNU Screen 环境及其子环境仍将引用在调用 GNU Screen 时存在的 ssh-agent 实例,但该实例随后在先前的注销中被杀死。Keychain 前端通过在登录之间保持 ssh-agent 进程处于活动状态来避免此问题。
pam_exec-ssh
作为 pam_ssh 的替代方案,您可以使用 pam_exec-ssh-gitAUR。它是一个使用 pam_exec 的 shell 脚本。有关配置的帮助,请参见 上游。
GNOME Keyring
GNOME Keyring 工具可以充当 ssh-agent 的包装器,提供 GUI 和/或自动密钥解锁。有关更多详细信息,请参阅 GNOME Keyring#SSH 密钥。
使用 Kwallet 存储 SSH 密钥
有关如何使用 kwallet 存储您的 SSH 密钥的说明,请参阅 KDE Wallet#使用 KDE Wallet 存储 ssh 密钥密码短语。
KeePass2 与 KeeAgent 插件
KeeAgent 是 KeePass 的插件,允许存储在 KeePass 数据库中的 SSH 密钥被其他程序用于 SSH 身份验证。
- 支持 PuTTY 和 OpenSSH 私钥格式。
- 在 Linux/Mac 上与原生 SSH 代理一起工作,在 Windows 上与 PuTTY 一起工作。
请参阅 KeePass#在 KeePass 中安装插件 或 安装 keepass-plugin-keeagent 软件包。
此代理可以直接使用,通过匹配 KeeAgent 套接字:KeePass -> 工具 -> 选项 -> KeeAgent -> 代理模式套接字文件 -> %XDG_RUNTIME_DIR%/keeagent.socket
- 和环境变量: export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR"'/keeagent.socket'
。
KeePassXC
KeePass 的分支 KeePassXC 可以充当现有 SSH 代理的客户端。存储在其数据库中的 SSH 密钥可以自动(或手动)添加到代理。它也与 KeeAgent 的数据库格式兼容。
故障排除
密钥被服务器忽略
- 如果 SSH 服务器似乎忽略了您的密钥,请确保您已在所有相关文件上设置了正确的权限。
- 对于本地机器
$ chmod 700 ~/.ssh $ chmod 600 ~/.ssh/key
- 对于远程机器
$ chmod 700 ~/.ssh $ chmod 600 ~/.ssh/authorized_keys
- 对于远程机器,还要检查目标用户的 home 目录是否具有正确的权限(它必须不可被组和其他用户写入)
$ chmod go-w ~target_user
- 如果这不能解决问题,您可以尝试临时将
StrictModes
设置为no
在/etc/ssh/sshd_config
中。如果使用StrictModes off
身份验证成功,则可能是文件权限问题仍然存在。
- 确保
~/.ssh/authorized_keys
中的密钥输入正确,并且仅使用单行。 - 确保远程机器支持您正在使用的密钥类型:某些服务器不支持 ECDSA 密钥,请尝试改用 RSA 密钥,请参阅 #生成 SSH 密钥对。
- 您可能想要使用调试模式并在连接时监视输出
# /usr/bin/sshd -d
- 如果您为密钥起了另一个名称,例如
id_rsa_server
,则需要使用-i
选项连接
$ ssh -i id_rsa_server user@server
agent refused operation (代理拒绝操作)
如果您的私钥需要密码(或者,例如,您有一个带有 PIN 码的硬件密钥),但 ssh-agent 没有提供密码,则 ssh
将失败
sign_and_send_pubkey: signing failed for ECDSA-SK user@host from agent: agent refused operation
一个可能的原因是 ssh-agent 无法提示输入密码。确保 ssh-agent 可以访问显示服务器(通过 DISPLAY
环境变量)或 TTY。对于某些图形环境,您可能只需要安装 x11-ssh-askpass,对于其他设置,也请遵循 #x11-ssh-askpass 说明。
如果使用硬件身份验证器,则另一个原因可能是密钥故障或未插拔。
当前存在一个公开的 bug,当使用使用 -O verify-required
选项创建的身份验证器密钥(如 ED25519-sk 和 ECDSA-SK)时,会触发“agent refused operation”错误。为避免此问题,请为 ssh
命令使用 -o IdentityAgent=none -o IdentitiesOnly=yes
选项,或将其添加到您的 ssh_config
文件中以用于相关主机
Host myserver.tld IdentityAgent none IdentitiesOnly yes
另请参阅
- OpenSSH 密钥管理: 第 1 部分, 第 2 部分, 第 3 部分
- 安全 Secure Shell