SSH keys (简体中文)

From ArchWiki
(Redirected from 使用SSH密钥)
Jump to navigation Jump to search

SSH 密钥对可以让您方便的登录到 SSH 服务器,而无需输入密码。由于您无需发送您的密码到网络中,SSH 密钥对被认为是更加安全的方式。再加上使用密码短语 (passphrase) 的使用,安全性会更上一层楼。

同时,我们可以使用 SSH agent 来帮助我们记住密码短语,无需我们记住每一个密钥对的密码短语,减轻了我们的负担。

本文将为您介绍如何管理密钥对,以方便的连接到您的 SSH 服务器。本文默认您已经熟知 SSH,并安装好位于官方软件仓库openssh

背景

SSH 密钥对总是成双出现的,一把公钥,一把私钥。公钥可以自由的放在您所需要连接的 SSH 服务器上,而私钥必须稳妥的保管好。

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录 shell,不再要求密码。这样子,我们即可保证了整个登录过程的安全,也不会受到中间人攻击。

生成密钥对

我们可以使用 ssh-keygen 命令生成密钥对

$ ssh-keygen -t ecdsa -b 521 -C "$(whoami)@$(hostname)-$(date -I)"
Generating public/private ecdsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_ecdsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/username/.ssh/id_ecdsa.
Your public key has been saved in /home/username/.ssh/id_ecdsa.pub.
The key fingerprint is:
dd:15:ee:24:20:14:11:01:b8:72:a2:0f:99:4c:79:7f username@localhost-2011-12-22
The key's randomart image is:
+--[ECDSA  521]---+
|     ..oB=.   .  |
|    .    . . . . |
|  .  .      . +  |
| oo.o    . . =   |
|o+.+.   S . . .  |
|=.   . E         |
| o    .          |
|  .              |
|                 |
+-----------------+

在上面这个例子中,ssh-keygen 生成了一对长度为 521 bit (-b 521) 的 ECDSA (-t ecdsa) 加密的密钥对,comment 为 -C "$(whoami)@$(hostname)-$(date -I)"。而 randomart image 是 OpenSSH 5.1 引入的一种简单的识别指纹 (fingerprint) 的图像。

选择合适的加密方式

椭圆曲线数字签名算法 (ECDSA) 生成的密钥更小,安全性更高。OpenSSH 5.7 建议默认使用 ECDSA,详情参见 OpenSSH 5.7 Release Notes。较旧的 OpenSSH 版本可能不支持 ECDSA 密钥,需要注意。而一些厂商因专利问题,暂未提供 ECDSA 的实现。

注意: 截至 2014 年06 月 10 日,这个 GNOME bug 导致 Gnome Keyring 暂不支持 ECDSA。

如果您要生成 RSA (768-16384 bit) 或者 DSA (1024 bit) 密钥对,需要使用 -t rsa 或者 -t dsa,并修改 -b 选项。-b 可以省略,ssh-keygen 会生成一个默认大小的密钥对。

注意: 这些密钥对是用于认证的,选择更加复杂的密钥类型并不会在登录时加重您的 CPU 负担。

选择密钥存储位置以及密码短语

输入 ssh-keygen 时,它会询问您将密钥对保存到何处,文件名如何命令等。默认情况下,密钥对保存到 ~/.ssh 下,文件名则根据加密类型自动命名为 id_ecdsa (私钥),id_ecdsa.pub (公钥)。建议您采用默认的存储位置和文件名。

而在 ssh-keygen 请求您输入一个密码短语时,您应该输入一些难以猜到的短语。如果短语足够随机和复杂,则私钥落入贼人之手时就不会容易被破解掉。

当然,您也可以不输入任何密码短语,也能够生成所需的密钥对。虽然这用起来挺方便的,但是您应该知道这会很危险。在没有输入密码短语的情况下,您的私钥未经加密就存储在您的硬盘上,任何人拿到您的私钥都可以随意的访问对应的 SSH 服务器。还有一种情况,如果您不是 root 用户,则该机器上的 root 用户可以完全拥有您的密钥对,因为他的权限是最大的。

