概述

假设要以用户 rainux 的身份登录运行 OpenSSH Server 的远程主机 www.rainux.org(现在几乎所有的 Linux 服务器都使用 OpenSSH Server 作为 SSH Server),那么需要做的操作如下:

  1. 生成用于 SSH 身份认证的密钥。密钥由公钥和私钥组成,一个公钥只对应一个私钥,一个私钥也只对应一个公钥。

  2. 将 OpenSSH 格式的公钥文本追加到远程主机上的 ~rainux/.ssh/authorized_keys 文件里(~rainux 意为用户 rainux 的 HOME 目录)。注意公钥文本中应该没有换行符,一行就是一个公钥。

  3. 确保 ~rainux/.ssh/authorized_keys 权限为 600,~/rainux/.ssh 目录权限为 700,并且它们的所有者都是 rainux。

  4. 指定 SSH 客户端使用对应的私钥并以用户 rainux 的身份登录 www.rainux.org,此时无需输入 rainux 在 www.rainux.org 上的密码。

注意,如果私钥是以加密形式存储的(强烈建议使用一个强壮的密码加密私钥,这样即使私钥文件被窃取,也无法被直接使用),第四步仍然需要输入加密私钥所用的密码。但是 PuTTY 和 OpenSSH 都有提供一个代理程序用于避免多次重复输入密码。运行代理程序后将私钥添加到其中,并且输入一次加密私钥所用的密码,然后保持代理程序的运行,以后 SSH 客户端即可在需要使用私钥进行身份认证的场合请求代理程序去完成身份认证(这是为了确保私钥不会被泄漏到任何使用代理程序的客户端上),无须再次输入任何密码。

Linux / Cygwin / MSYS 上使用 OpenSSH Client

生成密钥

执行 ssh-keygen 接受默认文件名,并且输入加密私钥用的密码(passphrase)即可生成私钥 ~/.ssh/id_rsa 和对应的公钥 ~/.ssh/id_rsa.pub。其中 id_rsa.pub 的内容可以直接追加到远程主机的 authorized_keys 文件里。

使用私钥

把公钥部署到远程主机上后,即可直接执行 ssh rainux@www.rainux.org 来登录远程主机。但如果私钥是加密形式保存,如上所述应该使用代理程序 ssh-agent 来避免每次输入密码。

直接执行 ssh-add 命令可以添加刚才生成的私钥到 ssh-agent 里,也可以用 ssh-add -l 查看已经添加的私钥。如果执行 ssh-add 时提示无法连接身份认证代理,则需要使用以下命令来启动 ssh-agent。

eval `ssh-agent`

使用 eval 是为了执行 ssh-agent 输出的设置环境变量的 bash 命令,以确保 ssh-add 可以通过 SSH_AUTH_SOCK 环境变量找到 ssh-agent。

GNOME 的桌面系统如果有安装 gnome-keyring,它会自动管理 ssh-agent,通常甚至无须手工添加私钥到 ssh-agent 里,第一次使用公钥时 gnome-keyring 就会提示输入一次密码,以后则不再需要。

Windows 上使用 PuTTY

生成密钥

PuTTY下载页面下载 putty-0.60-installer.exe 或者 putty.zip,两者都包含 PuTTY 所有的组件。

安装好 PuTTY 之后(对于 zip 版本来说,解压到任意目录即可)运行 PuTTYGen 生成自己的密钥。选择密钥类型为 SSH-2 RSA,点击 Generate,按照提示在 PuTTYGen 窗口内随机移动鼠标直到进度条达到 100%,然后 PuTYYGen 会生成密钥并且显示其公钥部分信息。这里可以填写一个有意义的注释,然后输入一个用来加密私钥的强壮的密码(Key passphrase)。最后点击 Save private key 将私钥保存为一个 .ppk 文件。不需要单独 Save public key,因为 .ppk 文件里已经包含了公钥。并且通过 Save public key 保存出来的公钥文件格式与 OpenSSH 的格式并不相同,若要将公钥部署到使用 OpenSSH Server 的服务器上,只能使用 PuTTYGen 窗口上显示的那一段 Public key for pasting into OpenSSH authorized_keys file。

