SSH 密钥
SSH 密钥可以用作使用公钥密码学和挑战-应答身份验证向 SSH 服务器标识您身份的一种方式。基于密钥的身份验证的主要优点是,与密码身份验证相比,它不易受到暴力破解攻击的影响,并且如果服务器已被入侵,您不会泄露有效的凭据(请参阅RFC 4251 9.4.4)。
此外,SSH 密钥身份验证可能比更传统的密码身份验证更方便。当与称为 SSH 代理的程序一起使用时,SSH 密钥可以允许您连接到一台或多台服务器,而无需记住或为每个系统输入密码。
基于密钥的身份验证并非没有缺点,并且可能不适用于所有环境,但在许多情况下,它可以提供一些强大的优势。大致了解 SSH 密钥的工作原理将帮助您决定如何以及何时使用它们来满足您的需求。
本文假设您已经对 Secure Shell 协议有了基本的了解,并且已经安装了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 内部。请参阅 Trusted Platform Module#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 密钥
- Trusted Platform Module#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
输出并对其求值,这将设置必要的环境变量。解锁密钥的生命周期设置为 1 小时。
在本节稍后描述的 ssh-agent
和替代代理也存在许多前端,它们避免了这个问题。
使用 systemd 用户启动 ssh-agent
如果您希望您的 ssh 代理在您登录时运行,而不管 X 是否正在运行,则 openssh 的 9.4p1-3 版本以来包含了一个方便的 ssh-agent.service
,可以将其 启用 为 用户单元。
然后 将环境变量 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
中运行,因为 DISPLAY
在 ~/.xinitrc
中设置。例如,您可以将 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 存储后端是可配置和可扩展的。
每个 OpenPGP 卡的用户 PIN 只需要持久化一次。在此代理的第一次 SSH 连接之前,列出可用的 SSH 公钥并添加它们各自的卡标识符
$ ssh-add -L ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJUz6VnFprMe33G88Pq8NLw3wnIKOsBg0CDrwFeUVrU6 FFFE:01234567 $ ssh-add -s FFFE:01234567 Enter passphrase for PKCS#11:
GnuPG Agent
gpg-agent 具有 OpenSSH Agent 协议模拟功能。请参阅 GnuPG#SSH agent 以获取必要的配置。
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 Linux 上,~/.bashrc
会被登录 shell 和非登录 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 前一个实例的环境变量。
使用 ssh-add 调用 x11-ssh-askpass
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 添加到窗口管理器的启动程序列表中。
主题化
可以通过设置 x11-ssh-askpass 对话框关联的 X 资源来自定义其外观。一些示例是 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 私钥的用户是透明的。
如果您使用其他登录方式,例如像 SLiM 或 XDM 这样的 X11 显示管理器,并且您希望它提供类似的功能,则您必须以类似的方式编辑其相关的 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 agent 和 Windows 上的 PuTTY。
请参阅 KeePass#在 KeePass 中安装插件 或 安装 keepass-plugin-keeagent 软件包。
此 agent 可以直接使用,通过匹配 KeeAgent 套接字:KeePass -> 工具 -> 选项 -> KeeAgent -> Agent 模式套接字文件 -> %XDG_RUNTIME_DIR%/keeagent.socket
- 和环境变量:export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR"'/keeagent.socket'
。
KeePassXC
KeePass 的分支 KeePassXC 可以充当现有 SSH agent 的客户端。存储在其数据库中的 SSH 密钥可以自动(或手动)添加到 agent。它也与 KeeAgent 的数据库格式兼容。
故障排除
密钥被服务器忽略
- 如果 SSH 服务器似乎忽略了您的密钥,请确保您在所有相关文件上设置了正确的权限。
- 对于本地机器
$ chmod 700 ~/.ssh $ chmod 600 ~/.ssh/key
- 对于远程机器
$ chmod 700 ~/.ssh $ chmod 600 ~/.ssh/authorized_keys
- 对于远程机器,还要检查目标用户的主目录是否具有正确的权限(它不能被组和其他用户写入)
$ 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 拒绝操作
如果您的私钥需要密码(或者,例如,您有一个带有 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