不修改密钥对的情况下修改密码短语

您可以使用 ssh-keygen 命令来修改密码短语,而无需改动密钥对。假设您要修改的密钥对使用 RSA 加密,输入以下命令即可:

$ ssh-keygen -f ~/.ssh/id_rsa -p

管理多组密钥对

您可以创建 ~/.ssh/config 来管理多组密钥对,每一个 SSH 服务器对应一组密钥对。或者,您甚至可以对所有的 SSH 服务器使用同一组密钥对。不过如果您觉得这样不合适,还是编辑配置文件:

~/.ssh/config
Host SERVERNAME1
  IdentitiesOnly yes
  IdentityFile ~/.ssh/id_rsa_SERVER1
  # CheckHostIP yes
  # Port 22
Host SERVERNAME2
  IdentitiesOnly yes
  IdentityFile ~/.ssh/id_rsa_SERVER2
  # CheckHostIP no
  # Port 2177
ControlMaster auto
ControlPath /tmp/%r@%h:%p

更多选项帮助请参考

$ man ssh_config 5

将公钥复制到远程服务器上

创建好密钥对之后,您需要将公钥上传到远程服务器上,以便用于 SSH 密钥认证登录。公钥文件名和私钥文件名相同,只不过公钥文件带有扩展名 .pub 而私钥文件名则没有。千万不要将私钥上传,私钥应该保存在本地。

简单的方法

注意: 如果您的远程服务器默认使用的是 non-sh 的 shell,比如 tcsh,则此方法可能不奏效。详情参见这个 bug
注意: 如果使用以下两种方法外的方法请不要忘记注册公钥文件,您只需要命令
$ cat ~/.ssh/id_ecdsa.pub >> ~/.ssh/authorized_keys

如果您的公钥文件为 ~/.ssh/id_rsa.pub,您只需要输入命令

$ ssh-copy-id remote-server.org

如果您的远程服务器用户名与本地的不同,您需要指明用户名

$ ssh-copy-id username@remote-server.org

如果您的公钥文件名不是默认的,您会得到错误 /usr/bin/ssh-copy-id: ERROR: No identities found。这种情况下,您需要修改命令为

$ ssh-copy-id -i ~/.ssh/id_ecdsa.pub username@remote-server.org

如果远程服务器监听端口不是 22,您也需要指明端口

$ ssh-copy-id -i ~/.ssh/id_ecdsa.pub -p 221 username@remote-server.org

传统的方法

使用命令

$ scp ~/.ssh/id_ecdsa.pub username@remote-server.org:

将位于~/.ssh/id_ecdsa.pub的公钥上传到服务器。注意,该命令最末的 : 不可省略。上传成功之后,先使用口令登录到服务器,将公钥文件重命名为 authorized_keys,并移动到 ~/.ssh 下,若 ~/.ssh 不存在则新建一个。

$ ssh username@remote-server.org
username@remote-server.org's password:
$ mkdir ~/.ssh
$ chmod 700 ~/.ssh
$ cat ~/id_ecdsa.pub >> ~/.ssh/authorized_keys
$ rm ~/id_ecdsa.pub
$ chmod 600 ~/.ssh/authorized_keys

上面最后两个命令移除服务器上的公钥,并设置 authorized_keys 的权限为只有您,也即文件拥有者,有读写权限。

安全性

禁用密码登录

将公钥上传到 SSH 服务器上之后,您就不再需要输入 SSH 账户密码来登录了。直接使用账户密码登录容易受到暴力破解的攻击。倘若您没有提供 SSH 私钥,默认情况下,SSH 服务器就会让您直接使用密码登录,这就有可能让不法之徒来猜测您的密码,有一定的安全隐患。要禁用这一行为,您需要编辑 SSH 服务器上的 /etc/ssh/sshd_config

/etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no

双因素认证与公钥

从 OpenSSH 6.2 开始,您可以使用 AuthenticationMethods 选项来自己添加工具链进行认证。这样就可以配合公钥使用双因素认证了。