使用私钥

将公钥部署到远程主机上之后,使用 PuTTY 登录该远程主机时可以在 Connection > SSH > Auth 里选择用于身份认证的私钥文件。同样,如果私钥是加密形式保存,应该使用代理程序 Pageant 以避免每次输入密码。

最简单的办法是创建一个 Pageant 的快捷方式,并且将 .ppk 私钥文件作为参数加到快捷方式的“目标”栏里,添加之后看上去应该像这样:

C:\PuTTY\pageant.exe D:\main.ppk "D:\My Secrets\secondary.ppk"

就像上面的例子一样,如果私钥文件路径中带有空格,应该使用引号将其括起来。通过这个快捷方式启动 Pageant 则会自动装载指定的私钥文件,并且立即询问用于加密私钥的密码。以后只要保持 Pageant 的运行,并且在 PuTTY 的某个 Saved session 里设置 Connnection > Data 里设置 Auto-login username,即可实现双击该 session 则完全自动登录。

Tags: , ,

做 Web 开发的工作经常需要 ssh 或者 scp 连接一堆远程主机,同样是 Linux 主机,其中一些创建 ssh 连接速度特别慢,连接建立之后执行操作速度却很正常,看来应该不是网络原因。

Google 了一下,这位老兄也遇到类似的问题,看完他描述的问题才知道可以用 ssh -v 来查看详细的连接建立过程,马上用一台建立连接很慢的主机试了一下,在一大堆输出信息中发现在这里停留最久:

debug1: Authentications that can continue: publickey,gssapi-with-mic,password
debug1: Next authentication method: gssapi-with-mic
debug1: Unspecified GSS failure.  Minor code may provide more information
No credentials cache found

debug1: Unspecified GSS failure.  Minor code may provide more information
No credentials cache found

debug1: Unspecified GSS failure.  Minor code may provide more information


debug1: Next authentication method: publickey

原来是因为尝试了个没有意义而且会失败的 gssapi-with-mic 认证方式浪费了时间,打开 /etc/ssh/ssh_config 把里面的 GSSAPIAuthentication yes 改成 no 关掉它,即可让 ssh 直接尝试美妙的 publickey 认证方式。

禁用 GSSAPIAuthentication 前后建立 ssh 连接时间的对比:

rainux@mutalisk:~$ time ssh root@i-will-not-tell-you.com exit

real    0m18.488s
user    0m0.004s
sys     0m0.008s
rainux@mutalisk:~$ time ssh root@i-will-not-tell-you.com exit

real    0m3.531s
user    0m0.016s
sys     0m0.000s
Tags: , ,

六月真是个好时节啊

六月 5th, 2008

Ruby 1.8.7Rails 2.1.0 前两天相继发布。Firefox 3.0 昨天进入 rc2 阶段,并且将在六月中旬发布 3.0 正式版。开发了十五年的 Wine 终于进入 1.0 的 rc 阶段,并且即将发布 1.0 正式版。

看来是时候更新 SCV Selector 了,可惜预想了很久的 rep 版本识别还是没有时间去研究……

Tags: ,

最近需要测试 Flash 内容,搜到了这个很简单的办法,原文是针对 etch 的,对于 lenny 来说更简单,官方仓库里已经有 nspluginwrapper 了,直接 apt 安装即可。

aptitude install nspluginwrapper

然后在自己的账户(非 root 账户)里下载 Adobe 的 flash player 并使用 nspluginwrapper 安装为 Iceweasel/Firefox 的插件即可。

wget http://fpdownload.macromedia.com/get/flashplayer/current/install_flash_player_9_linux.tar.gz
tar -zxf install_flash_player_9_linux.tar.gz
mkdir ~/.mozilla/plugins/
mv install_flash_player_9_linux/libflashplayer.so ~/.mozilla/plugins/
nspluginwrapper -i ~/.mozilla/plugins/libflashplayer.so
# Start/restart iceweasel/firefox/whatever
Tags: ,

注: 本文第一次写作时没有进行全面的测试,由于 svn update 操作更新文件时会破坏 NTFS 上的硬链接而导致第一次的方法无法正常使用。现在你看到的已经是新的方法。在此为我的疏忽向大家道歉。

