SSH 安全加固篇：通过“运维密码”小程序实现 SSH 双因子认证
本文讲述了如何通过 Linux中国 所开发的微信小程序“运维密码”实现在 Linux 系统上 OpenSSH 双因子认证，从而对 SSH 进行安全加固。
OTP 运维密码
101.207.125.80
貌似无互联网连接的场景下，离线安装包安装，Verification code： 密码不正确，是说安装这个必须要联网才可行吗
来自四川的 Chrome 55.0|Windows 7 用户
2017-04-11T16:45:09.000
116.255.132.12
是这样的。
netb2c [Chrome 56.0|Windows 10]
2017-04-13T16:24:44.000
116.255.132.12
安装google-authenticator过程中若出现二维码无法显示的问题，请检查系统中是否安装了二维码生成器（rpm - q qrencode），若未安装，请安装“yum install qrencode -y”后重试。
netb2c [Chrome 56.0|Windows 10]
2017-04-13T16:27:45.000
116.255.132.12
在centos6.6 或更低的版本中如果遇到，无法进行google-authenticator验证的情况，请将 “auth&nbsp; &nbsp;&nbsp; &nbsp; required&nbsp; &nbsp;&nbsp;&nbsp;pam_google_authenticator.so<br />
” 放在 /etc/pam.d/sshd 最顶端进行测试。
netb2c [Chrome 57.0|Fedora]
2017-04-13T16:32:49.000
116.255.132.12
centos6 安装google-autenticator时建议使用epel源yum安装，编译安装可能会导致一下无法预期的错误，从而导致无法加载验证模块（pam_google_authenticator.so）。
netb2c [Chrome 57.0|Fedora]
2017-04-13T17:00:45.000
202.103.228.86
如何使用公钥+“运维密码”的组合方式登录呢？
reweialaye [Chrome 57.0|Windows 10]
2017-04-20T17:26:40.000
本文讲述了如何通过 Linux中国 所开发的微信小程序“运维密码”实现在 Linux 系统上 OpenSSH 双因子认证，从而对 SSH 进行安全加固。