谷歌身份验证器设置请参考 Google Authenticator

如果您使用 PAM (Pluggable Authentication Module,插入式验证模块),编辑下面这几行:

/etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive:pam
/etc/pam.d/sshd
#%PAM-1.0
auth required pam_google_authenticator.so

如果您设置 PAM 仅使用 pam_google_authenticator.so,则 sshd 则会采用双因素认证,而无需密码。如果双因素认证失败,即使有有效的公钥,sshd 也不允许登录。您可以加上 nullok 选项来允许用户使用公钥登录:

/etc/pam.d/sshd
#%PAM-1.0
auth required pam_google_authenticator.so nullok

SSH agents

如果您的私钥使用密码短语来加密了的话,每一次使用 SSH 密钥对进行登录的时候,您都必须输入正确的密码短语。

而 SSH agent 程序能够将您的已解密的私钥缓存起来,在需要的时候提供给您的 SSH 客户端。这样子,您就只需要将私钥加入 SSH agent 缓存的时候输入一次密码短语就可以了。这为您经常使用 SSH 连接提供了不少便利。

SSH agent 一般会设置成在登录会话的时候自动启动,并在整个会话中保持运行。有不少的 SSH agent 供您选择,我们将为您介绍几种常用的 SSH agent,您可以根据您的需要进行选择。

ssh-agent

ssh-agent 是 OpenSSH 自带的一个 SSH agent,它可以直接作为 SSH agent 来使用,或者作为其他 SSH agent 的后端。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 命令来运行它

$ eval $(ssh-agent)
Agent pid 2157

您可以将上述命令添加到 ~/.bash_profile,以便您启动 登录外壳 的时候它自动运行。

$ echo 'eval $(ssh-agent)' >> ~/.bash_profile

如果您想要所有的用户都可以使用 ssh-agent,可以直接把上述命令加入到 /etc/profile

# echo 'eval $(ssh-agent)' >> /etc/profile

ssh-agent 运行起来之后,您还需要将您的私钥加入它的缓存。

$ ssh-add ~/.ssh/id_ecdsa
Enter passphrase for /home/user/.ssh/id_ecdsa:
Identity added: /home/user/.ssh/id_ecdsa (/home/user/.ssh/id_ecdsa)

如果您想要在登录 shell 的时候自动添加您的私钥到 ssh-agent 的缓存,您需要在 ~/.bash_profile 加入以下命令:

ssh-add

如果您的私钥已用密码短语加密,ssh-add 会提示您输入密码短语。输入正确的密码短语之后,您在*当前会话*中就不需要再次输入密码短语就能够使用密钥对进行 SSH 登录了。

如果您想要在用到私钥的时候再输入密码短语,您可以添加以下命令到 ~/.bashrc

$ ssh-add -l >/dev/null || alias ssh='ssh-add -l >/dev/null || ssh-add && unalias ssh; ssh'

但是这样做有个缺点,每次启动 登录外壳 都会产生一个 ssh-agent 实例,并在会话期间一直运行。不久之后,您的系统中就会有多个根本不再需要的 ssh-agent 进程在运行。稍后,我们将为您介绍其他 ssh-agent 前端,它们能够避免这个问题。

ssh-agent 作为包装程序运行

根据加州大学伯克利分校实验室的这份 ssh-agent 教程,还有一个随 X 会话启动 ssh-agent 的方法,该法用于您在使用命令 startx 启动 X的时候,可以在命令前加上 ssh-agent

$ ssh-agent startx

您也可以在 .bash_aliases 中设置别名以方便使用:

alias startx='ssh-agent startx'

这样做可以避免多个会话中存在多余的 ssh-agent 进程,实际上,整个 X 会话中只有一个 ssh-agent 在运行了。

GnuPG

注意: GnuPG < 2.0.21 不支持 ECC。GnuPG >= 2.0.21 支持 ECDSA。