现在我的生活早已离不开 Vim 了,无论是在公司还是在家里,无论是在本地计算机还是远程服务器上,Vim 必定是我使用得最多的程序之一。当然了,Vim 虽好,也一定要有符合自己习惯的 .vimrc 和插件集才能发挥出更大的潜能,让我们用得更爽。也就是说,.vimrc 肯定是会被经常修改的,插件集也应该会时常添加新成员或进行版本更新。显然,要在家里和公司以及一些远程服务器之间频繁地同步这些修改是件很烦人的事。幸好,我们有 Subversion 可以让这一切变得简单。

让我们从公司的 Linux 桌面机上开始。

# 将 $HOME 里的 .vimrc 和 .vim 目录放到 vimfiles 目录里以便进行 SVN 导入操作
cd ~
mkdir vimfiles
mv .vimrc vimfiles
mv .vim vimfiles
svn import vimfiles http://svn.rainux.org/vimfiles/trunk
# 从 SVN 里 checkout 出一份 working copy 到 $HOME 里,
# 并且设置忽略掉 $HOME 里其他所有文件
svn co http://svn.rainux.org/vimfiles/trunk .
svn ps svn:ignore '*' .
svn ci -m "Let's ignore all files those not managed by Subversion in $HOME."
# 用来进行导入操作的 vimfiles 目录已经不再有任何存在的意义了
rm -rf vimfiles

好了,我们的 .vimrc 和插件集已经版本化了,下班回家。

# 先备份一下家里 Windows 上的文件,这假设我们没有使用 Cygwin
CD %HOME%
MOVE .vimrc .vimrc.backup
MOVE vimfiles vimfiles.backup
svn co http://svn.rainux.org/vimfiles/trunk .

现在我们遇到问题了,Vim 在 Windows 上会尝试从 %HOME%\vimfiles 目录加载用户脚本,而不会理会 %HOME%\.vim 目录。我的解决办法是为 .vim 目录建立符号链接 vimfiles,这需要 %HOME% 所在分区必须是 NTFS 文件系统。新的问题是 Windows 本身并没有附带创建符号链接的工具,需要用 Sysinternals 提供的命令行工具 junction.exe 或者 Windows 2000 Resource Kit 里的 linkd.exe 来创建。

CD %HOME%
junction vimfiles .vim

OK,开始享受 Subversion 为我们带来的便利吧!不仅仅可以方便的同步在公司和在家里对 .vimrc 及插件集的修改,在远程服务器上要取得这些最新的修改也变得非常容易。什么,某个服务器上还在使用古老的 Vim 6.3 ?没有关系,为 http://svn.rainux.org/vimfiles 创建一个名为 6.x 的 branch,删掉无法用于 6.x 的插件即可。

最后,如果你对我的 Vim 配置感兴趣,可以从上面提到的 URL 里 checkout 一份出来看看,是的,它是真实的 URL。:)

Tags: , , , ,

Vim? Emacs!

十一月 3rd, 2007

我: Hmmm,你的 Emacs 编辑 Rails 项目明显没有偶的 Vim 爽嘛,连 RHTML 里的 Ruby 代码都没法语法高亮。

W 君: (看了偶演示 Vim + rails.vim 后)确实不太方便。

我: 那何不转过来用 Vim?反正 Vim 你也经常用(W 是 Linux 系统管理员)。

W 君: 不行,我用 Emacs 是有很特殊的原因地。

我: ?

W 君: ……

我: ……?

W 君: …… 因为 Emacs 的图标是牛啊。(同事 W 君是股民 -_-b)

我: …… 狂晕!

Tags: , , , ,

按照《使用 Subversion 进行版本控制》 书中的建议,项目在被 import 到版本库前,最好能在项目根目录下建立 trunk、branches、tags 等目录,并将项目源代码存放在 trunk 子目录下,这将有利于工作中对相对稳定的项目做较大修改时创建分支。

但是一些历史项目,或者我们自己在对 Subversion 不够熟悉的情况下创建的项目,可能是直接把项目源代码存放在项目根目录里的,这种情况下要修改版本库布局,将所有文件移动到 trunk 子目录下就比较麻烦了。设想过使用 svn mv 命令应该可以做到,但是它会产生一个整个项目所有文件被移动的 revision,不但很不优雅,也不便于以后查看历史记录以及回溯到旧版本。

这种情况下我们可以这样做:

  1. 将整个项目的版本库导出为一个 dump 文件(它是一个可编辑的文本/二进制混合文件,其中包含了所有的 revision 记录)。
  2. 在 dump 文件中所有的路径前加上 trunk/ 前缀,使它们处于 trunk 子目录下。
  3. 在 dump 文件中 revision 1 里直接加入 trunk 等目录的创建。
  4. 删掉项目的版本库并重建新版本库,然后导入我们修改后的 dump 文件。

进行这样的操作后,项目的版本库状态就是从 revision 1 开始所有的文件就存在于 trunk 目录下了。

具体操作步骤,以在 Linux 命令行下为例:

svnadmin dump /path_to_svn_repos/your_project > your_project.svn_dump
vim -b your_project.svn_dump
# 以下为 Vim 命令
:%s/^Node-path:\s\+/\0trunk\//ge
:%s/^Node-copyfrom-path:\s\+/\0trunk\//ge
# Vim 命令结束
# 添加 trunk 等目录的创建,见后文详述
rm -rf /path_to_svn_repos/your_project
svnadmin create /path_to_svn_repos/your_project
svnadmin load /path_to_svn_repos/your_project < your_project.svn_dump

注意一定要用 -b 参数启动 Vim,这样可以避免 Vim 在没有配置好多字节文件支持的选项时对 dump 文件可能的破坏。添加 trunk 等目录的方法是在 Revsion-number: 1 那一行后的第一个 PROPS-END 行后插入以下蓝色的代码。注意那些空行,有些是空一行有些是空两行,我是严格按照已有的目录的格式来写的,在没有看到这个 dump 文件的规格文档前最好连空行也照原样复制。

...
Revision-number: 0
Prop-content-length: 56
Content-length: 56

K 8
svn:date
V 27
2007-10-11T14:15:09.781135Z
PROPS-END

Revision-number: 1
Prop-content-length: 100
Content-length: 100

K 7
svn:log
V 0

K 10
svn:author
V 6
rainux
K 8
svn:date
V 27
2007-10-11T14:15:48.344335Z
PROPS-END
Node-path: trunk
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10

PROPS-END


Node-path: branches
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10

PROPS-END


Node-path: tags
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10

PROPS-END

Node-path: trunk/README
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 8001
Text-content-md5: 2ffe7715fd6ba5509508a29022c22a17
Content-length: 8011

PROPS-END
== Welcome to Rails
...
Tags: , ,

四个小时前,在上海紫金山大酒店四楼的会场见到了自由软件社区的大哥级人物 Ian Murdock。一身白色 T shirt 的他看上去比活动宣传照上更年轻。

虽然了解、使用和喜爱 Debian 已经有四年了,但对于创始人 Ian Murdock 却没有太多的了解,只知道他现在为 Sun 工作,Debian 这个词源自他当时的女友(现在的妻子)Debra 和他自己名字的组合。很浪漫的命名方式,如果有机会,我也希望能做这样的事。 :) 看到活动的介绍后,才知道 Ian 还是 Linux Foundation 的 CTO,Linux Standard Base 的 Chief。

今天活动的主要内容是 Ian 的个人演讲和跟现场的 Linux 用户交流。给我的印象是 Ian 比较健谈,对大家提出的大多数问题都聊了很多。可惜,可惜,最大的遗憾是,他说的话有 95% 我都没听懂……-_-b 平时自诩流畅阅读英文技术文档的我,听力实在是太差了。(以后一定要经常对照字幕看原版电影来练习听力!)

晚上九点左右,活动结束后,Ian 还坐在舞台的一角为大家签名,跟大家合影。我也有幸跟他合影了一张(感谢好友 AKAI 帮助拍摄!),本来想说 "I've loved and enjoyed Debian for several years and will continue, many thanks to your hard work!",不过没好意思说出口。-_-b

