xshell确实好用,但是不便于辗转流连于多个电脑之间,虽然用onedrive解决了多台电脑之间的秘钥文件的存储问题,但是令人讨厌的秘钥密码却总是如幽灵般的闪现,偶尔记不住某个服务器的密码就要翻箱倒柜地寻找密码薄。因此,一直有一个想法,就是搞一套webssh,像登录网站那样登录ssh,这样不论在哪台电脑上工作,只要有一个浏览器,就可以随时登录服务器,岂不快哉。
原理
首先要了解我们使用webssh的工作原理,传统单工的http协议并不能满足我们的要求,我们要求实时双工通信,因此前端的协议就是websocket,前端处理终端字符有一个非常著名的库xterm.js, 最近非常流行的vscode的terminal就是使用xterm.js编写的。后端websocket服务采用tornado, ssh通信及认证等工作交给python的paramiko库。
原理图大概如下这个样子:
xterm.js的使用
了解了原理,接下来我们来实现它。首先我们先看一下xterm.js这个工具的使用。
xterm.js是一个非常强大的库,可以帮我们简化很多的工作和麻烦,有了xterm.js,我们不需要考虑terminal下的各种字符的问题。
xterm.js的官网。
我们使用的代码比较简单,只需要引入xterm.js,然后创建一个websocket即可。
1 | <head> |
xterm.js自带了两个插件,一个fit,另一个fullscreen,使用fullscreen的时候需要注意,不要忘了引用css文件。详细使用说明请参考官网说明文档。
tornado 中使用websocket
tornado是一个强大的web应用框架,其最受关注的功能是对异步的支持,它使得处理非阻塞请求更容易,最终导致更高效的处理以及更好的可扩展性,同时也支持websocket. 所以,我们这里选用的websocket实现就是tornado。
1 |
|
这里不仅使用了websocket,还是用flask部分代码,是因为我的项目中http请求是用flask来实现的。
处理secure websocket
webssh这么敏感的业务肯定是要跑在secure websocket上的,tornado添加证书和秘钥也非常简单:
1 | httpserver = HTTPServer(application,ssl_options={ |
这样就构建好了服务端的websocket。
paramiko
xterm.js官方使用的是termindo,一个由tornado编写而来的演示框架,但是它只支持本机的ssh,并不能满足我们希望连接其他主机的跳板需求。
这里是用的解决方案是python的paramiko库,paramiko提供很多接口供我们使用,能让我们轻松地完成ssh连接操作。
通常我们远程登录主机使用的验证方式有两种,一种是用户名和密码认证,另一种是私钥公钥认证,有时候私钥还会有密码。这些在xshell等工具中都有配置,我们现在就用paramiko来实现同样的功能。
1 |
|
核心代码不多,主要的方法就是connect,在其中要将用户名和密码、端口等信息都配置好。如果有私钥,私钥又有密码,就使用from_private_key_file方法把私钥文件名和密码传入。invoke_shell方法返回一个channel,拿到这个channal就可以像xshell一样打开一个terminal一样来操作了。
跳坑指南
Nignx部署400错误
本地测试完,部署服务器的时候碰到nginx返回400错误。原因是忘了在nginx配置中将协议升级为websocket:
1 | proxy_http_version 1.1; |
RuntimeError: There is no current event loop in thread ‘Thread-1’.
也是在部署服务器的时候出现的,原因是本地tornado版本是4.5.3,而服务器版本变成了5.1. 在5.1版本中使用多线程,就会出现上述错误。解决方案就是使用asyncio的set_event_loop方法:
1 | import asyncio |
Websocket报403错误
也是因为服务器版本5.1的问题,解决方案,在websocket的handler中添加一个check_origin方法:
1 | def check_origin(self, origin): |
运行界面:
点击终端进入webssh界面: