使⽤ssh对服务器进⾏登录
⼀、什么是SSH?
简单说,SSH是⼀种⽹络协议,⽤于计算机之间的加密登录。
如果⼀个⽤户从本地计算机,使⽤SSH协议登录另⼀台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。
最早的时候,互联⽹通信都是明⽂通信,⼀旦被截获,内容就暴露⽆疑。1995年,芬兰学者Tatu Ylonen设计了SSH协议,将登录信息全部加密,成为互联⽹安全的⼀个基本解决⽅案,迅速在全世界获得推⼴,⽬前已经成为Linux系统的标准配置。
需要指出的是,SSH只是⼀种协议,存在多种实现,既有商业实现,也有开源实现。本⽂针对的实现是,它是⾃由软件,应⽤⾮常⼴泛。此外,本⽂只讨论SSH在Linux Shell中的⽤法。如果要在Windows系统中使⽤SSH,会⽤到另⼀种软件,这需要另⽂介绍。⼆、最基本的⽤法
SSH主要⽤于远程登录。假定你要以⽤户名user,登录远程主机host,只要⼀条简单命令就可以了。
$ ssh user@host
如果本地⽤户名与远程⽤户名⼀致,登录时可以省略⽤户名。
$ ssh host
SSH的默认端⼝是22,也就是说,你的登录请求会送进远程主机的22端⼝。使⽤p参数,可以修改这个端⼝。
$ ssh -p 2222 user@host
上⾯这条命令表⽰,ssh直接连接远程主机的2222端⼝。三、中间⼈攻击
SSH之所以能够保证安全,原因在于它采⽤了公钥加密。
整个过程是这样的:(1)远程主机收到⽤户的登录请求,把⾃⼰的公钥发给⽤户。(2)⽤户使⽤这个公钥,将登录密码加密后,发送回来。(3)远程主机⽤⾃⼰的私钥,解密登录密码,如果密码正确,就同意⽤户登录。
这个过程本⾝是安全的,但是实施的时候存在⼀个风险:如果有⼈截获了登录请求,然后冒充远程主机,将伪造的公钥发给⽤户,那么⽤户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中⼼(CA)公证的,也就是说,都是⾃⼰签发的。
可以设想,如果攻击者插在⽤户与远程主机之间(⽐如在公共的wifi区域),⽤伪造的公钥,获取⽤户的登录密码。再⽤这个密码登录远程主机,那么SSH的安全机制就荡然⽆存了。这种风险就是著名的(Man-in-the-middle attack)。SSH协议是如何应对的呢?四、⼝令登录
如果你是第⼀次登录对⽅主机,系统会出现下⾯的提⽰:
$ ssh user@host
The authenticity of host 'host (12.18.429.21)' can't be established. RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d. Are you sure you want to continue connecting (yes/no)?
这段话的意思是,⽆法确认host主机的真实性,只知道它的公钥指纹,问你还想继续连接吗?
所谓\"公钥指纹\",是指公钥长度较长(这⾥采⽤RSA算法,长达1024位),很难⽐对,所以对其进⾏MD5计算,将它变成⼀个128位的指纹。上例中是98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d,再进⾏⽐较,就容易多了。
很⾃然的⼀个问题就是,⽤户怎么知道远程主机的公钥指纹应该是多少?回答是没有好办法,远程主机必须在⾃⼰的⽹站上贴出公钥指纹,以便⽤户⾃⾏核对。
假定经过风险衡量以后,⽤户决定接受这个远程主机的公钥。
Are you sure you want to continue connecting (yes/no)? yes系统会出现⼀句提⽰,表⽰host主机已经得到认可。
Warning: Permanently added 'host,12.18.429.21' (RSA) to the list of known hosts.
然后,会要求输⼊密码。
Password: (enter password)如果密码正确,就可以登录了。
当远程主机的公钥被接受以后,它就会被保存在⽂件$HOME/.ssh/known_hosts之中。下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从⽽跳过警告部分,直接提⽰输⼊密码。
每个SSH⽤户都有⾃⼰的known_hosts⽂件,此外系统也有⼀个这样的⽂件,通常是/etc/ssh/ssh_known_hosts,保存⼀些对所有⽤户都可信赖的远程主机的公钥。五、公钥登录
使⽤密码登录,每次都必须输⼊密码,⾮常⿇烦。好在SSH还提供了公钥登录,可以省去输⼊密码的步骤。
所谓\"公钥登录\",原理很简单,就是⽤户将⾃⼰的公钥储存在远程主机上。登录的时候,远程主机会向⽤户发送⼀段随机字符串,⽤户⽤⾃⼰的私钥加密后,再发回来。远程主机⽤事先储存的公钥进⾏解密,如果成功,就证明⽤户是可信的,直接允许登录shell,不再要求密码。这种⽅法要求⽤户必须提供⾃⼰的公钥。如果没有现成的,可以直接⽤ssh-keygen⽣成⼀个:
$ ssh-keygen
运⾏上⾯的命令以后,系统会出现⼀系列提⽰,可以⼀路回车。其中有⼀个问题是,要不要对私钥设置⼝令(passphrase),如果担⼼私钥的安全,这⾥可以设置⼀个。
运⾏结束以后,在$HOME/.ssh/⽬录下,会新⽣成两个⽂件:id_rsa.pub和id_rsa。前者是你的公钥,后者是你的私钥。这时再输⼊下⾯的命令,将公钥传送到远程主机host上⾯:
$ ssh-copy-id user@host
好了,从此你再登录,就不需要输⼊密码了。
如果还是不⾏,就打开远程主机的/etc/ssh/sshd_config这个⽂件,检查下⾯⼏⾏前⾯\"#\"注释是否取掉。
RSAAuthentication yes PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys然后,重启远程主机的ssh服务。 // ubuntu系统
service ssh restart
// debian系统
/etc/init.d/ssh restart
如果想使⽤特定的⽤户名邮箱来⽣成密钥,可以使⽤如下的⽅法:
$ ssh-keygen -t rsa -C “user@example.com”
ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa | rsa1] [-N new_passphrase] [-C comment] [-f output_keyfile]其余⽅式跟如上的⽅式⼀样。六、authorized_keys⽂件
远程主机将⽤户的公钥,保存在登录后的⽤户主⽬录的$HOME/.ssh/authorized_keys⽂件中。公钥就是⼀段字符串,只要把它追加在authorized_keys⽂件的末尾就⾏了。
这⾥不使⽤上⾯的ssh-copy-id命令,改⽤下⾯的命令,解释公钥的保存过程:
$ ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub
这条命令由多个语句组成,依次分解开来看:(1)\"$ ssh user@host\",表⽰登录远程主机;(2)单引号中的mkdir .ssh && cat >>
.ssh/authorized_keys,表⽰登录后在远程shell上执⾏的命令:(3)\"$ mkdir -p .ssh\"的作⽤是,如果⽤户主⽬录中的.ssh⽬录不存在,就创建⼀个;(4)'cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub的作⽤是,将本地的公钥⽂件~/.ssh/id_rsa.pub,重定向追加到远程⽂件authorized_keys的末尾。
写⼊authorized_keys⽂件后,公钥登录的设置就完成了。
七、其他⾼效使⽤⽅法
1. 连接中转
有时候你可能需要从⼀个服务器连接另外⼀个服务器,⽐如在两个服务器之间直接传输数据,⽽不⽤通过本地电脑中转:
www1 $ scp -pr templates www2:$PWD
(顺便说⼀下,当你需要在两台服务器间拷贝⽂件时,$PWD变量时⾮常有⽤的),因为即使你已经在两台服务器上添加了你本地电脑的公钥,scp默认仍然会提⽰你输⼊密码:这是因为你⽤来作为跳板的那 台服务器上并没有你的私钥,所以,第⼆台服务器会拒绝你的公钥,但是⼀定不要通过将你的私钥拷贝到中转服务器上来解决这个问题,你可以使⽤agent forwarding来解决这个问题,只要在你的.ssh/config⽂件中加⼊下⾯这⾏代码就可以了 ForwardAgent yes 2. 省略主机名
输⼊服务器的完整主机名来建⽴⼀个新的SSH连接实在是太乏味⽆聊了,尤其是当你有⼀组拥有相同域名但是⼦域名不同的服务器需要管理时,⽐如下⾯这样:* * * * * *
或许你的⽹络已经配置了可以直接使⽤短域名,⽐如intranet,但是如果你的⽹络不⽀持,实际上你可以⾃⼰搞定这个问题,⽽不⽤求助⽹络管理员。
解决办法根据你⽤的操作系统⽽略有差异,下⾯是我的Ubuntu系统的配置:prepend domain-search “internal.example.com”, “example.com”;然后你需要重启⽹络:$ sudo restart network-manager
不同的系统,这两条命令可能会略有差异。
3. 主机别名
你也可以在你的SSH配置中直接定义主机别名,就像下⾯这样:Host devHostName
你还可以使⽤通配符来进⾏分组:Host dev intranet backupHostName %h.Host www* mailHostName %h.
在Putty中你可以为每个主机名保存单独的session,然后双击建⽴连接(但是它可能没办法⽀持通配符)。
4. 省去⽤户名
如果你在远程服务器上的⽤户名和你本地的⽤户名不同,你同样可以在SSH配置中进⾏设置:Host www* mailHostName %h.User simon
现在就算我的本地⽤户名是 smylers,我仍然可以这样连接我的服务器:$ ssh www2
SSH会使⽤simon账户连接你的服务器,同样,Putty可以保存这个信息在你的session中。
5. 在服务器间跳转
有些时候,你可能没法直接连接到某台服务器,⽽需要使⽤⼀台中间服务器进⾏中转,这个过程也可以⾃动化。⾸先确保你已经为服务器配置了公钥访问,并开启了agent forwarding,现在你就可以通过2条命令来连接⽬标服务器,不会有任何提⽰输⼊:$ ssh gatewaygateway $ ssh db
然后在你的本地SSH配置中,添加下⾯这条配置:Host dbHostName
ProxyCommand ssh gateway netcat -q 600 %h %p现在你就可以通过⼀条命令来直接连接⽬标服务器了:$ ssh db
这⾥你可能会需要等待长⼀点的时间,因为SSH需要进⾏两次认证,,注意netcat也有可能被写成nc或者ncat或者前⾯还需要加上g,你需要检查你的中间服务器来确定实际的参数。
6. 突破⽹络封锁
有些时候,你使⽤的⽹络可能只开放了80端⼝,或者它们封锁了SSH端⼝(默认的22端⼝),这种情况下,你可以通过配置SSH服务器在80或者443端⼝进⾏监听来,只需要编辑你的服务器的/etc/ssh/sshd_config⽂件:Port 443
然后重启SSH服务器:$ sudo reload ssh
当然这样做的前提是你的服务器没有使⽤HTTS服务,但是实际上你只需要设置⼀台服务器使⽤https端⼝就够了,你但你可以访问这台服务器,你就可以使⽤我们前⾯提到的技术利⽤它作为跳板来访问其它服务器,但是记住,你需要提前配置好这台服务器(现在怎么样?),这样万⼀当你⾝处⼀个只能访问Web的⽹络环境时,就可以省掉打电话让其他⼈帮你配置中间服务器的⿇烦了。
7. 穿越Web代理
有些时候,你所在的⽹络不⽌封锁SSH端⼝,它们有可能更进⼀步,只让你通过Web代理来访问⽹络,幸运的是我们有⼀个叫做的程序可以通过Web代理在发送SSH数据。Corkscrew的使⽤⾮常简单,⼀般我都是在需要时搜索,然后直接下载,跟随⽹站上的指⽰,然后就搞定了,⼀般你需要这样⼀条配置:
ProxyCommand corkscrew 8080 %h %p
8. 本地操作远程⽂件
让远程GUI程序显⽰在本地的替代⽅案就是让本地的GUI程序可以直接操作远程⽂件,你可以通过SSHFS来实现,只需要创建⼀个空⽬录,然后使⽤SSHFS将⼀个远程⽬录mount到这个⽬录就可以了:$ mkdir gallery_src
$ sshfs dev:projects/gallery/src gallery_src$ cd gallery_src$ ls
现在你就可以使⽤任何你喜欢的本地程序来便捷这个⽬录中的⽂件了,它们看起来是在你的本地,但其实时远程服务器上的⽂件,你可以使⽤fusermount命令来unmount这些⽂件,不要担⼼记不住,它们就在sshfs⼿册的顶上:$ cd ..
$ fusermount -u gallery_src
SSHFS可以在Linux和OSX上⼯作,Windows⽤户我⽬前还没找到什么好办法。
9. 通过Vim访问远程⽂件
Vim有⼀个内置的功能可以直接编辑远程⽂件,需要借助SCP URL:$ gvim scp://dev/projects/gallery/src/templates/search.
这中⽅式明显不如SSHFS灵活,但是如果你只需要对远程服务器的1,2个⽂件进⾏编辑时,这条命令就要更灵活⼀些了,并且可以在
Windows上你也可以这样做:
:help netrw-problems
10. 使⽤本地App连接远程服务器
有时可能有些服务,⽐如数据库或是Web服务器,它们运⾏在远程服务器上,但是如果有⽤⽅式可以直接从本地程序连接它们,那会⾮常有⽤,要做到这⼀点,你需要⽤到端⼝转发(port forwarding),举个例⼦,如果你的服务器运⾏Postgres(并且只允许本地访问),那么你就可以在你的SSH配置中加⼊:Host db
LocalForward 5433 localhost:5432
现在当你连接你的SSH服务器时,它会在你本地电脑打开⼀个5433端⼝(我随便挑的),并将所有发送到这个端⼝的数据转发到服务器的5432端⼝(Postgres的默认端⼝),然后,只要你和服务器建⽴了连接,你就可以通过5433端⼝来访问服务器的Postgres了。$ ssh db
现在打开另外⼀个窗⼝,你就可以通过下⾯这条命令在本地连接你的Postgres数据库了:$ psql -h localhost -p 5443 orders
如果你想要使⽤服务器不⽀持的图形化Postgres客户端时,这条命令会显得尤其有⽤:$ pgadmin3 &
或者你有⼀个后台的Web服务器,你不希望直接通过Internet访问它,你也可以通过端⼝转发来访问它:Host api
LocalForward 8080 localhost:80现在连接到服务器:$ ssh api
然后将浏览器指向你选择的端⼝号:
$ firefox