GnuPG 可以从 官方软件仓库 安装 gnupg。如果您已经在使用 GnuPG,您也许想要 GnuPG 来缓存您的私钥。当然咯,有些用户比较喜欢在 GnuPG 对话框来输入 PIN 码,这样子管理密码短语也是不错的选择。

{注意|如果您使用 KDE 作为桌面环境,且安装了 kde-agent[broken link: package not found] 的话,您只需在 ~/.gnupg/gpg-agent.conf 中设置 enable-ssh-support。否则,请继续阅读。}

要使用 GnuPG agent,您首先要启动 gpg-agent,并加上 --enable-ssh-support 选项。比如,记得给脚本加上可执行权限:

/etc/profile.d/gpg-agent.sh
#!/bin/sh

# Start the GnuPG agent and enable OpenSSH agent emulation
gnupginf="${HOME}/.gpg-agent-info"

if pgrep -x -u "${USER}" gpg-agent >/dev/null 2>&1; then
    eval `cat $gnupginf`
    eval `cut -d= -f1 $gnupginf | xargs echo export`
else
    eval `gpg-agent -s --enable-ssh-support --daemon --write-env-file "$gnupginf"`
fi

gpg-agent 运行起来之后,您就可以使用 ssh-add 命令来认证密钥对,正如上文中 ssh-agent 做的那样。已认证的密钥对列表保存在 ~/.gnupg/sshcontrol 文件中。此后,在需要使用您的私钥密码短语是,GnuPG 会显示一个对话框让您输入。您可以通过配置文件 ~/.gnupg/gpg-agent.conf 来管理您的密码短语缓存。比如,下面这个例子说明如何使用 gpg-agent 来缓存您的密钥 3 小时:

~/.gnupg/gpg-agent.conf
  # Cache settings
  default-cache-ttl 10800
  default-cache-ttl-ssh 10800

您还可以在此文件中设置 PIN 输入框 (GTK、QT 或者 ncurses 版本) 等内容。

注意: 您必须创建一个 gpg-agent.conf 文件,write-env-file 变量必须设置好,以便允许 gpg-agent 在 SSH 登录过程中的使用。
~/.gnupg/gpg-agent.conf
  # Environment file
  write-env-file /home/username/.gpg-agent-info
  
  # Keyboard control
  #no-grab
    
  # PIN entry program
  #pinentry-program /usr/bin/pinentry-curses
  #pinentry-program /usr/bin/pinentry-qt4
  #pinentry-program /usr/bin/pinentry-kwallet
  pinentry-program /usr/bin/pinentry-gtk-2

要使用 gpg-agent 作为 SSH agent,您需要 source & export gpg-agent 写入 ~/.gpg-agent-info 文件的环境变量,也就是 write-env-file 所指的文件。

~/.bashrc
...

if [ -f "${HOME}/.gpg-agent-info" ]; then
  . "${HOME}/.gpg-agent-info"
  export GPG_AGENT_INFO
  export SSH_AUTH_SOCK
fi

Keychain

Keychain 是一个用来方便管理 SSH 密钥对的程序,它能尽最大努力去减少对用户的打扰。实际上,它就是一个 shell 脚本,驱动 ssh-agent 或者 gpg-add 来工作。一个值得注意的特性是,keychain 在多个会话中重复使用同一个 ssh-agent 进程。这意味着您只需要在机器启动时输入一次密码短语即可。

您可以从 官方软件仓库 安装 keychain

将下列内容加入到 ~/.bash_profile

~/.bash_profile
eval $(keychain --eval --agents ssh -Q --quiet id_ecdsa)

如有需要,请把 id_ecdsa 换成您的私钥路径。如果您使用的 shell 不兼容 Bash (简体中文),请参考 keychain --help 或者 keychain(1)

要测试 keychain 是否配置成功,请注销当前会话,重新登录。如果这是您第一次运行 keychain,则您会被要求输入私钥的密码短语。因为 keychain 复用已成功登录的会话中的 ssh-agent 进程,所以您再次登录是就无需输入密码短语了。当您重启机器时,您才会被要求再次输入密码短语。

