ssh的使用

正式写之前先说几句题外话,这个博客也开了一段时间了,但是基本上很少更新。现在感觉把自己的技术总结,经验等写下来确实很有必要,因为人的记忆力是有限的,久了没接触肯定会遗忘。以前的自己太懒了,总是找各种借口推诿写博客。今年开始我决定坚持写博客,就算每篇博客质量不佳,篇幅不长也没关系,只要坚持下去对自己也是有帮助的。另外下半年就要开始找工作了,也正好可以总结一下自己的学习成果,以应对以后的面试和笔试。

昨天看到阮一峰先生两篇介绍ssh的文章:远程登录远程操作与端口转发,感觉很有意思。虽然我ssh一直在用,但也仅限于用其远程登录Linux。看了这两篇文章后我意识到原来ssh还有这么好玩的地方,我又孤陋寡闻了。于是在网上找了些ssh的资料大致研究了下,现在写这篇文章总结一下,以后看到新的东西也会持续修改这篇博文。本文只讨论ssh的常见用法,而不讨论其具体实现。

什么是ssh

ssh是Secure Shell的缩写,它是一个网络协议。ssh主要用来进行远程登录,数据传输,还有其他一些网络服务,同时这些操作都是建立在安全的信道之上的。ssh是通过公钥体系来保证安全性的,后面远程登录会讲到。需要注意的是ssh只是一种网络协议,它有不同的实现,其中openssh是其最广泛的实现,我们所使用的Linux上的ssh用的就是openssh。

远程登录

ssh最常用的功能就是远程登录计算机进行各种操作,因为在ssh远程登录计算机进行操作的整个过程中网络中传输的数据都是加密的,所以用ssh登录有相当的安全性。ssh远程登录主要有两种方式:通过密码登录,通过公钥登录。

密码登录

第一种方式是一种比较直观的方式,也是最常用的方式。它和我们一般的登录方式是一样的,即输入用户名及其密码进行登录。

如下shell命令:

ssh user@hostname

我们将登录的主机ip地址为hostname,使用该主机的账户user进行登录。接着会提示输入该用户的密码,输入正确后登录成功,就可以进行各种操作了。

但这种登录方式有一个缺点,它容易遭受中间人攻击。因为这种登录方式验证用户的方法是这样的:需要登录的一方将登录请求发给远程主机,远程主机收到登录请求后将自己的公钥发回来,在用户输入完密码后用传回的公钥加密这个密码,然后再将加密后的密码传给远程主机,远程主机用自己的私钥解密密码并进行验证,如果密码正确则说明该用户是合法的,允许登录。这里有一个漏洞,即攻击者可以冒充远程主机截获用户的登录请求,然后将自己伪造的公钥发给用户,以骗取用户的密码。在这种情况下用户是无法辨别此次登录的主机就是真正的主机。

为了避免这种情况,ssh在我们第一次登录一个主机时会有提醒,如下所示:

The authenticity of host '192.168.13.233 (192.168.13.233)' can't be established.
RSA key fingerprint is a9:97:4d:56:2f:77:f9:b7:1c:a9:d9:b9:a4:21:37:67.
Are you sure you want to continue connecting (yes/no)?

意思是ssh无法验证该远程主机,该主机公钥的指纹是a9:97:4d:56:2f:77:f9:b7:1c:a9:d9:b9:a4:21:37:67,这需要用户自己验证这个主机公钥的指纹是否真的是这个,如联系该主机管理员。如果用户确认无误,该远程主机的公钥就会保存在~/.ssh/known_hosts文件下,以后登录就可以直接判断该远程主机是合法的。

公钥登录

另一种登录方式可以不需要用户输入密码即可登录,而且比起密码登录更加安全。但是在用户登录远程主机之前需要一些额外的步骤。用户需要事先生成自己的公钥私钥对,通过某种手段将其生成的公钥放到远程主机上。该登录方式用户验证步骤如下:在用户登录远程主机的时候,远程主机获取已经存在本地的用户公钥,验证其合法性,如果合法就用该公钥加密一段随机字符串,然后将加密的内容传回用户,用户用自己对应的私钥进行解密,将解密之后的内容连同本次会话标示进行MD5,将该MD5值发给远程主机,远程主机收到后再进行同样的MD5比较它们是否一致,如果一致则说明目前用户是合法的。

可见这种登录方式稍微复杂一点。我们用如下命令生成密钥对:

ssh-keygen -t rsa

这里会提示输入密码(passphrase),该密码会用来加密私钥,也可以直接回车不输入密码。成功之后会生成rsa的公钥和私钥对,公钥放在~/.ssh/id_rsa.pub文件中,私钥放在~/.ssh/id_rsa文件中。