> 
> 本文讲述了如何通过 [Linux中国](https://linux.cn/) 所开发的微信小程序“[运维密码](/article-8317-1.html)”实现在 Linux 系统上 OpenSSH 双因子认证，从而对 SSH 进行安全加固。
> 
> 
> 


### 背景


![](/data/attachment/album/201703/29/234422h4bvvp9d9olvfzp0.png)


近来很多知名企业都出现了密码泄露，通过单一的密码对敏感和重要信息进行保护已经面临越来越多的挑战，因此业内对多重认证的呼声也越来越高，而其中的双因子认证得到了业界的普遍认可。


#### 什么是双因子认证


<ruby> 双因子认证 <rp>  （ </rp> <rt>  Two-factor authentication </rt> <rp>  ） </rp></ruby>（即 2FA），是一种通过组合两种不同的验证方式进行用户身份验证的机制。


在这种多重认证的系统中，用户需要通过两种不同的认证程序：


* 提供他们知道的信息（如用户名/密码）
* 再借助其他工具提供用户所不知道的信息（如用手机生成的一次性密码）


### SSH 双因子认证实现思路


在 SSH 服务器端安装 Google 身份验证器服务器端组件，这样，在使用密码或密钥登录 SSH 服务器时，同时通过与 Google 身份验证器相匹配的客户端所提供的验证信息来确认登录者的身份和权限。这里的客户端可以使用<ruby> <a href="https://github.com/google/google-authenticator">  Google 身份验证器 </a> <rp>  （ </rp> <rt>  Google Authenticator </rt> <rp>  ） </rp></ruby>应用，也可以使用我们开发的、采用同一个 RFC 规范的“运维密码”微信小程序。


Google 身份验证器所采用的算法规范基于 [TOTP RFC 草案](http://tools.ietf.org/id/draft-mraihi-totp-timebased-06.html)。


（题外话，RSA 硬件令牌，也是采用了类似的机制，只不过客户端是硬件的；而最近 Apple 公司的一些服务所需要的双因子认证也是一样的，但是其用于呈现验证信息的是手机或平板上的 iOS 内部组件，非独立应用。）


#### 关于Google 身份验证器


为了鼓励广泛采用双因子认证的方式，Google 公司发布了<ruby> <a href="https://github.com/google/google-authenticator">  Google 身份验证器 </a> <rp>  （ </rp> <rt>  Google Authenticator </rt> <rp>  ） </rp></ruby>，这是一款开源的、可基于开放规则（如 HMAP/ 基于时间）生成一次性密码的软件。它是一款跨平台软件，可运行在 Linux、Android、iOS 上。Google 公司同时也支持<ruby> 插件式鉴别模块 <rp>  （ </rp> <rt>  pluggable authentication module </rt> <rp>  ） </rp></ruby> PAM ，使其能和其它适用于 PAM 进行验证的工具（如 OpenSSH）协同工作。


Google 身份验证器分为两个部分，分别是[服务器端组件](https://github.com/google/google-authenticator-libpam)和客户端应用，都称之为“Google 身份验证器”，这里，我们为了澄清起见，会在说明时指明。


#### 关于运维密码


Google 公司所开发的身份验证器以简洁著称，但也因此缺乏一些必要的特性，比如备份功能——这使得使用该身份验证器的人时时处于手机丢失的恐慌之中。（虽然 Google 提供的服务器端和自身的服务也提供了紧急验证码，以用于这种情况下的自救，但是很多采用 Google 身份验证器的服务并不支持和提供紧急验证码）


作为一家紧密关注于运维安全、积极倡导信息安全的技术社区，Linux 中国 久已有开发一个新的替代品的想法。恰逢微信推出小程序平台，我们感觉到这正是一个良机，可以充分利用到微信和小程序的便利之处，又适合小程序的使用情境。因此，由 Linux 中国旗下的 LCTT 技术组的白宦成同学独立开发了一款旨在移动互联网场景中提供更好的多因子认证体验的小程序：[运维密码](/article-8317-1.html)。


顺便说一句，在产品初步成熟之后，我们已经将该小程序开源给社区，代码托管于 GitHub：<https://github.com/LCTT/WeApp-Password> ，希望更多的人能够受益和共同完善它，有什么功能需求、错误反馈请到 GitHub 上提出 issue，也欢迎发送拉取请求给我们。


此外，大家在使用过程中，发现什么问题或需要帮助，也可以加入微信体验群：


![](/data/attachment/album/201704/02/183245nlijim1huakgc4ul.jpeg)


扫描识别添加上面的好友，验证信息：“运维密码”，可获得入群邀请。


言归正传，我们来看看如何使用“运维密码”来为你的 SSH 服务提供双因子认证支持。


### 如何开始


首先我们需要一些准备工作：


* 一台运行着 OpenSSH 服务（版本大于 6.2）的 Linux 主机
* 一台能运行微信的智能手机
* 一台支持 SSH 登录的终端


### 在 Linux 系统中安装 Google 身份验证器服务器端组件


第一步需要在运行着 OpenSSH 服务的 Linux 主机上安装 Google 身份验证器服务器端组件。按照如下步骤安装 Google 身份验证器及其 PAM 模块。


#### 用安装包安装 Google 身份验证器服务器端组件


如果你不想自己构建 Google 身份验证器服务器端组件，在几个主流 Linux 发行版上有已经编译好的安装包。安装包里面包含 Google 身份验证器服务器端组件的二进制程序和 PAM 模块。


在 Ubuntu 上安装 Google 身份验证器服务器端组件：



```
sudo apt-get install libpam-google-authenticator

```

在 Fedora 上安装 Google 身份验证器服务器端组件：



```
sudo dnf install google-authenticator

```

在 CentOS 上安装 Google 身份验证器服务器端组件，需要首先[启用 EPEL 软件库](/article-2324-1.html)，然后运行如下命令：



```
sudo yum install google-authenticator

```

#### 编译安装 Google 身份验证器服务器端组件


提示：编译安装建议对 Linux、pam 等熟悉的同学采用。


首先，安装构建 Google 身份验证器所需的软件包。


在 Debian、 Ubuntu 或 Linux Mint 上：



```
sudo apt-get install wget make gcc libpam0g-dev

```

在 CentOS、 Fedora 或 RHEL 上：



```
sudo yum install wget make gcc pam-devel

```

然后下载 Google 身份验证器服务器端组件的源代码：



```
git clone https://github.com/google/google-authenticator-libpam.git
```

编译安装 Google 身份验证器服务器端组件：



```
cd google-authenticator-libpam

./bootstrap.sh
./configure

make

```

如果构建成功，你会在目录中看到 `pam_google_authenticator.so` 和 `google-authenticator` 两个二进制文件。


最后，将 Google 身份验证器的服务器端组件安装到合适位置。其默认会安装到 `/usr/local/lib/security` 下，根据你的系统不同，你可能需要将其符号链接到 pam 库的位置（比如 CentOS 7 会在 `/usr/lib64/security`）。如下图所示：


![](/data/attachment/album/201703/29/223623g70fvkkshvkwtfzv.png)



```
sudo make install

```

至此，Google 身份验证器服务器端组件安装完成。


### 配置 Google 身份验证器服务器端组件及“运维密码”小程序


完成 Google 身份验证器服务器端组件的安装我们仅仅完成了第一步，接着需要对 Google 身份验证器服务器端组件、“运维密码”、OpenSSH 进行配置才能达到我们预期的效果。


#### 配置 google-authenticator 及生成验证密钥


使用以下命令生成验证密钥：



```
./google-authenticator 

```

生成验证密钥的时候，会再次确认信息。



```
Do you want authentication tokens to be time-based (y/n)

```

意思是：你想要生成基于时间生成验证码吗？这里需要需要输入 `y`。


输入 `y`之后你将看到一个代表着该“场景”密钥的二维码和密钥字符串，它使用如下二维码图形格式表示我们数字形态的密钥（这里也提供了一个用于在浏览器中再次显示该二维码的 URL，但是需要翻墙）。接着我们要用到它在“运维密码”上完成配置。（安装 google-authenticator 过程中若出现二维码无法显示的问题，请检查系统中是否安装了二维码生成器：`rpm - q qrencode`，若未安装，请运行命令 `yum install qrencode -y` 安装后重试。）


![](/data/attachment/album/201703/29/223836dy0wpp1ct0phy5g1.png)


在二维码和密钥字符串后面，接着显示了一个当前的校验码和几个紧急密钥。紧急密钥你可以另行保存的一个安全的地方，以防你在无法使用 Google 身份验证器应用或“运维密码”时使用（紧急密钥是 8 位的，不同于普通的 6 位密钥，也是一次性使用的）。


![](/data/attachment/album/201703/29/223951fn5r958jililyz0k.jpg)


保存 Google 服务器端组件的配置文件，Google 身份验证器虽然运行了，但是相关设置还没有保存，接下来会提示保存：



```
Do you want me to update your "/root/.google_authenticator" file? (y/n) 

```

意思是：你想将配置文件更新到 `/root/.google_authenticator` 保存吗？


输入 `y` 回车。


**禁止同一令牌多次登录**



```
Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n)

```

意思是：你是否要禁用同一密钥多次登录，这将限制你每 30 秒只能使用该密钥登录一次，但这能够让你可以更多地被提醒受到了中间人攻击，甚至能够防止这种攻击。


输入 `y` 回车。


**时间容错设置**



```
By default, tokens are good for 30 seconds. In order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with
poor time synchronization, you can increase the window from its default
size of +-1min (window size of 3) to about +-4min (window size of
17 acceptable tokens).

Do you want to do so? (y/n) 

```

意思是：默认情况下，密钥在 30 秒内有效，为了防止由于客户端与服务器时间偏移（时间相差太大）导致认证失败，google 身份验证器设计了时间容错措施。可以让你使用与当前时间偏移 1 到 4 分钟的密钥。


这个可根据实际情况进行配置，一般一分钟就足够了。


输入 `y`回车。


**暴力破解防护**



```
If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.

Do you want to enable rate-limiting (y/n) 

```

意思是：为了避免暴力破解，可以启用速率限制，默认情况下，每 30 秒只能尝试 3 次。


输入 `y`回车。


**配置完成**


配置完成后会在home目录下生成一个权限为 400 的隐藏文件，如下图所示：


![](/data/attachment/album/201703/29/224056m505osu5rj77yr5n.jpg)


#### 配置运维密码


**打开微信小程序**


打开微信，依次点击“发现”，“小程序”，输入“运维密码”并搜索。或长按识别下列二维码：


![](/data/attachment/album/201704/18/231139di7prrdkpi7o45rd.jpg)


点击“运维密码”进入应用，然后点击列表下方的“添加场景”：


![场景列表](/data/attachment/album/201704/14/083650lp2xivllix52x5p3.jpg "场景列表")


这会调起“扫一扫”功能，请扫描配置 google-authenticator 时所生成的二维码，然后会识别出该场景信息，你可以根据需要修改场景信息，点击确定添加场景。


这样 Google 身份验证器就和“运维密码”匹配上了。下面我们要使 SSH 服务可以支持该验证。


### 配置 SSH 服务


**添加认证模块**


使用如下命令在 `/etc/pam.d/sshd` 文件添加认证模块：



```
echo "auth required pam_google_authenticator.so" >>/etc/pam.d/sshd

```

在 Centos 6.6 或更低的版本中如果遇到无法进行 google-authenticator 验证的情况，请将 `auth required pam_google_authenticator.so`放在 `/etc/pam.d/sshd` 文件的最顶端进行测试一下。


配置挑战式密码认证：



```
sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/g' /etc/ssh/sshd_config

```

重启 sshd 服务：



```
systemctl restart sshd.service

```

**切记，如果你是远程登录到服务器上配置，切勿退出当前的 SSH 会话，而应该另外开一个会话去测试 SSH 登录。重启不会中断当前的 SSH 会话。**


### 测试登录


以上配置完成基本上就搞定了，下面我们进行测试。


#### 密码登录测试


另外开一个终端窗口进行连接，不要关闭当前的 SSH 连接。


输入命令登录主机：



```
ssh root@10.112.2.3

```

首先输入服务器的密码，接着会让输入“运维密码”生成的 6 位数字密钥。


![场景详情](/data/attachment/album/201704/14/083653nelhc0w7pwme1cl0.jpg "场景详情")


如下图：


![](/data/attachment/album/201703/29/225450t8tretbcyn5ierkc.jpg)


我们可以看到，在登录的时候，需要配合“运维密码”才能登录服务器。


#### 公钥登录测试


如果使用公钥登录呢？以上配置是不是也是需要配合“运维密码”才能登录的，我们进行验证一下：


首先，我们将本机的公钥复制到远程机器的 `authorized_keys` 文件中。



```
ssh-copy-id root@10.112.2.3

```

![](/data/attachment/album/201703/29/225550s5at35jpqppi5ppa.jpg)


登录测试：


![](/data/attachment/album/201703/29/230656kzuywsks3yqqyb38.jpg)


我们可以看到，不需要输入任何密码和一次性密钥，直接登录到了系统。


#### 结合运维密码配置增强型SSH安全选项


针对上面公钥登录的测试，如果认为还不是很安全，我们可以设定如下登录场景：公钥 + 密码 + 运维密码，我们需要如何做呢？


配置 SSH 公钥双因子


修改 `/etc/ssh/sshd_config` 配置文件：



```
echo "AuthenticationMethods publickey,keyboard-interactive:pam" >>/etc/ssh/sshd_config

```

重启 SSH 服务：



```
systemctl restart sshd.service

```

登录测试（同样，请新开窗口）：



```
ssh root@10.112.2.3

```

![](/data/attachment/album/201703/29/230833bwbvtrjnvrb9j9ys.jpg)


可以看到，登录的时候是需要验证公钥、密码，及输入“运维密码”生成的密钥才能登录到系统。


没有密钥的情况下尝试登录测试，如下图：


![](/data/attachment/album/201703/29/230911xg58rugdnuw81z54.jpg)


### 总结


至此，本文结束，更多的使用细节可以参照小程序内的帮助，或[此文](/article-8317-1.html)。


如有错误及不足欢迎指正。也欢迎大家加入到这个小程序的开发当中，将这个小程序应用到你的应用场景中。