另一种启动 keychain 的方式

调用 keychain 的方法有很多种,您可以自行尝试,找到最适合您的那一种。keychain 命令提供了不少的选项来进行设置,您可以参考它的手册。

这里我们介绍其中一种不错的方法,以 root 身份创建 /etc/profile.d/keychain.sh,并添加下列内容:

/etc/profile.d/keychain.sh
/usr/bin/keychain -Q -q --nogui ~/.ssh/id_ecdsa
[[ -f $HOME/.keychain/$HOSTNAME-sh ]] && source $HOME/.keychain/$HOSTNAME-sh

记得给该脚本加上可执行权限:

# chmod +x /etc/profile.d/keychain.sh

如果您想要在第一次使用私钥时才输入密码短语,而非一开机登录就输入的话,您可以将下列内容加入 ~/.bashrc

alias ssh='eval $(/usr/bin/keychain --eval --agents ssh -Q --quiet ~/.ssh/id_ecdsa) && ssh'

这样子,当您开机后首次使用密钥对连接 SSH 服务器时,您才需要提供密码短语。但是,这只在 ~/.bashrc 可用的情况下才奏效,所以您应该保证第一次使用 SSH 连接时是在终端下进行的。

envoy

Envoy 算是 keychain 的一个替代品,您可以从 官方软件仓库 下载安装 envoyAUR,或者从 AUR 安装 envoy-gitAUR

安装完成之后,使用以下命令启用 envoy 套接字 (socket):

# systemctl enable envoy@ssh-agent.socket

加入您的外壳 rc 文件:

 envoy -t ssh-agent -a ssh_key
 source <(envoy -p)

如果您的私钥为 ~/.ssh/id_rsa, ~/.ssh/id_dsa, ~/.ssh/id_ecdsa,或者 ~/.ssh/identity,则不需要 -a ssh_key 参数。

利用 KDE 电子钱包存储密码短语并和 envoy 配合工作

如果您的密码短语很长很复杂,那记忆起来是有点痛苦。您可以让 KDE 电子钱包来为您记住密码短语哦!

安装位于 官方软件仓库ksshaskpasskdeutils-kwalletmanager[broken link: package not found],然后按照上文介绍的方法启用 envoy 套接字。

添加一个脚本到 ~/.kde4/Autostart/,输入以下内容:

~/.kde4/Autostart/ssh-agent.sh
#!/bin/sh
envoy -t ssh-agent -a ssh_key

记得加上可执行权限:

$ chmod +x ~/.kde4/Autostart/ssh-agent.sh

添加一个脚本到 ~/.kde4/env/,加入以下内容:

~/.kde4/env/ssh-agent.sh
#!/bin/sh
eval $(envoy -p)

同样不要忘记加上可执行权限:

$ chmod +x ~/.kde4/env/ssh-agent.sh

当您登录到 KDE 桌面环境时,它就会自动运行脚本 ssh-agent.sh。这样子就能够调用 ksshaskpass,当 envoy 调用 ssh-agent 时,ksshaskpass 就会请求您输入 KDE 电子钱包的密码了。而 KDE 电子钱包则会保存您的密码短语,在 ssh-agent 需要的时候由 KDE 电子钱包提供。

pam_ssh

pam_ssh 项目为 SSH 私钥提供了一个 插入式验证模块 (PAM)。当您的密码短语与系统登录用户密码相同的时候,可以为您减去再次输入密码的麻烦。开机登录时,您需要输入您的登录密码,如有需要,还要输入 ssh 密钥的密码短语。您成功登录系统之后,ssh-agent 则会在整个会话期间缓存您已解密的私钥。

要在 tty 模式下使用 pam_ssh,您需要安装位于 AURpam_sshAUR

注意: pam_ssh 2.0 要求所有用于认证的私钥文件必须保存在 ~/.ssh/login-keys.d/ 下。

您可以为您的私钥文件创建一个软链接,并放到 ~/.ssh/login-keys.d/

$ mkdir ~/.ssh/login-keys.d/
$ cd ~/.ssh/login-keys.d/
$ ln -s ../id_rsa

注意将上述例子中的 id_rsa 换成您对应的私钥文件。

编辑 /etc/pam.d/login,将下面例子中高亮加粗的那几行加进去。请注意配置内容的顺序会影响到登录行为,应当按照例子中的来。

警告: PAM 配置丢失会导致系统中所有的用户被锁定。因此,您必须在保存任何 PAM 配置并生效之前,做好配置文件的备份,准备好一份 live CD,以防万一用户被锁定时还有办法恢复原样。您可以参阅 IBM 的 这篇文章 详细了解一下 PAM 的配置。
/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

在上面的例子中,登录认证初始化进程如平常那样启动,用户要输入登录密码。try_first_pass 选项传递给 pam_ssh 模块,它会尝试使用用户密码作为密码短语去解密 ~/.ssh/login-keys.d/ 下的私钥。如果用户密码与密码短语一致,那么私钥可以被解密,用户就无需再次输入相同的密码。如果两者不同,ssh_pam 模块会在用户正确输入登录密码之后输入密码短语。optional 可以保证 ~/.ssh/login-keys.d/ 下没有私钥时,用户也能够正常登录系统。这种情况下,ssh_pam 模块对于没有私钥的用户来说就等于无。

如果您使用其他方式登录,比如使用登录管理器 SLiM (简体中文) 或者 XDM (简体中文),您必须按照类似的方法来编辑 PAM 配置文件,才能正常工作。/etc/pam.d/ 目录下保存有默认的配置文件,您可以参考默认配置文件来进行配置。

pam_ssh 已知问题

pam_ssh 并不广泛使用,其提供的文档也比较少。您应该注意一下该软件包的一些使用限制,比如:

  • pam_ssh < 2.0 不支持 ECDSA,您必须使用 RSA 或者 DSA。
  • pam_ssh 调用的 ssh-agent 进程仅限于当前会话,不能在多个会话中共享。上文提到的 keychain 前端可以避免这个问题。

GNOME Keyring

如果您使用 Gnome,您也可以使用 Gnome 钥匙圈 (GNOME Keyring) 作为 SSH agent。详情请参考 GNOME Keyring

疑难排解

如果您的 SSH 服务器忽略了您的 SSH 密钥对,您需要检查一下相关文件的权限是否正确。

本地机器上:

$ chmod 700 ~/
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/id_ecdsa

服务器上:

$ chmod 700 ~/
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys

如果这样还不能解决您的问题,您可以试试将 sshd_config 中的 StrictModes 设为 no。如果将 StrictModes 关闭就能够顺利认证的话,说明相关文件的权限还没有改对。

提示: 为保证安全,记得把 StrictModes 设为 yes

请确认您的 SSH 服务器支持您所使用的密钥类型, 可以实施 RSA 或者 DSA。某些服务器可能不支持 ECDSA 密钥。

如果还不行,打开 sshd 的 debug 模式,查看连接时的日志输出,查找原因吧:

# /usr/bin/sshd -d

使用 kdm

KDM 并不能直接启动 ssh-agent,KDM 要用 kde-agent[broken link: package not found] 来启动 ssh-agent,但是 kde-agent 自 20140102-1 开始已经被 移除

为了让 KDE 启动时启动 ssh-agent,您可以创建一个脚本用于登录时启动 ssh-agent,还有一个脚本用于注销时杀死进程:

echo -e '#!/bin/sh\n[ -n "$SSH_AGENT_PID" ] || eval "$(ssh-agent -s)"' > ~/.kde4/env/ssh-agent-startup.sh
echo -e '#!/bin/sh\n[ -z "$SSH_AGENT_PID" ] || eval "$(ssh-agent -k)"' > ~/.kde4/shutdown/ssh-agent-shutdown.sh
chmod 755 ~/.kde4/env/ssh-agent-startup.sh ~/.kde4/shutdown/ssh-agent-shutdown.sh