而后要通过某种方法(如U盘拷贝,网络传输等)将id_rsa.pub中的内容追加到远程主机上相应用户~/.ssh/authorized_keys文件中,至此准备工作完成,用户登录的时候只需要输入下面的命令即可:

ssh hostname

如果没有加密私钥则直接登录成功,否则需要输入加密私钥的密码。

端口转发

ssh另一大用处就是端口转发,这是我以前不了解的。所谓端口转发就是将本来发往某个目的地的数据包转发到其他的地址和端口上。其实这个是很常见的应用,包括外网要访问局域网内部的机器都要用到端口转发,只是我以前没注意到ssh也可以干这事。端口转发分为本地端口转发和远程端口转发两种,其实两者大同小异,只是ssh建立连接的方向不一样。为了以后方便讨论,现假设有三台主机A,B,C,主机名分别为hosta,hostb,hostc。

本地端口转发

本地转发中的本地是指将本地的某个端口转发到其他主机的某个端口,这样当我们的程序连接本地的这个端口时,其实间接连上了其他主机的某个端口,当我们发数据包到这个端口时数据包就自动转发到了那个远程端口上。本地端口转发的ssh命令格式如下:

ssh -L [bind_address:]port:host:hostport hostname

Linux manual解释如下:

Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side. This works by allocating a socket to listen to port on the local side, optionally bound to the specified bind_address. Whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to host port hostport from the remote machine. Port forwardings can also be specified in the configuration file. IPv6 addresses can be specified with an alternative syntax:[bind_address/]port/host/hostport or by enclosing the address in square brackets. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the GatewayPorts setting. However, an explicit bind_address may be used to bind the connection to a specific address. The bind_address of “localhost” indicates that the listening port be bound for local use only, while an empty address or‘*’indicates that the port should be available from all interfaces.

可知运行上面命令后ssh会在本地主机监听端口port,如果有一个指向该端口的连接就将其转发的主机host的hostport端口上,hostname是用于转发的中间主机,它可以和host一样也可以不一样。事实上,运行这条命令之后,本地主机的ssh客户端会连接上hostname主机上的ssh服务器端,通过这条链路将到本地端口的连接转发的host主机的hostport端口上。所以本地主机到hostname主机的通信是安全的,因为这两者是通过ssh连接的,然而从hostname到host的通信是不安全的,这点需要注意。另外本地端口绑定的地址是可选的,即可以指定该端口接收连接来自的网络接口,如果不指定则根据配置的GatewayPorts选项确定:如果该选项为yes则其他主机上程序可以连上该本地端口,否则只能使用回环接口,即只有本主机上的程序能连上该端口。如果指定了绑定地址则无视GatewayPorts选项。
例如我现在想将主机A的1132端口通过主机B转发到主机C的21端口(ftp默认端口)上。要用本地端口转发在主机A上运行如下命令:

ssh -L 1132:hostc:21 hostb

这条命令之后所有发向主机A上1132端口的连接好像都变成了与主机C上21端口的连接。

同样地,我可以不通过中间主机B,而用ssh直接连上主机C进行端口转发,在主机A上运行如下命令:

ssh -L 1132:hostc:21 hostc

这条命令和上一条效果是一样的,都是将端口转发到主机C,区别在ssh连接的是哪个主机。之所以第一条命令要先连上主机B作为中转是因为在有些情况下主机A和主机C无法连通,而主机A和主机B可以连通,同时主机B可以和主机C相连,这时就可用ssh先连上主机B再通过其转发端口,这样我们就可以在无法连上主机C的情况下也可以使用主机C的ftp服务。如下图所示,这种情况下主机A和主机B之间就形成了一条ssh隧道,在这条隧道中数据传输时加密的,所以不用担心传输内容被诸如防火墙之类的屏蔽。因此本地端口转发的一大用处就是在局域网内部通过建立一条到外网主机的ssh隧道,以此来访问被局域网网关屏蔽的外部服务。

cc1

远程端口转发

与本地端口转发相对的是远程端口转发。与本地转发不同,它指定的是远程主机的一个端口,将指向该端口的连接转发到本地端(不一定是本机)。可知远程转发和本地转发本质上是一样的,主要区别在于需要转发的端口是在远程主机上还是在本地主机上。远程端口转发ssh命令格式如下:

ssh -R [bind_address:]port:host:hostport hostname

Linux manual解释如下:

Specifies that the given port on the remote (server) host is to be forwarded to the given host and port on the local side. This works by allocating a socket to listen to port on the remote side, and whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to host port hostport from the local machine.

