(译)SSH还能这样用?提高SSH效率的小技巧
前几天在找SSH相关资料时看到了这篇文章,觉得其中很多技巧还是很有用的,花了两个晚上翻译了一下。如果有什么地方没说清楚,请参考原文。 原文地址:http://blogs.perl.org/users/smylers/2011/08/ssh-productivity-tips.html 以下是翻译内容:
在连接远程服务器的时候SSH有很多特性功能,相比于只使用基本功能,这些特性能能让你显著提高生产力。如果你经常使用SSH,那么不妨花点时间来学习和配置,这会让生活更美好。
以下内容已经在里加的Yapc Europe 2011和爱丁堡的Floss UK Spring 2012 Conference上分享过了,如果你想让我也到你那里讲讲这个,请联系我。
克隆连接
通常来说,与同一个服务器建立多个连接是非常有用的,比如当你在第一个窗口里编辑文件的时候,可以在第二个的窗口里执行文件系统命令,同时在最后一个窗口里查看日志。有时候打开多个连接会特别麻烦,于是你只能在一个窗口里不停的进行命令切换。幸运的是,OpenSSH有一个特性能让你在与服务器建立连接后很容易的克隆一个新的:共享连接。OpenSSH是很多类Unix操作系统的SSH实现,包括所有常见的Linux发行版和Mac OS X。
想要打开共享会话,只需要编辑你自己的SSH配置,在~/.ssh/config
中增加如下几行:
ControlMaster auto
ControlPath /tmp/ssh_mux_%h_%p_%r
然后退出所有SSH连接,重新连接到你的服务器,然后在第二个窗口里也连接到同一台服务器。如果你已经在第一个会话(这样称呼是为了更方便的区分不同的共享连接)里输入了密码,那么在第二个连接中你应该不用输入密码就可以直接登入了。不过共享连接有个问题,就是当连接异常终止时,ControlPath
指定的文件没有被清除掉,那么下次你再次连接这台服务器时,OpenSSH会发现这个旧文件并不是当前的,只能创建一个非共享的会话,并报错:
ControlSocket /tmp/ssh_mux_dev_22_smylers already exists, disabling multipleing
在这种情况下,我看到以上错误信息时找到的唯一解决方案就是退出会话,删除文件然后重新连接,欢迎有人能够提供更好的方法。
Windows用户怎么办
这些能提高生产力的特性有时候只在OpenSSH上有效,其他SSH客户端,比如Putty,可能没法用。不过,Windodws也可以使用OpenSSH。如果你觉得这些技巧有用的话,也许值得在Windodws上试试OpenSSH(或者干脆换个操作系统得了)。
文件复制
共享连接不仅利于创建多个会话窗口,还能够让文件传输也开始如沐春风。如果你已经用SSH与远程服务器建立连接,然后使用scp
命令上传文件,那么Scp会使用已经存在的连接–在Bash里你甚至可以通过Bash补全包使用Tab键直接补全远程文件名。其他使用SSH的工具如git、rsync等也可以使用共享连接。
反复连接
如果你经常连续性的多次连接到同一台服务器(在服务器上干点活,然后退出,过一小会后再次登录),那么启用持久连接功能吧,只需要在你的配置文件里多加一行(在上述共享连接的两行之后):
ControlPersist 4h
这样当你退出会话后,连接还能保持4个小时(或者其他任何你指定的时间),当你重新连接到同一台服务器的时候能继续使用。同样,这能加快传输多个文件的速度,多个git push
或scp
命令不会每次都需要进行重复的认证过程。ControlPersist
需要OpenSSH 5.6或更新版本。
不再敲密码
目前建立SSH连接时还需要输入密码,可以通过使用SSH公钥省略这个步骤。使用SSH公钥后你仍需要输入私钥密码,但只有在重启电脑后才输入一次,并不是每次连接都输入。使用OpenSSH生成自己的私钥:
$ sh-keygen
然后根据提示进行。请使用私钥密码,这样你的私钥是加密存储的。然后你需要将公钥复制到你要登陆的服务器上,如果你的系统上有ssh-copy-id
这个命令,那么步骤就很简单了:
$ ssh-copy-id smylers@compo.example.org
如果没有这个命令,那么就需要手动复制了:
- 找到公钥文件。
ssh-keygen
的输出应该说明它在哪了,一般是~/.ssh/id_rsa.pub
。 - 将公钥文件的内容(公钥)插入到每台远程服务器的
~/.ssh/authorized_keys
文件中。 - 确保只有你对这些目录和文件具有写权限。
下面这条命令应该可以完成上述内容:
$ < ~/.ssh/id_rsa.pub ssh clegg.example.org 'mkdir -p .ssh; cat >> .ssh/authorized_keys'
然后使用SSH连接服务器、复制文件以及提交代码都不再需要使用麻烦的密码。
在Putty上使用SSH公钥
Putty也可以使用SSH公钥这种认证方式。从Putty的网站下载PuttyGen和Pageant,使用PuttyGen生成密钥,像上面一样复制公钥到服务器的~/.ssh/authorized_keys
,然后运行Pageant并导入私钥,Pageant会在后台运行。Putty能够意识到这些,然后自动使用Pageant提供的私钥而不是让你输入密码来进行连接。更多细节请参考Putty手册第8章和第9章。
不再输入服务器全名
每次都需要输入服务器全名是一件很烦的事情。一般来说,一组服务器的主机名都是某个域的子域名。比如你也许有以下服务器:
- www1.example.com
- www2.example.com
- mail.example.com
- intranet.internal.example.com
- backup.internal.example.com
- dev.internal.example.com
你的网络也许配置了能够使用短名,比如intranet
。如果没有,那么即使你没有本地网络管理权限你也应该能够自己完成这些。具体怎么做依赖于你的操作系统,下面这个方法在我最近安装的Ubuntu系统上能用,编辑/etc/dhcp/dhclient.conf
,添加一行:
prepend domain-search "internal.example.com", "example.com"
然后重启网络:
$ sudo restart network-manager
具体需要修改的文件和重启网络所需的命令看起来随着操作系统升级频繁变化,所以你可能需要做一些调整。
主机名别名
你也可以在SSH配置中定义主机名别名,尽管这需要列出每个主机名。比如:
Host dev
HostName dev.internal.example.com
你可以使用通配符来将相似主机名归为一组,在主机全名中使用%h
:
Host dev intranet backup
HostName %h.internal.example.com
Host www* mail
HostName %h.example.com
在Putty中你可以为每个主机名分别保存会话,然后双击它来启动到对应服务器的连接。(我并不认为有什么方法能在Putty上使用通配符进行主机名映射)
不再输入用户名
如果你在远程服务器上的用户名和本地的不一致,那么将以下内容添加到SSH配置:
Host www* mail
HostName %h.example.com
User simon
现在即使我的本地用户名是smylers
,我一样可以只输入:
$ ssh www2
然后SSH会连接到服务器的simon
用户。同样,Putty用户可以在会话配置中保存他们的用户名来避免每次连接都需要输入。
中继连接
有时候从一个远程服务器连接到另一个远程服务器很有用处,特别是传输文件的时候,不需要分成两个阶段先把文件下载到本地再进行上传。如:
[www1] $ scp -pr templates www2:$PWD
(注:要知道当两台服务器具有相同目录结构时$PWD是多么有用)即使你在两台服务器上都有公钥,你也依旧需要输入密码:连接是由第一个远程服务器发起,其上并不具有与第二台服务器上的公钥相对应的私钥来完成认证。不要通过将你的私钥上传到服务器上来“修复”这个问题,不应该把私钥另存一份在服务器上,而且效果也不好,还得输入一遍私钥密码。你应该使用代理转发,添加以下一行到~.ssh/config
:
ForwardAgent yes
如果用Putty则勾选Allow agent forwarding
。此时你的本地SSH代理(让你输入私钥密码进行解密的那个)就被转发到第一台服务器上然后用以与第二台服务器建立中继连接。需要注意,只有你信任中转机(www1)管理员时才使用代理转发。
弹性连接
网络瞬断导致SSH连接中断是很不爽的,可以配置OpenSSH忽略短时间的网络中断(当然也变得更久才能知道真断了)。将下面的内容加入你的SSH配置看起来还是很有好处的,具体的值按你喜好来定:
TCPKeepAlive no
ServerAliveInterval 60
ServerAliveCountMax 10
当你的网络异常的时候会话会挂起,10分钟内网络能恢复的话会话就能恢复。
断线重连
有时候你的连接会完全终止,比如你的电脑整晚挂起或者被拿到没有网络的地方,而当你重新联网的时候需要继续使用这个连接。AutoSSH能够知道这个连接什么时候失败了,然后在你需要的时候帮你重新启用,当然如果连接是你主动断开的,它也不会自讨没趣。AutoSSH就像是ssh的一个加强版,需要在SSH配置中设置ServerAliveInterval
和ServerAliveCountMax
参数,而且还要在Shell环境变量里设置(这点令人不爽):
export AUTOSSH_PORT=0
然后你就可以使用autossh
命令来替换ssh
命令来进行需要断线重连的远程连接了。如果对所有的连接你都需要这个功能,那么通过将ssh替换为autossh能让你不用每次都多打几个字符。如果你的搜索路径(PATH)里有~/bin/
这个目录的话(而且在搜索顺序中排在其他系统目录之前),那么你可以:
$ ln -s /usr/bin/autossh ~/bin/ssh
$ hash -r
现在你再输入ssh
就像输入autossh
一样了。如果你在使用一个Debian系的系统,包括Ubuntu,万一你还想用ssh的-M
选项的话,你应该使用下面这个软链:
$ ln -s /usr/lib/autossh/autossh ~/bin/ssh
AutoSSH对上面所提到的ControlPath
文件残留问题无能为力,不过当然也不会变得更糟糕。
保留远程进程
有时候你希望即使SSH连接断开了远程进程也能够继续运行,然后过段时间你还能通过另一个连接再接管这个进程,这样的话你就可以在执行一个持续很久的命令时先退出,过段时间再来检查。这也能对不稳定的网络或电源情况具备更强的适应能力,如果因为一些问题断开了的话还能在问题解决后重连上去。Screen或者Tmux的在单个窗口里开多个会话的能力能提供这个功能。如果你已经使用Screen或Tmux在一个窗口里管理多个本地会话的话,那么你显然也会这样管理远程会话。不过如果你是那种为本地的每个会话都开一个标签页或者窗口的人,那么同样,你也会为每个远程会话单独开窗口,在这种情况下你也许就能用到Dtach了,他从Screen中引入且只引入了掉线时保持进程的特性,你可以这样使用:
$ dtach -A /tmp/mutt.dtach mutt
你第一次运行这个命令的时候会启动一个新的mutt
进程。如果你的连接挂掉了(默认使用Ctrl+\
可以将该进程从Shell上拆离)那么mutt
还会保持运行,重新连接服务器并第二次执行上面的命令时,Dtach会发现进程已经在运行了然后直接切换到该进程。如果你回邮件回了一半时断开了,再次连接时你刚好能继续回复。
在服务器间跳转
有时候你无法直接连接到你想要登陆的远程服务器,只能先登陆到中转机然后跳转,这个过程也可以自动化。首先要确保你使用了公钥然后配置了代理转发(中继连接):可以使用一条命令登录到中转机,然后在中转机上再执行一条命令就登陆到服务器,而不需要输入额外的东西。如下:
$ ssh gateway
[gateway] $ ssh db
然后在本地SSH配置中指定与服务器的连接需要使用中转机进行代理:
Host db
HostName db.internal.example.com
ProxyCommand ssh gateway -W %h:%p
现在只输入ssh db
你就直接登陆到第二台服务器上了(SSH需要进行两次认证,可能稍微卡一下)。-W
选项是OpenSSH 5.4版本引入的,如果你的版本更老你可以通过Netcat来达到同样的效果。
跳出网络封锁
有时候会出现你可以访问Web服务但是SSH连接,或者说,SSH默认的22端口被禁用的问题。但是你可以将你的远程服务器的SSH服务配置成监听80或443端口来绕过这个问题,这样任何可以访问Web的网络就都能够访问这台服务器了。在服务器的/etc/ssh/sshd_config
中加入这一行:
Port 443
然后重载SSH服务:
$ sudo reload ssh
这样,在受限的网络里连接这台服务器时使用-p
选项指定端口,如:
$ ssh -p 443 sid@cafe.example.net
如果你需要那台服务器继续使用443端口做Web服务的话,你依然可以使用这个端口进行SSH连接:使用sslh就行了。sslh是个很聪明的软件,能识别出请求是Web访问还是SSH连接,并将这个请求转发到对应的服务上。你只需要在远程服务器上配置SSH使用443端口,一旦你连上这台服务器,还可以像平常一样继续使用22端口进行中继连接。记住,你需要提前完成这个设置,一旦你已经身处网络封锁之中,你就只能联系别的具有SSH权限的人帮你设置了,所以现在就配置吧。
应对Web代理
有时候一个网络不仅只提供Web访问,还要求你必须使用Web代理。幸运的是有个能够通过Web代理进行SSH连接的软件叫Corkscrew。这个软件用起来非常简单,当我用到它的时候,我只需要在网络上搜搜它、下载它然后根据它网站上的指导设置它,然后它就能用了。你需要临时增加以下配置:
ProxyCommand corkscrew proxy.example.com 8080 %h %p
在服务器上使用图形界面的程序
能在远程服务器上使用图形界面的程序来处理文件还是很有用的,比如编辑图片或者是查看PDF文档,或者仅仅是在你的编辑器不支持字符界面的时候编辑一段代码。我发现Gvim比字符界面下的Vim好用多了,而且喜欢输入gvim
命令打开一个窗口然后让字符界面能够继续执行其他命令。这个需求也可以通过SSH来实现,使用一种叫做X转发的功能,配置如下:
ForwardX11 yes
当然你的服务器也要支持才行,通过在/etc/ssh/sshd_config
中增加一行来实现(需要重启一下SSHD):
X11Forwarding yes
同时服务器上需要装有xauth
命令,以及文本编辑器、图片编辑器、图形调试工具或者其他任何你想运行的图形程序。这在任何Linux或其他具有本地X服务的操作系统上都能用。Mac及Windodws下也有相应的X服务,有人推荐我在Windodws上使用Xming来连接Linux桌面(不过我没试过)。你也许发现切换到Linux系统上也很容易。
在本地操作远程文件
除了在本地显示和操作远程图形工具以外,还有一种选择,就是通过本地图形工具来操作远程文件,这个功能可以通过SSHFS来实现。在本地创建一个空目录,然后使用sshfs挂载远程目录。指定服务器、远程目录目录以及本地空目录:
$ mkdir gallery_src
$ sshfs dev:projects/gallery/src gallery_src
$ cd gallery_src
$ ls
然后你就可以使用本地图形工具来处理这个目录的文件了。额,文件只是看起来在这个目录里,实际上是存储在远程服务器内的。如果想要卸载需要使用fusermount
命令。如果你觉得这些命令难记也别急,sshfs
手册在最开始的简介部分就包含了这个命令:
$ cd ..
$ fusermount -u gallery_src
SSHFS在Linux和OSX上可用。有些Windodws用户说Dokan SSHFS能正常工作,另一些人认为应该用ExpanDrive,我自己是没试过这两个的。
使用Vim编辑远程文件
Vim内置了编辑远程文件的功能,使用rsnc链接:
$ gvim rsync://dev/projects/gallery/src/templates/search.html.tt
如果你只是想编辑一两个远程文件的话这比使用SSHFS容易些,而且这在Windodws上也是可用的,在Vim里获取更多信息:
:help netrw-problems
使用Emacs编辑远程文件
在Emacs中,对应的功能是Tramp。(我没用过,我对Emacs的了解比对Windodws的了解还少)
使用本地程序访问远程服务
有时候远程服务器上有个服务,比如数据库或网站,只有服务器本身具有访问权限,如果让本地程序也能访问的话是很有用的。这可以通过端口转发来实现。你既可以单独指定某个端口,也可以通过使用Sshuttle来转发到特定IP的所有流量,每种实现方式都有其优缺点。
指定端口转发
如果你有一个叫db
的服务器运行了Postgres数据库服务,而且该数据库只允许服务器本机访问,那么你可以在SSH中增加以下配置,让你在自己的本地电脑上也能访问:
Host db
LocalForward 5433 localhost:5432
当你使用SSH连接服务器时会建立本地5433端口(我随便用的,没啥特别的),该端口的所有流量都会转发到服务器的5432端口上(默认的Postgres端口),此时这个服务会认为流量都来自服务器本地:
$ ssh db
然后在另一个窗口里(如果你使用了上文提到的持久连接的话你也可以立即退出由上个命令建立的会话)你可以使用本地的Postgres客户端来连接数据库:
$ psql -h localhost -p 5433 orders
如果服务器上不提供图形界面的客户端,你可以通过如下命令这样来使用本地的,非常好用:
$ pgadmin3 &
或者是你有个不能通过网络访问的后端Web服务,你也可以通过本地端口进行流量转发:
Host api
LocalForward 8080 localhost:80
然后SSH连接到服务器:
$ ssh api
然后在浏览器里使用你指定的端口来访问服务器网站:
$ firefox http://localhost:8080/
为连接转发设定主机名
如果网站配置了虚拟主机,那么仅仅将流量转发到服务器的对应端口是不足以访问页面的,你需要让你的浏览器发送正确的HTTP请求头,其中包含了Web服务所需要的域名。
你可以在/etc/hosts
中来伪造信息,将站点域名指向本机,然后你就可以在浏览器里输入网站实际域名(还得加上自己设定的本地端口)来请求,浏览器就能发出正确的请求头。
总是使用端口转发也比较乏味,你也许觉得使用Sshuttle能帮你降低工作量。
使用Sshuttle进行连接转发
Sshuttle通过使用SSH进行流量转发来实现VPN功能。比如,假设你有一台能SSH连接的服务器叫做dev
,这台服务器在一个你本机不在的内网里。你可以通过如下命令来建立一个VPN连接(需要本机的sudo权限):
$ sshuttle -DHr dev 192.168.42.0/24
192.168.42.0/24
是内网网段。现在你的电脑能够与这个网段内的其他服务器连接了,Sshuttle会通过与dev
的SSH连接来转发流量。你不需要再像上面那样为每个你需要连接的服务进行单独配置然后还要记住对应的端口号。
-H
选项告诉Sshuttle帮你完成主机名配置,自动将远程服务器知道的主机名放到本地的/etc/hosts
中。这意味这你可以认为你就在远程服务器上,直接使用主机名连接服务,比如:
$ firefox http://api/
api
会被解析成192.168.42.*
网段内的一个地址,然后转发流量到对应的服务器上。不过这个功能有个限制,就是无法使用主机全名,如果你需要使用api.example.net
来进行域名解析,那么你还得自己编辑/etc/hosts
文件(如果你知道如何自动使用全名,希望你能告诉我)。实际上我经常把远端的Apache配置成全短名都识别。
-D
选项让Sshuttle以守护进程的方式运行。这有个好处就是能够释放终端,但是同时也意味着当你需要关闭连接的时候得使用ps
命令来找到进程然后杀掉(killall没啥用,因为进程名是python,而不是sshuttle)。另外一个选择就是不使用-D
而是使用&
来把进程放到后台执行,然后就可以使用jobs
命令找到它然后用kill
%sshutl
来结束运行:
$ sudo -v && { sshuttle --syslog -Hr dev 192.168.42.0/24 & }
额,不像说的那样简单。一个后台进程是没法输入密码来获取sudo
权限的,所以只能提前完成sudo认证(在后台启动sshuttle进程之前)。使用--syslog
选项也能避免警告信息直接输出到终端干扰后续使用,这样一来这个命令就有点笨重了,但是对于经常需要连接的服务器,还是值得把命令保存在脚本里或者做成别名的。
至于具体是使用Sshuttle还是单独进行端口转发就看你的环境和个人喜好了。Sshuttle通常更方便一些,端口转发的优势是一旦完成在SSH配置内的定义,不管什么时候只要进行SSH连接就会自动设置,如果你需要在服务器上开个终端并且使用Sshuttle的VPN,那么ssh
和sshuttle
这两个命令你都得使用。Sshuttle需要本地的root权限,这个你可能没有也可能不想用。
避免延迟
如果每次连接服务器都卡几秒,那么试试把下面这行加到你的SSH配置里:
GSSAPIAuthentication no
GSSAPI是Kerberos提供的一种认证方式,如果你不知道这是什么,那么基本可以确定你也没在用这个。有些服务器被配置了先去尝试GSSAPI认证,在2秒超时之后才继续使用其他认证方式。通过告诉你的SSH客户端永远不使用这种认证方式,那么这个尝试以及对于的2秒延迟就都被跳过了。
如果这个方法确实让你的登录加快了,那么请通知服务器管理员关闭服务端的这个配置让所有用户都受益——使用和上面一样的配置,只不过是写在/etc/ssh/sshd_config
中的。
连接提速
如果你的网络带宽不怎么样,你可以通过流量压缩来提高SSH连接的速度,连接时使用-C
参数:
$ ssh -C wainwright
这将使用Gzip进行压缩,减少需要在网络上传输的数据量(但我并不总这么干:在高速网络上时,压缩和解压缩消耗的时间比它省的都多)。
如果你在连接服务器时使用的网络已经是安全的(比如是在办公区的内网),那么你可以通过使用arcfour
加密算法来提高数据传输速度:
Host dev
Ciphers arcfour
注意,这种提速是通过使用弱一点的加密算法达到的,所以SSH连接经过公网时不要使用这种方法。确认该配置只针对特定服务器,而不是默认值。在笔记本(一般会连接到各种网络上)上只有在通过VPN进行连接时才使用这个选项。
用起来吧
以上是使用SSH时能提高生产力的小技巧。如果你还有需要分享的内容,请联系Smayler@cpan.org或者@Smaylers2。(这篇文章还没有校对,所以也欢迎纠错)
现在就开始用用这些功能吧,现在花点时间把SSH配置好一点,远程连接时就容易多啦!