0、是什么
SSH(Secure Shell)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境。(更多详情可参阅* https://zh.wikipedia.org/wiki/Secure_Shell)
SSH协议基于的传输层协议为TCP,默认端口22。(与Telnet的区别,Telnet明文传输内容故不安全。默认端口FTP 21、SSH 22、Telnet 23)
大多数现代操作系统(包括macOS、大部分Linux、OpenBSD、FreeBSD、Solaris等系统)都提供了SSH协议的实现程序,但Windows系统未自带SSH程序。Windows用户可以使用Cygwin来建立SSH服务。(补注:从 Win10 1809 和 Windows Server 2019 开始 Windows 已支持 OpenSSH Server)
SSH命令参数说明:Linux下可通过 man ssh 命令查看(中文翻译版可参阅 http://linux.51yip.com/search/ssh)
1、SSH登录原理及免密登录设置
先验知识
ssh登录需要用到公钥、秘钥,它们用于对文本内容进行加密或解密。通常用RSA加密方式,其是一种对称加密,由公钥加密的内容私钥可解密、由私钥加密的内容公钥也可解密。公钥可以对外公开,私钥则不能。关于RSA加密可参阅:RSA算法原理-阮一峰
通过ssh登录远程主机通常有两种登录方式:
1、输密码登录:每次登录都要输密码。内部原理:登录时服务端发送服务端自己的公钥给客户端,客户端用该公钥加密密码并发送到服务端,服务端用对应的私钥解密,检查密码是否正确以确定是否登录成功。
第一次登录时会警告公钥是否可信任,以防止中间人攻击返回的是中间人的公钥,若用户确认信任之则公钥会保存到客户端的$HOME/.ssh/known_hosts文件里,以后再登录就不会有警告了。
2、秘钥登录(免密码登录):预先配置好秘钥,之后每次登录无需输密码。方法是在一台机器上生成公钥/私钥对并将其中一个秘钥放到另一个机器上。有两种:
2.1、公钥登录:事先将客户端的公钥放到服务端上(本质上相当于配置免密登录白名单)。内部原理:登录时服务端发送一个随机字符串给客户端,客户端用自己的私钥加密并发送到服务端,服务端根据是否有事先存储的公钥能进行解密以确定是否登录成功。
2.2、私钥登录:事先将服务端的私钥放到客户端上,每次登录时需要指定私钥,可见此时私钥相当于服务端的临时密码。
前一种方式需要对服务端进行配置,若要免密登录的客户端很多,则需要频繁修改服务端配置;后一种可以避免频繁更改服务端,但比较不安全因为私钥是不宜外泄的,这种模式只要有私钥的人就能访问服务器,但是例如阿里云服务器就是这种访问方式。
这里介绍公钥登录的配置方法(假设机器A登录机器B需要密码,但不想每次登录都需输密码):
1、A上生成密钥对: ssh-keygen ,会在当前用户目录 ~/.ssh/ 下生成 id_rsa、id_rsa.pub 两个文件,里面内容分别为私钥、公钥
2、将公钥(id_rsa.pub里的内容)追加到到B上 ~/.ssh/authorized_keys 文件中,文件不存在则新建。也可在A上通过命令完成该效果: ssh-copy-id -i ~/.ssh/id_rsa.pub <username of B>@<host of B>
3、重启B上的ssh服务: sudo service sshd restart
4、之后若还不行,可检查B上的/etc/ssh/sshd_config文件,确认如下内容没被注释:
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
参考资料:http://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html
2、SSH远程操作
可通过SSH执行远程命令而不需要登录远程主机,示例:(更多可参阅:https://www.cnblogs.com/sparkdev/p/6842805.html)
# 主要命令格式
ssh user@10.5.6.47 "ls /home; pwd " # 执行基本命令,可有多个
ssh -t user@10.5.6.47 " top " # 对于需要交互(需要tty)的命令,通过ssh远程执行时不会为此会话分配tty,故ssh立即退出远程主机从而需要交互的命令立即结束。通过 -t 参数显示声明需要为会话分配tty以解决之
ssh user@10.5.6.47 " bash -s " < test.sh helloworld # 远程执行本地主机上的脚本,参数为往脚本传的参数
ssh user@10.5.6.47 /home/user/test.sh helloworld # 远程执行远程主机上的脚本,参数为往该脚本传的参数 # 其他trick示例
cd && tar czv src | ssh user@host 'tar xz' # 将$HOME/src/目录下面的所有文件,复制到远程主机的$HOME/src/目录
ssh user@host 'tar cz src' | tar xzv # 将远程主机$HOME/src/目录下面的所有文件,复制到用户的当前目录
3、SSH端口转发
3.1、是什么
SSH是安全的,会自动加密和解密所有 SSH 客户端与服务端之间的网络数据。此外,SSH 还提供了一个非常有用的功能——端口转发:它能够将其他 TCP 端口的网络数据通过 SSH 连接来转发,并自动提供了相应的加密及解密服务。因 SSH 为其他 TCP 连接提供了一个安全的通道来进行传输,故也被称作SSH隧道(SSH Tunnel)。例如,Telnet,SMTP,LDAP 这些 TCP 应用均能够从中得益,避免了用户名,密码以及隐私信息的明文传输。而与此同时,如果工作环境中的防火墙限制了一些网络端口的使用但允许 SSH 连接,则也可通过 TCP 端口转发来使用 SSH 进行通信。
总的来说,SSH 端口转发能提供两大功能:
1、加密SSH Client与SSH Server间传输的数据
2、突破防火墙限制以间接完成一些之前无法建立的 TCP 连接
从效果上看,端口转发相当于是反向代理。
3.2、类型
SSH端口转发为无法直接任意互通但能通过SSH访问的两个网络或机器间提供了一个互通的桥梁。分为 本地端口转发、远程端口转发、动态端口转发三种。
- 本地端口转发与远程端口转发的区别在于负责执行端口转发的是所建立的SSH会话中的SSH Client还是SSH Server(SSH Client指执行SSH命令的机器,下同)。
- 前两种把对转发端口的请求转发到目的机的特定端口、而最后一种则相当于一个代理功能——设置为该代理后会把对任意端口的请求转发到目的机的相应端口。
这里设想一种场景,假设有机器A、B、C、D,其间关系为:A、B间可互通,C、D间可互通,AB与CD间不能*互通(因设置了防火墙、白名单等,只有B能通过SSH访问C、反之则不可。(想象AB、CD分别在不通的网段、两个网段间只有B能通过SSH访问到C且C只对B开放SSH的22端口)。
3.2.1、本地端口转发
命令: ssh -L <forward port>:<target host>:<target port> <SSH server addr>
- 四个参数分别表示转发的端口(注意非管理员无权绑定熟知端口0~1023)、目标host、目标主机端口、SSH server地址(如 zsm@10.5.6.47)。若target host与SSH Server是同一个机子则target host可写为 "localhost",原因见下面的原理部分
- 对于IPV4,前三个参数用冒号 ":" 分隔,对于IPv6则用斜杠 "/" 分隔。
- 为了允许其他机子连接转发端口(否则只有转发端口所在机器能连接该端口),可以加 -g 参数(listen 0.0.0.0:tarport和listen 127.0.0.1:tarport的区别);SSH连接默认会登录到SSH server(退出登录后转发功能会失效),可加 -fN 参数让命令在后台执行以避免登录到SSH server。
功能:将对SSH Client所在机器<forward port>端口的请求转发到指定机器的指定端口(即 <target host>:<target port> )
原理:SSH Client所在机器分配一个 socket 监听 <forward port> 端口,一旦此端口上有了连接,该连接就经安全通道转发出去,同时SSH server所在机器和 <target host>:<target port> 建立连接。(请求的内部走向是SSH Client -> SSH Server -> 目标机,返回时原路返回)
应用场景:使得“内网”(SSH Client所在的)能访问到不能直接访问的其他"网络"
示例:假设D上有个web服务监听8080端口,显然,A、B都无法直接访问到该服务但C可以。而B可以通过SSH访问C,此时在B上执行: ssh -g -fN -L :hostD: hostCSshAddr ,则A或B通过访问B的1000端口就能访问到D的web服务(内部走向是B -> C -> D)。
若没有-g参数,则A访问不了B的1000端口,只有B自己可以。
若web服务在SSH server上(即目标主机与SSH服务端是同一个),则命令可简为: ssh -g -fN -L :localhost: hostCSshAddr ,可见这里的localhost是相对于SSH server而言的。
3.2.2、远程端口转发
(与本地端口转发几乎一样,只不过转发端口在SSH Server且请求的内部走向相反)
命令: ssh -R <forward port>:<target host>:<target port> <SSH server addr> ,注:不支持 -g 参数
- 若target host与SSH Client是同一个机子则target host可写为 "localhost",原因见下面的原理部分
功能:将对SSH Server所在机器<forward port>端口的请求转发到指定机器的指定端口(即 <target host>:<target port> )
原理:SSH Server所在机器分配一个 socket 监听 <forward port> 端口,一旦此端口上有了连接,该连接就经安全通道转发出去,同时SSH Client所在机器和 <target host>:<target port> 建立连接。(请求的内部走向是SSH Server -> SSH Client -> 目标机,返回时原路返回)
应用场景:使得"外网"能访问到"内网"(SSH Client所在的)
示例:若上述web服务在A,显然,C、D无法直接访问该服务但B可以。而B可以通过SSH访问C,此时在B上执行: ssh -fN -R :hostA: hostCSshAddr ,则C可通过访问其1000端口就能访问到A的web服务(内部走向是C -> B -> A)。
注:远程端口转发不支持-g参数故这里无法像本地端口转发那样通过 -g 参数让D能访问到A的web服务。解决:在SSH Server(即C)的/etc/ssh/sshd_config添加一行并重启SSH服务: GatewayPorts yes
3.2.3、动态端口转发
命令: ssh -D <foward port> <SSH server addr> ,可用参数与本地端口转发的一样
功能及原理:与本地端口转发一样,将对SSH Client所在机器<forward port>端口的请求转发到SSH Server上,只不过设置时不用指定目的机器的host和port。SSH Client成为一个反向代理,应用设置代理为SSH Client后,应用发起请求时请求的内部走向也是SSH Client -> SSH Server -> 目标机,收到SSH Client转发来的请求时SSH Server会与该请求的目标机连接。SSH 动态端口转发是通过 Socks 协议实现的,创建动态端口转发时 SSH 服务器就类似一个 Socks 代理服务器,所以这种转发方式也叫 Socks 转发。
应用场景:
作为代理:把所有请求都转发到目的机,以突破防火墙等限制。
提高安全性:让那些不加密的网络连接,全部改走SSH连接,从而提高安全性。
示例: 作为反向代理——在本节首所述场景下,在B执行: ssh -g -fN -D hostCSshAddr ,相当于启动了个反向代理服务。之后在A或B上设置Sockets代理(如在浏览器设置Sockets代理),地址为 hostB:1000,则C、D上的服务均能被A或B访问到。如A访问服务hostD:8080,请求会通过 SSH Client B -> SSH Server C -> hostD:8080,由C完成对D的请求后原路返回结果。
注:
SSH 端口转发是通过 SSH 连接建立起来的,故必须保持这个 SSH 连接以使端口转发保持生效。一旦关闭了此连接,相应的端口转发也会随之关闭。
只能在建立 SSH 连接的同时创建端口转发,而不能给一个已经存在的 SSH 连接增加端口转发。
参考资料:
https://www.cnblogs.com/sparkdev/p/7497388.html ssh端口转发
http://www.ruanyifeng.com/blog/2011/12/ssh_port_forwarding.html