这次活动到会的粗略估计大概有四五百人,应该是超出了报名人数不少,以致于主办方准备的简单晚餐在还有很多人排队时就已经被“洗劫一空”了。

PS: 发现到会的 Geek 中 MM 还挺多的。

Ian & Rainux

Tags: , ,

gvimext.dll 是一个为 Windows 里任意文件的右键菜单 (context menu) 添加“用 Vim 编辑”菜单项的 shell 扩展,随 gVim 一起安装。

但是无论是官方的安装版本,还是我自己编译的版本,在使用 UTF-8 locale 时 (比如设置环境变量 LANG 为 zh_CN.UTF-8) 这个右键菜单项都会显示乱码。其实原因很简单,Windows 并不原生支持 UTF-8 编码,所以直接使用 TextOut() 之类的 API 输出 UTF-8 编码的消息文本肯定会显示乱码。

曾经尝试修改 gvimext.cpp,在里面覆盖环境变量 LANG 的值,去掉末尾的 .UTF-8,但这样做的结果是导致 gvimext.dll 寄生进程 explorer.exe 或 TotalCmd.exe (如果是在 Total Commander 中使用右键菜单的话) 的 LANG 也被修改,造成由 explorer 启动的 gVim 的 LANG 也成了被去掉 .UTF-8 的值 (因为子进程默认会继承父进程的环境变量)。后来又想起了 Cygwin 里的 OUTPUT_CHARSET 环境变量,可以在 gvimext 里将获取到的 UTF-8 消息文本使用 iconv() 转换为 OUTPUT_CHARSET 设定的编码,这样问题就可以圆满的解决了。

直到今天打算来实施这个想法时,又回头去看了一下以前 Google 到的这篇 The Unicode HOWTO: Locale setup,才发现原来 glibc 本来就有这样的自动编码转换能力,并且 2.2 以后的版本也不再需要设定 OUTPUT_CHARSET 了。偶太无知无畏了……

那么 gVim 安装版里的 libintl.dll 肯定不是 gcc 编译的,所以没有自动编码转换能力导致乱码,用 PEiD 看了一下果然是 MS VC 6.0 编译。接下来就简单了,去 GnuWin32LibIntl 页面下载 Binaries 和 Dependencies 两个压缩包,取得其中的 libintl3.dll 和 libiconv2.dll,前者改名为 libintl.dll (gVim 被硬编码为只尝试载入 libintl.dll 这个文件,想了想没必要改源代码,因为以后 LibIntl 出了新版本 libintl3.dll 里的 3 还会变化)。然后将这两个文件放到 PATH 环境变量中列出的任意一个目录里,最后删掉 gVim 安装目录下的 libintl.dll,OK,一切都是那么完美了。:p

Tags: , , ,

Vim 用户都应该知道 Vim 的 Normal 模式下 operator + motion 组合的便利,比如

  • daw 删除光标下的普通单词以及其后的空格
  • ciW 删除光标下的以空格分隔的“单词”并进入插入模式 (i 表示 inner,不删除单词后的空格)
  • yis 复制光标下的句子到默认寄存器

确实这几个都比较平淡无奇,不过请看以下这些

  • ci[ 删除一对 [] 中的所有字符并进入插入模式
  • ci( 删除一对 () 中的所有字符并进入插入模式
  • ci< 删除一对 <> 中的所有字符并进入插入模式
  • ci{ 删除一对 {} 中的所有字符并进入插入模式
  • cit 删除一对 HTML/XML 的标签内部的所有字符并进入插入模式
  • ci" ci' ci` 删除一对引号字符 (" 或 ' 或 `) 中所有字符并进入插入模式

怎么样?ci[ ci< cit 在编辑 PHP 或者 HTML 源码时简直太爽了 (当然 matchit 插件增强的 motion % 也是必不可少的),ci" ci' ci` 几乎在编辑任何语言的源代码时修改字符串都会非常方便。不过可惜的是以上这些严格的说只能叫文本对象选择,不是 motion,因此只能跟在 v 命令或者某个 operator 后面。可恨用了两年 Vim 居然今天才想起查 Vim 帮助里的 motion.txt。 -_-b

Tags: ,