分类: LINUX
2009-12-01 14:31:56
代理程序转发和 keychain 改进 |
|
级别: 初级
Daniel Robbins, 总裁兼首席执行官, Gentoo Technologies,Inc.
2002 年 2 月 01 日
在这一系列的第三篇文章中,Daniel Robbins 向您显示了如何利用 OpenSSH 代理程序连接转发来增强安全性。他还共享 keychain shell 脚本近期的改进。
我们中的许多人都使用非常优秀的 OpenSSH 作为古老的
telnet
和
rsh
命令的替代品,OpenSSH 不仅安全而且是加密的。OpenSSH 更吸引人的特性之一是它能够使用以一对互补的数字式“密钥”为基础的 RSA
和 DSA 认证协议来认证用户。RSA 和 DSA
认证承诺不必提供密码就能够与远程系统建立连接,这是其主要魅力之一。有关更多背景资料,请参阅关于 OpenSSH
密钥管理的本系列文章中以前的几篇,分别包括 RSA/DSA 认证(第 1 部分)和
ssh-agent 和 keychain(第 2 部分)。
由于第 2 部分发表在 2001 年 9 月的
developerWorks上,并且稍后在 Slashdot 和 Freshmeat(请参阅本文后面的
参考资料,获取到这些站点的链接)上引用了此文,因此,许多人已经开始使用
keychain
,而且它已经做了许多更改。我收到了世界各地开发人员编写的约 20 个高质量的补丁程序。我已将其中许多补丁代码合并入
keychain
源码中,它目前的版本是 1.8(请参阅
参考资料)。我真诚地感谢所有提交补丁程序、错误报告、功能请求及感谢信的那些人。
在
上篇文章中,我花了一些时间来讨论运行
ssh-agent
在安全性方面的利弊。第二篇文章发表在
developerWorks的几天后,我收到来自 Sarnoff Corporation 的 Charles Karney 的一封电子邮件,他非常礼貌地通知了我 OpenSSH 新的认证代理程序转发的能力,我们将会简要地讨论这一能力。另外,Charles 强调:在
不可信机器上运行
ssh-agent
是十分危险的:如果有人成功地获取系统上的 root 访问权,那么就
能从
ssh-agent
中抽取您解密的密钥。即使抽取密钥有一定的困难,但是它还是在专业的解密高手的能力范围之内。而且,基本事实就是偷窃私钥是
可能的,这意味着我们应首先采取措施来防止这种情况的发生。
为了简单描述保护私钥的策略,首先必须把我们访问的机器归为两种类型中的一类。如果特定主机是安全性良好或是孤立的 ― 要成功获取主机的 root 访问权几乎不可能 ― 那么,那台机器应被认为是
可信主机。不过,如果许多其他人使用这台机器或者您怀疑系统的安全性,那么这台机器应被认为是
不可信主机。为防止您的私钥被他人抽取,绝对不应在不可信主机上运行
ssh-agent
(和由此启动的
keychain
)。 那样的话,即使系统安全性受到威胁,由于
ssh-agent
没有运行,闯入者在第一时间内也不能抽取密钥。
但是,这产生了一个问题。如果不能在不可信主机上运行
ssh-agent
,那么如何从这些系统上建立安全的、无密码的
ssh
连接呢?答案是:只在
可信主机上使用
ssh-agent
和
keychain
,并利用 OpenSSH 新的
认证转发能力将无密码的认证扩展到任何不可信主机上。简略地说,就是通过允许远程
ssh
会话来联系运行在可信系统上的
ssh-agent
,使认证转发工作。
|
要了解认证转发工作的原理,让我们首先看一下一个假设情况,其中用户
drobbins
有一个称为
lappy
的可信的便携式电脑、一个称为
trustbox
的可信服务器和另外两个他必须访问的不可信系统,分别称为
notrust1
和
notrust2
。当前,他在所有这四台机器上都使用
ssh-agent
以及
keychain
,如下所示:
这种方法所带来的问题是如果有人获取
notrust1
或
notrust2
的 root 访问权,那么这个人当然可以从现在易受攻击的
ssh-agent
进程中抽取密钥。为了解决这个问题,
drobbins
停止运行不可信主机
notrust1
和
notrust2
上的
ssh-agent
和
keychain
。事实上,为了更为小心,
drobbins
决定只在
lappy
上使用
ssh-agent
和
keychain
。这样限制了他解密的私钥的泄露,同时防止他的私钥被偷窃:
当然,这种方法带来的问题是
drobbins
现在只能从
lappy
建立无密码的连接。让我们看一下如何启用认证转发并解决这个问题。
假设所有机器都运行 OpenSSH 的最近版本,通过使用认证转发,我们能解决这个问题。认证转发允许远程
ssh
进程联系您正在本地可信机器上运行的
ssh-agent
― 而不要求在您正运行
ssh
的同一台机器上运行
ssh-agent
的一个版本。这通常允许您在单个机器上运行
ssh-agent
(和
keychain
),并且这意味着源于这台机器的所有
ssh
连接(直接或间接)都将使用本地
ssh-agent
。
为了启用认证转发,我们在
lappy
和
trustbox
的
/etc/ssh/ssh_config 中添加了下面行。请注意:这是
ssh
的配置文件(
ssh_config),而不是 ssh 守护进程
sshd
的配置文件(
sshd_config):
|
现在,为了利用认证转发,
drobbins
可以从
lappy
连接到
trustbox
,然后在不提供任何连接的密码的情况下,从
trustbox
连接到
notrust1
。这两个
ssh
进程都“进入”运行在
lappy
上的
ssh-agent
:
|
如果您尝试使用类似的配置,并发现代理程序转发不起作用,请尝试使用
ssh -A
替代原来单纯的
ssh
来明确启用认证转发。这里是当我们使用上面提到的认证转发而登录到
trustbox
和
notrust1
时,实现此操作的内部运行图:
正如您看到的,当
ssh
连接到
trustbox
时,它维持与运行在
lappy
上的
ssh-agent
的连接。当产生从
trustbox
到
notrust1
的
ssh
连接时,这个新的
ssh
进程维持与以前
ssh
的认证连接,这样有效地延伸了链。这个认证链是否能延伸到
notrust1
以外的其它主机取决于
notrust1
的 /etc/ssh/ssh_config 是如何配置的。 只要启用了代理程序转发,通过使用在可信
lappy
上运行的
ssh-agent
,这个链上的所有部分都能认证。
|
认证转发提供了许多在此没有提到的安全性优点。为了让我相信代理程序连接转发的重要性,Charles Karney 与我分享了以下三个安全性优点:
ssh-agent
只运行在可信机器上。这样防止闯入者进行远程
ssh-agent
进程的内存转储并从转储中抽取出您的解密私钥。
使
用认证代理程序连接转发的一个缺点是:它不能解决允许 cron 作业利用 RSA/DSA 认证这个问题。解决这个问题的一个方案是设置所有需要
RSA/DSA 认证的 cron 作业,这样它们就可以从局域网中的一台可信机器上执行。如果需要的话,这些 cron 作业能使用 ssh
连接到远程系统来实现自动备份、使文件同步等操作。
既然我们已经了解了认证代理程序连接转发,那么让我们转到近期对
keychain
脚本本身所做的改进上。
|
感谢用户发来补丁程序,许多重要的改进已添加到
keychain
源码中。 用户提交的
keychain
补丁程序中有几个与功能有关。例如,您应记得
keychain
创建了一个 ~/.ssh-agent 文件;这个文件的名字现在已经改为 ~/.ssh-agent-[hostname],这样
keychain
可以使用从几个不同物理主机上能访问已挂装 NFS 的主目录。除了 ~/.ssh-agent-[hostname] 文件外,现在还有一个 ~/.ssh-agent-csh-[hostname] 文件,兼容
csh
的 shell 利用 source 命令读入并执行该文件。最后,添加了一个新的
--nocolor
选项,这样,如果您碰巧在使用不兼容 vt100 的终端时,就能禁用彩色化功能部件。
|
当完成了许多重要的功能改进时,对
shell 兼容性问题也做了大量的修正。您看,keychain 1.0 需要
bash
,而以后的版本则改为可以使用任何
sh
兼容的 shell。这一更改使得
keychain
跳出固有的框架,可以在包括 Linux、BSD、Solaris、IRIX 和 AIX 以及其它 UNIX 平台的几乎所有 UNIX 系统上运行。转至
sh
并与常规 UNIX 兼容,这已经是困难重重了,而同时它也经过了大量的学习经验。创建运行在所有这些平台上的单个脚本事实上是非常棘手的,主要因为我根本无权访问这些操作系统中的大多数系统!要感谢的是,全球范围内的
keychain
用户这样做了,并且许多人在识别兼容性问题以及提交补丁程序来解决它们等方面提供了非常大的帮助。
事实上,有两类兼容性问题必须解决。首先,我需要确信
keychain
只使用所有
sh
实现下完全支持的内置件、表达式和操作符,包括所有流行的免费和商业 UNIX
sh
shell、
zsh
(以
sh
兼容的模式)和
bash
版本 1 和 2。这里是用户提交的应用到
keychain
源码中的一些 shell 兼容的修正:
由于较早的
sh
shell 不支持
~
约定来引用用户的主目录,因此将使用
~
的行更改为使用
$HOME
来代替:
|
接着,所有对
source
的引用都更改成
.
,以确保与纯 NetBSD 的
/bin/sh
兼容,因为它根本不支持
source
命令:
|
按照这个方法,我还应用了一些与性能相关的好的修正。一位聪明的 shell 脚本编写者告诉我不要通过输入
touch foo
来“更新”文件的修改日期,您可以这样做:
|
通过使用内置的 shell 语法,而不是使用外部二进制文件,这样避免了使用
fork()
,而脚本却变得更加有效。
> foo
应使用任何兼容
sh
的 shell;但是,好象
ash
并不支持它。对大多数人来说这不应是个问题,因为
ash
更象是急救磁盘类型的 shell,而不是人们每天都要使用的程序。
|
获取在多个 UNIX 操作系统下运行的脚本不单单需要坚持纯粹的
sh
语法。请记住,大多数脚本还要调用诸如
grep
、
awk
、
ps
和其它命令的外部命令,而且必须尽可能以与标准相符的方法来调用这些命令。例如,包含在大多数 UNIX 版本中的
echo
能识别
-e
选项,而 Solaris 中的
echo
却不能识别 ― 当使用它时,它只把
-e
打印到标准输出(stdout)。因此为了处理 Solaris,
keychain
现在自动检测
echo -e
是否起作用:
|
上面的代码中,如果支持
-e
,那么将
E
设置为
-e
。然后,可以按如下所示调用 echo:
|
通过使用
echo $E
,而不是
echo -e
,可以根据需要动态地启用或禁用
-e
选项。
可能最重要的兼容性修正涉及到更改
keychain
如何检测当前正在运行的
ssh-agent
的进程的方法。以前,我使用
pidof
命令来这样做,但是由于有几个系统没有
pidof
,所以不得不抛弃这个方案。实际上,
pidof
无论如何都不是最佳的解决方案,因为它列出系统上运行的
所有
ssh-agent
进程,而不管用户是谁,但我们实际上感兴趣的是当前有效的 UID 所拥有的所有
ssh-agent
进程。
所以,为抽取所需的进程标识,我们不使用
pidof
,而是转向将
ps
输出通过管道输送到
grep
和
awk
上。 这是一个用户提交的修正:
|
上面的管线将
mypids
变量设置为当前用户拥有的所有
ssh-agent
进程的值。
grep -v grep
命令是管线的一部分,这样确保
grep ssh-agent
进程不会成为我们的 PID 列表中的一部分。
这种方法从概念上来说非常好,但是因为
ps
选项未在各类 BSD 和 System V 的 UNIX 派生系统上标准化,所以使用
ps
开启了一个全新的尚未解决的难题。这里是一个示例:虽然
ps uxw
在 Linux 下起作用,而在 IRIX 下不起作用。
ps -u username -f
在 Linux、IRIX 和 Solaris 下起作用,而在只理解 BSD 样式的
ps
选项的 BSD 下不起作用。为了解决这个问题,在执行
ps
管线之前,
keychain
会自动检测当前系统的
ps
是使用 BSD 语法或还是 System V 语法:
|
为了确保我们能同时使用 System V 和 BSD 样式的
ps
命令,脚本试着运行
ps uxw
,而丢弃任何输出。如果这个命令的错误码为零,那么我们知道
ps uxw
正常工作,并且我们正确地设置了
psopts
值。但是,如果
ps uxw
返回一个非零的错误码(指出我们需要使用 BSD 样式的选项),那么我们试着运行
ps -u `whoami` -f
,并再次丢弃了任何输出。此时,我们有希望发现可以使用的
ps
是 BSD 的变体还是 System V 的变体。如果我们不知道答案,那么打印出错误并退出。但是很有可能这两个
ps
命令中的一个工作正常,在这样的情况下,执行上面代码段的最后一行,即
ps
管线。通过紧跟在
ps
后面使用
$psopts
变量扩展,我们能将正确的选项传送给
ps
命令。
ps
管线还包含一个
grep
,它的确是一个宝物,是 Hans Peter Verne 好心发给我的。 请注意
grep -v grep
不再是管线的一部分;实际上它已经被除去并且
grep "ssh-agent"
已经改为
grep "[s]sh-agent"
。这样一个
grep
命令最后执行与
grep ssh-agent | grep -v grep
相同的操作;您知道为什么吗?
|
是不是有点困惑?如果您确定
grep "ssh-agent"
和
grep "[s]sh-agent"
应匹配完全相同的文本行的话,那么您是正确的。所以当
ps
的输出通过管道输送给它们时,为什么它们生成不同的结果呢?这里是它的工作原理:当使用
grep "[s]sh-agent"
时,您更改了
grep
命令在
ps
进程列表中显示的方式。通过这样做,防止
grep
与它本身相匹配,因为
[s]sh-agent
字符串与
[s]sh-agent
正则表达式不匹配。那样不是很完美吗?如果您仍不太明白,请用一下
grep
,您很快就会明白了。
|
本专栏文章对讨论的 OpenSSH 作出了结论。希望您已经学到了有关 OpenSSH 的很多知识,足以开始使用 OpenSSH 来保护您系统的安全。
|
|
Daniel Robbins 居住在美国新墨西哥州阿尔布开克,他是 Gentoo Technologies, Inc. 的总裁兼首席执行官,他主创了 ,这是一种用于 PC 机的高级 Linux,以及 Portage系统,是用于 Linux 的下一代移植系统。他还是几本 Macmillan 出版的书籍 Caldera OpenLinux Unleashed、 SuSE Linux Unleashed和 Samba Unleashed的投稿人。Daniel 自二年级起就和计算机结下不解之缘,那时他最先接触的是 Logo 编程语言,并沉溺于 Pac Man 游戏中。这也许就是他至今仍担任 SONY Electronic Publishing/Psygnosis首席图形设计师的原因所在。Daniel 喜欢和他的妻子 Mary 以及他们的女儿 Hadassah 一起共度时光。您可以通过 与 Daniel 联系。 |