Port forwardings can also be specified in the configuration file. Privileged ports can be forwarded only when logging in as root on the remote machine. IPv6 addresses can be specified by enclosing the address in square braces or using an alternative syntax:[bind_address/]host/port/hostport.

By default, the listening socket on the server will be bound to the loopback interface only. This may be overriden by specifying a bind_address. An empty bind_address, or the address‘*’, indicates that the remote socket should listen on all interfaces. Specifying a remote bind_address will only succeed if the server’s GatewayPorts option is enabled (see sshd_config(5)).

在这里要转发的端口port是在远程主机hostname上,将指向该端口的连接转发到本地端的主机host的hostport端口上,此时ssh会在远程主机hostname的port端口上监听到来的连接。同本地转发一样,这里也可以指定远程转发端口绑定的地址,但与本地转发不同的是只有在远程主机配置中将GatewayPorts 选项设为yes绑定的地址才有效,否则就算设置了绑定地址该端口也只会接收来自回环接口的连接。

比如我现在在主机A上想要将主机B的1132端口转发到本机的22端口(ssh默认端口)上,则在主机A上运行ssh命令如下:

ssh -R 1132:127.0.0.1:22 hostb

该命令执行之后主机A的ssh客户端向主机B的ssh服务器端建立一条连接,而后所有指向主机B上1132端口的连接都会转发到主机A的22端口,如下图所示。我现在可以在主机B上运行如下命令:

ssh -p 1132 root@127.0.0.1

这样就相当于我远程登录了主机A,所以远程端口转发可以用来从外网访问局域网内部的服务。

cc2

ssh还有两个选项与上诉端口转发命令一起使用。

-N选项:指示当前ssh连接上服务器端之后不执行远程命令。

-f选项:指示当前ssh命令在后台执行。

SOCKS代理

上面介绍的端口转发都是两个特定的端口间的转发,如果我们想用同一个本地端口访问不同的外部服务怎么办?ssh对于这种情况也提供了解决方案,那就是将ssh作为SOCKS代理服务。SOCKS也是一种网络协议,它用于客户端和服务器之前数据传输的代理服务。ssh使用SOCKS服务的命令格式如下:

ssh  –D  [bind_address:]port  hostname

Linux  manual解释如下:

Specifies a local “dynamic” application-level port forwarding. This works by allocating a socket to listen to port on the local side, optionally bound to the specified bind_address.  Whenever a connection is made to this port, the connection is forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine.  Currently the SOCKS4 and SOCKS5 protocols are supported, and ssh will act as a SOCKS server.  Only root can forward privileged ports. Dynamic port forwardings can also be specified in the configuration file.

IPv6 addresses can be specified with an alternative syntax:[bind_address/]port or by enclosing the address in square brackets.  Only the superuser can forward privileged ports.  By default, the local port is bound in accordance with the GatewayPorts setting.  However, an explicit bind_address may be used to bind the connection to a specific address. The bind_address of “localhost” indicates that the listening port be bound for local use only, while an empty address or‘*’indicates that the port should be available from all interfaces.

通过以上描述可知该命令相当于动态端口转发,将指向本地端口的连接转发到远程主机的端口上,而具体转发到哪里,是由上层的应用层协议决定的,目前ssh支持SOCKS4和SOCKS5两种协议。执行该命令后本机与hostname主机建立一条ssh连接用于中转SOCKS请求,这样所有启用SOCKS支持的本地程序都可以连接port端口,其连接将通过hostname主机转发到各自的目的地。同本地端口转发一样,本地用于转发的端口也可以绑定地址,如果不绑定则视GatewayPorts选项来决定是否接受其他主机的连接。

由于从本地到ssh服务器之间的数据传输是加密的,而且由于我国特殊的网络情况,所以ssh的这个功能常被用来突破网络封锁。例如如果我们有一个国外的ssh账号,就可以通过这种方法连接上国外的主机,然后启用浏览器的SOCKS代理使其指向本地的转发端口,由于本地和国外的这个主机传输内容是通过ssh加密的,这样就能躲过网络审查以浏览被屏蔽的内容。在Windows上的MyEntunnel等软件就是利用此原理穿墙的。

 

参考文献

1. SSH原理与运用(一):远程登录

2. SSH原理与运用(二):远程操作与端口转发

3. ssh公钥方式认证攻略

4. 实战 SSH 端口转发

5. SSH隧道技术简介

posted @ 2012-01-31 19:34  silverwings  阅读(950)  评论(0编辑  收藏  举报