以前发了一条tweet感慨,现在几乎就生活在ssh之中了。因为无论生活还是工作都离不开它,真是由衷的感谢那些设计和开发出这个伟大工具的程序员们。ssh最主要的功能就是把所有传输的数据加密,最初是的设计是为了替代传统明文传输的telnet、rlogin等程序,以避免密码嗅探攻击。而且ssh传输的数据还是经过压缩的,所以还能加快传输速度。
ssh是Secure Shell的简写,也就是说它能提供给你一个安全的shell,而保护这个安全shell的加密通道是通过端口转发(Port Forwarding)来完成的。这意味着ssh可以帮你把任意原本不安全的端口连接起来,提供给你安全的通道。安全的端口转发,这一点可以让ssh来完成很多有趣的应用。
SSH隧道代理
近期一个常见应用就是通过ssh本地端口转发数据包到国外主机,以穿透GFW访问被墙的网站。应用场景是这样的:
- 你的国内主机,比如一台笔记本
- 你拥有ssh权限的国外主机: forward.com,用户名是 user
- 被墙的网站,比如Google主机: google.com
在笔记本上运行:
ssh -L localhost:1080:google.com:80 user@forward.com
这种方式称作本地转发(Local Forwarding),意思就是把本地端口1080通过foward.com转发到了Google服务器的80端口上,这样你访问本地的1080端口就和访问Google服务器一样了。
不过这只是在传输层的端口转发,因为被GFW的网站很多,所以一个个的转发未免太麻烦了。所幸ssh还提供应用层的端口转发(a local “dynamic” application-level port forwarding),这时候ssh就扮演一个sock代理服务器角色,目前支持sock4和sock5。下边就是我们平时常用的ssh tunnel的建立办法:
ssh -D 1080 user@forward.com
这样就在你的本地架设了一个sock proxy,然后你可以通过这个proxy来访问被墙的网站了,如果不希望打开一个shell,可以加上参数-N
。
如果你穿透NAT,有些防火墙看你半天不活动,会终止你的连接。不过你可以配置你的ssh客户端,让它定时在应用层发送一个keepalive的请求,这样能够保持你的sock proxy连接。比如每隔120秒发送一次,则在 ~/.ssh/config
中写入:
Host forward.com
ServerAliveInterval 120
如果你使用Firefox,推荐安装FoxyProxy插件,可以根据规则匹配被墙的网站地址。如果使用Chrome,可以使用Switchy插件。这样没有被墙的网站,你还是可以直接连接。这也是我觉得ssh tunnel比VPN更好用的地方。
如果你使用Windows主机,那就没有OpenSSH工具包,你需要去Putty网站下载一个plink,把它放到你的PATH目录,比如 C:\\Windows\\
,然后在命令行中运行:
plink.exe -D 1080 user@foward.com
如果你讨厌Windows那个黑乎乎的cmd窗口,那么你可以另起一个进程,把这个proxy放在后台运行,写一段VB脚本,如果连接失败,plink进程会自行结束:
set ws=WScript.CreateObject("WScript.Shell")
ws.Run "plink.exe -N -D 1080 -pw password user@forward.com",0
SSH反向穿透
ssh既然可以把本地端口转发到远程端口,同样也可以把远程端口转发到本地端口(Remote Forwarding)。比如大家平时工作的时候都是在公司的NAT里,也许你是个勤劳的员工,回家之后还想在公司主机上干点活,一般公司都给大家配备一个VPN帐号,可以让你连接公司主机。但是,如果你有一台外网主机,你依然可以通过ssh端口转发来方向穿透防火墙,访问公司主机。应用场景如下:
- 你的公司主机:desktop
- 你可以访问的外网主机:myhost
- 你在家使用的笔记本: notebook
首先你需要从公司主机发起一个ssh连接:
ssh -R localhost:9922:localhost:22 myhost
这里我写了两个localhost,其实是想加深大家对端口转发的理解,因为为了安全起见,侦听端口默认都是绑定在loopback上的,所以如果只是本地访问,第一个localhost总是可以省略的。这里说明一下,第一个localhost代表的是myhost的loopback地址,第二个localhost代表的是desktop的主机地址。
所以你下班回家,只要登录到myhost上,访问9922端口,就和访问desktop的22端口的效果是一样的。
SSH加密通道
X forwarding
SSH的密钥验证
首先在客户端运行命令,
$ ssh-keygen -t rsa
生成公钥文件 ~/.ssh/id_rsa.pub
,然后复制该公钥到服务器端,保存成验证文件 ~/.ssh/authorized_keys
。
$ scp -p ~/.ssh/id_rsa.pub remote-server:~/.ssh/authorized_keys
SSH登录配置
为了省去输入用户名的麻烦,可以直接把用户名与对应主机保存在配置文件 ~/.ssh/config
中,可以使用通配符 ?
或者 *
:
ServerAliveInterval 180
Host xxx*
User yyy
Host *
Compression yes
CompressionLevel 9
KeepAlive yes
ssh远程文件系统
如果使用emacs编辑远程服务器脚本,可以使用内置的tramp,但体验不是很好,latency很大。一般可使用nfs挂载到本地再编辑。如果远程没有nfs服务,也可以使用sshfs 这个工具,这样服务器只要有ssh服务,就能够把服务器的目录mount到本地了。同时,可以使用上文提到的公钥验证办法,这样每次挂载的时候不需要输入密码。远程目录需使用绝对路径。
$ sshfs remote-host:/home/user local-directory