SSH其实是一个功能非常强大的武器,我们以前只是使用了其加密登陆的最基础的功能,SSH其实可以在如下方面增强我们产品的竞争力:
1. 免密码却更为安全的SSH登陆;
2. 应用SSH隧道技术实现的无线组网方案;
3. 应用SSH压缩功能的无线省流量方案;
4. 应用SSH SOCKS5代理的降成本方案;
5. 应用SSH X11的远程桌面方案。
我们以往产品的安全性其实是很低的,体现在:
1. 允许root账号的ssh登陆;
2. Root密码被列为密码字典的一部分,可轻易破解;
3. 默认监听的22端口没有改变;
4. 没有禁止密码登陆。
这意味着,不怀好意的人通过网络扫描工具发现单板IP的22端口是打开的,然后用户设置为最易猜测的root账号,导入密码字典,一会功夫,就可以轻易入侵到我们的单板内部。
因此,建议以后的产品做如下改动:
1. 将默认的22端口禁止掉,ssh服务改为另一个端口进行监听
2. 禁止密码,采用密钥
3. 禁止root用户的ssh登陆,改用另一个不易猜测的用户名,并使该用户具有管理员的权限;
采用以上方式,正常的破解方式已经接近于不可能!
正常情况下,仅采用方法1和2,已经具有足够高的安全,保留root用户的ssh登陆,具有一定的方便性。
实施方法:
打开/etc/ssh/sshd_config
将PasswordAuthentication 改为no,可以禁止密码登陆;
将Port 22 取消注释并改为另一个端口xx
(ssh user@ip –p xx,即可实现采用xx端口登陆)
(如果需要禁止root用户的ssh登陆,将PermitRootLogin 改为 no)
在linux上,执行ssh-keygen,默认生成rsa加密的密钥对,可以将其命名为和项目有关的名字,以方便管理,举例pmsc_v10(密钥自动为pmsc_v10,公钥自动为pmsc_v10.pub)。
执行ssh-keygen
第一步,输入pmsc_v10
后面两步直接回车即可(空密码)。
然后,将公钥导入到目标单板中。
ssh-copy-id –i ~/.ssh/pmsc_v10.pub root@target_ip
那么,以后pc采用私钥pmsc_v10,即可ssh访问单板。
如果为了防止密钥被人窃取使用,在ssh-keygen时,加入密码即可,这样,获取密钥的人,也需要通过密码才能使用,这和我们平时的数字证书加密码的原理是一致的。
为了防止内部人员离职带走密钥,防止一把钥匙打开所有的单板,每个项目可以拥有自己的密钥,这样密钥丢失的影响也会降到最小。
如果你非常顽固,就是不采用密钥的方式,我还是有一个免密码的方式推荐给你,sshpass。
安装sshpass后
sshpass –p your_password ssh user@ip
即可登陆
如果不想输入密码,可以将密码设置为全局有效。
export SSHPASS=your_password
sshpass –e ssh user@ip
即可免密码登陆。
这里的无线组网,准确地说,是移动网络组网,通过SIM卡获取的网络IP,属于局域网IP。然而,单板更多时候是作为服务端存在,客户端无法主动访问,单板服务。这里就是要通过SSH隧道的方式来解决客户端无法主动访问单板服务的问题。
使用该方案的前提是,至少具备一个公网IP的电脑
假设公网IP电脑的IP为10.9.81.145;
公网电脑已经安装了ssh服务(sshd);
假设公网电脑的系统为linux,且在/etc/ssh/sshd_config中将GatewayPorts设置为yes。
客户需要访问处于移动内网中的单板的WEB界面,如何做?
第一步,在单板执行如下命令:
ssh -fNR *:8000:localhost:80 [email protected]
然后……然后就完成了。
客户在自己的电脑浏览器输入:10.9.81.145:8000
就可以访问单板的WEB了。
(如果客户就在公网电脑上操作,输入localhost:8000或127.0.0.1:8000即可)
这个要麻烦些。
因为SSH隧道是基于TCP的,所以只支持TCP协议,web使用的http协议就是基于tcp的,所以可以直接使用。
SNMP是基于UDP的,所以需要多一个工序。
公网IP电脑和单板需要新增一个socat指令。
执行步骤:
第一步,单板执行:
ssh -fNR *:6666:localhost:6666 [email protected]
socat -v tcp4-listen:6666,reuseaddr,fork udp4-sendto:localhost:161
第二步,公网电脑执行:
socat udp4-listen:1610,reuseaddr,fork tcp:localhost:6666
然后……然后就完成了。
客户在自己的电脑上对10.9.81.145的1610端口进行snmp轮询,就能获取单板的snmp信息了。
可以看到单板IP和端口被映射为一个远程端口。
开局时,每个站点的单板,必须设置一个唯一的远程端口。
1.需要增加一个参数,供开局时设置,单板的远程端口不能重复。
2.为了稳定,可以采用autossh,当网络断开(含IP改变),会自动重建ssh反向隧道。
3.由于建立ssh隧道时,需要输入公网电脑的账号密码(否则无法在公网电脑打开它的端口),因此,需要加入ssh密钥方式或sshpass自动输入密码的方式解决人工输入密码的过程。
网管访问单板,不需要再关注IP(或会改变的IP),只需要注册哪个站点的单板对应的是哪个远程端口即可,轮询这个远程端口,不管单板IP怎么变化,都能访问到。
单板主动上送改变的IP,已经没有意义。
网管可以直接部署在公网电脑上,也可以处于内网,通过访问公网电脑的方式,间接访问另一个内网的单板。
ssh隧道的性能测试,接近等价于公网电脑的端口转发性能测试。
单板处于内网,只能是单板主动发起连接,因此,单板主动发起的ssh隧道叫ssh反向隧道,原理和ssh隧道一样,只是发起方向不一样而已。
Ssh隧道本质还是通过长连接实现一个通道,但是,它的长连接可以被其他进程所用。
如下图,建立了单板80端口映射远程电脑8000端口的隧道。
远程电脑的ssh服务监听8000端口,如果有数据,会通过socket将数据通过22端口发给单板的ssh端口;单板xx端口收到数据后,立刻转发给80端口。
这样,就等价于如下的公式:
远程电脑的8000端口 == 单板的80端口
由于远程电脑IP是公网IP,因此,内网单板web就可以直接访问了。
本质就是解决ssh隧道传输udp的问题。
在远程电脑和单板增加TCP和UDP互转的工具即可,可以采用netcat或socat实现(测试中,发现socat具有更好的稳定性)。
Snmp的mib浏览器访问远程电脑的udp:1610端口,socat监听它,发现有数据,转发给tcp:6666端口。
远程的6666端口通过ssh隧道到达单板的6666端口(原理和web一样)。
单板的socat监听本地的tcp6666端口,发现有数据,转发给udp161端口。
Snmpd监听着udp161端口,数据就到达snmpd内部了。
因此,上述过程等价于:
远程电脑的udp1610端口 == 单板的udp161端口
具体比较见下表,加黑的为优势方:
SSH隧道 |
VPN |
|
实施难度 |
前后台实施都非常简单。第5代监控,全部默认具有ssh功能,无需移植。 |
前后台实施的工作量很大。复杂。 |
网络灵活性 |
针对的是进程级。意味着,A进程可以和网管通信的同时,B进程可以将数据发给家里的服务器。这是大数据分析的基础。 |
单板全局有效。单板的任何一个进程,只能处于客户的网络当中,无法发送数据到其他网络中。 |
开局难度 |
必须为每个站点设置唯一的远程端口,并在网管中注册 |
每个站点的开局工序一样。 |
网管轮询单板 |
不变的端口 |
会改变的IP |
组网适用性 |
网管和单板,均可处于内网。 建议用于组网规模不大的场合(规模大小的界定,应与实测结果结合) |
网管和单板,均可处于内网。 建议用于组网规模大的场合 |
总的说,ssh隧道的方案相比VPN实施起来更简单,建议用于监控规模的不大的市场。
前一节提到的免密码登陆,是PC具有私钥主动登陆单板。
而这里的场景为,单板具有私钥,需要主动登陆客户的PC,是相反的过程。
详细实施如下:
开发阶段:用ssh-keygen -t rsa生成密匙对,建议改掉默认名,举例这里私钥为id_rsa_esmu,公钥为id_rsa_esmu.pub。将私钥放入单板文件系统的/root/.ssh中,公钥给客户。
在客户服务器:建立一个我们单板将要登陆的用户A,将公钥放入(假设为linux系统)A的home目录下的.ssh文件夹下,将公钥导入认证钥匙中,cat ~/.ssh/id_rsa_esmu.pub >> ~/.ssh/authorized_keys。
在我们的单板:加入ssh和ssh-keyscan指令,ssh-keyscan -H 客户IP >> ~/.ssh/known_hosts, 然后ssh -i ~/.ssh/id_rsa_esmu A@客户IP 就可以自动登陆了。(免人工干预最关键的就是使用了ssh-keyscan,它可以免人工确认的生成known_hosts文件。)
当ssh处于移动网络时,容易出现断开的问题,这时可以采用autossh。它会间接调用ssh,但是加入了链路检测。
编译:将Makefile中的gcc改为交叉编译gcc,ssh的路径改为单板的ssh实际路径即可。
用法:原来ssh的语句,将开头的ssh改为autossh -M port即可。
(—M后的端口是autossh用来检测链路用,要确保port和port+1可用即可)
检测的原理:autossh会额外加入如下命令(假设-M后是6000):
ssh -L 6000:127.0.0.1:6000-R 6000:127.0.0.1:6001
很明显,对本地6000端口发检测包,最终又会回到本地的6000+1端口,这样就能知道链路是否断开,如果断开,它会自动重连。
实践中,发现偶尔会出现如下告警:
Warning: remote port forwarding failed for listen port xx
ssh默认不会发送心跳包维持长连接。 ssh需要加了一个ServerAliveInterval的参数,告诉ssh多久发送一次心跳包。
在外网环境中,中间各级的路由器维持链路的记忆一般只有几分钟,假设按5分钟计算。
另外一方面,autossh检测链路通断会由快到慢,最后慢下来后,基本维持10分钟检测一次链路通断。(也就是说,开始时,这个问题不会暴露,当autossh的检测慢下来后,这个问题才会出现。)
这时,在autossh发送下一次检测包之前,链路已经断开,这样,autossh的检测包,就得不到回复,autossh认为链路断了,就会重启ssh客户端。
注意,由于网络断,ssh服务端那块不知道ssh客户端挂了,一直还在运行。
当autossh重启ssh客户端时,会要求服务器那重启一个ssh服务,但是,以前那个还在,所以就会存在无法监听端口的问题。
当加入 ServerAliveInterval=10 后, 每10秒, ssh与sshd会产生3个包
ssh --> sshd 的加密包
ssh <-- sshd 的加密包
ssh --> sshd 的普通TCP包
这意味着,加入ServerAliveInterval这个参数,是可以保证长连接状态。
再次验证,发现出现告警的概率大幅下降,但是仍然完全杜绝掉告警的产生。
1.
建立隧道后, 杀死ssh, wireshark显示,存在网络结束的正常交互包;
这时,再次按相同端口,与sshd建立隧道,不会报任何告警。
2.
建立隧道后, 关掉ssh电脑的网络, 然后杀死ssh;
恢复网络后,再次按相同端口,与sshd建立隧道,产生和开头描述一样的告警信息;
告警信息产生后,通过wireshark检查,ssh与sshd的通信一切正常。
这证明了告警信息的产生,是不会影响实际效果的。
1. 存在一段时间的网络无法互通,可能是外网服务器的问题,也可能是csu的无线网络问题;
2. autossh发现网络断后 ,重启ssh(这时,由于网络断,ssh死亡的信息,无法被sshd知道,sshd仍然傻傻地在坚守着);
3. autossh周期重启ssh,过了一段时间,网络恢复,ssh建隧道的信息到达 sshd,这时,会新建一个sshd进程,监听的端口和原来那个不知情的老sshd进程一样。
由于老sshd霸占着端口,所以显示端口被占用的告警信息,然后新sshd进程退出。
4. ssh继续与老的sshd进程进行通信。
在无线组网方案中,省流量是非常有优势的:
1. 更少的流量,意味着可以节省更多的流量费用;
2. 更少的流量,意味着通信更快,在网络差的环境中,通信成功率更为明显。
在内容不变的情况下,采用压缩算法,是省流量的最佳办法。
由于单板使用的web服务boa不支持压缩功能,因此,需要借助其他方式来实现。
这里,采用SSH通道的方式来访问web网页,在SSH传输时,对内容加入gzip的压缩。
如果需要SSH在传输内容时支持内容的压缩,只需要加入-C参数,就可以实现。
在上面的ssh隧道中,加入压缩功能,针对web访问的情况进行了测试对比:
直接访问(KB) |
SSH隧道下的访问(KB) |
SSH隧道加压缩的访问(KB) |
测试网页 |
隧道压缩对比直接访问节省 |
备注 |
41 |
39.79 |
12.25 |
PLC |
70.12% |
单包 |
156 |
156.78 |
22.18 |
站点配置 |
85.78% |
单包 |
20.5 |
23.99 |
12.17 |
活动告警(1个) |
40.63% |
单包 |
34.3 |
56 |
24 |
活动告警(18个) |
30.03% |
多包 |
89 |
111 |
47 |
开关电源实时量 |
47.19% |
多包 |
30.5 |
32.78 |
4.01 |
历史告警(100条) |
86.85% |
单包 |
可以发现,由于web是基于html,而html又是文本类型,因此,压缩效果非常明显!!
对于SNMP,由于都是一条条命令交互,内容越少,SSH隧道附加的内容就会相对显得越多,压缩效果也没这么明显。实测,walk获取324条数据,普通轮询需要55K,SSH隧道用了95K,SSH隧道加上压缩用了58K,反而多了5%的流量消耗。
因此,SSH隧道压缩特别适合在html、xml等文本传输的协议中使用。
这项技术是在服务器维护的实践中发现的。
由于作者需要同时维护多台服务器,而公司的上网账号只能在一处登陆,因此,会出现多台服务器上网更新的网络问题。
作者采用的方式是:
在一台服务器A上登陆上网账号,由于sshd默认支持SOCKS5代理,其他服务器只需要通过如下方法,即可通过服务器A进行上网更新软件:
执行 ssh –fND localhost:xx_port server_A_user_name@server_A_ip
那么服务器通过访问本机xx_port,即可通过服务器A的sshd进行上网。
举例,firefox的高级-网络-设置中,选择手动代理,将除socks5以外的全部清空,socks5填入127.0.0.1 xx_port,那么,firefox上网时,就会通过服务器A来上网。
对于很多不支持SOCKS5的软件而言,这种方法是不可操作的,这里,可以引入polipo软件。
polipo可以强制流量全部通过SOCKS5走掉。
下载polipo压缩包,解压,make,复制polipo到/usr/bin即可使用。
创建/etc/polipo/config文件,加入三行
daemonise = true
socksParentProxy = 127.0.0.1:xx_port
socksProxyType = socks5
最后,export http_proxy=http://localhost:8123.这样,普通的程序也可以上网下载了. 其中,8123是polipo默认创建的端口.
正确情况下,单板如果需要无线网络通信,需要配置无线网卡等,每个单板都需要一套,成本较高。
如果,这些单板可以通过有线网络进行通信,如通过交换机等,那么,其中一套单板配置一个无线网卡,其他单板通过SSH的SOCKS5代理方式连接到这套具有无线网卡的单板中,则可能具有降成本的可能。
这项技术是在服务器的维护实践中发现。
以往维护远程电脑,都必须远程桌面,对应统一开发环境的服务器,对每一个用户开启远程桌面是非常麻烦的事情。
使用SSH默认支持的X11协议,开发人员可以非常非常简单的打开服务器上的应用软件显示窗口。
X11就是X windows system 版本11的意思,用于窗口的位图显示,是一个现代操作系统都基本支持的通用显示协议。
linux默认都支持X11, ssh服务也默认支持X11转发.
那么当需要打开远程电脑的图形界面时,就不需要VNC或rdp等远程桌面, 只需要在ssh登录中加入-Y, 如ssh -Y user_name@ip
登录后, 举例,执行firefox,那么就会弹出firefox的图形界面了. 这种方式,不需要全局桌面,只显示你需要的软件GUI,非常方便.
这其中的原理就是利用了ssh的转发功能, X11的客户端和服务端数据都通过ssh转发实现交互.
单板的液晶采用的就是基于位图的窗口,如果加以改造,使其满足X11的要求,那么我们将不再需要自己定制的远程桌面,通过ssh即可获取单板的液晶窗口,非常方便和通用。
每一个软件开发同学,每天都要使用SSH,用了这么多年SSH,是不是第一次发现SSH居然有这么强大的功能。
生活在于发现,有趣的事情总会出现。