首页 > 解决方案 > 从客户端浏览器通过 SSH 连接到服务器,无需中间人服务器

问题描述

没有要查看的特定代码——只是希望能解决我面临的问题,列出我知道的选项,看看是否有任何我遗漏的选项。请让我知道这是否不合适,我将删除该问题。

问题

我正在开发一个基于 Web 的应用程序,该应用程序具有在用户拥有的服务器上运行命令的方法,最好是通过 SSH。我需要实现一种解决方案,以尽可能安全和方便(对用户)的方式从浏览器执行 SSH 命令。我正在详细介绍我提出的一些解决方案,但这些解决方案并不能完全消除芥末,希望大家可能有一些我没有想到的想法,或者我可能不知道的工具。

我目前的解决方案

目前,我有一个有效的 API,可以将 HTTPS 帖子转换为 SSH 命令,主要是 SFTP。应用程序将应用程序服务器上的 SSH 实例连接到用户拥有的服务器,给定用户名和密码或上传的私钥。然后,服务器维护 SSH 客户端,必要时使用用户提供的凭据重新连接。应用程序不存储用户名、密码或私钥,客户端和应用程序服务器之间的信息通过 HTTPS 进行;但是,身份验证信息保存在 redis 内存会话实例中。因为该信息必须通过我的中间人应用程序服务器,从而引起对客户端服务器安全性的潜在担忧,所以我想找到另一种解决方案。

我的理想解决方案

理想情况下,我会用 Go 之类的语言编写一个 SSH 客户端,转换为 WebAssembly,可以下载并在浏览器中运行。没有对我的应用服务器的调用——所有 SSH 流量,包括身份验证,都直接从客户端到他们的服务器。WebAssembly 将被浏览器缓存(尽管无论如何都很轻量级),防止下载大小过大,同时仍然允许我根据需要进行更新。但是,这不起作用,因为浏览器是沙盒的,无法与远程服务器建立连接。我研究了像Browsix这样的解决方案,在浏览器中运行一个轻量级的 Unix 系统,它又可以运行 SSH 应用程序,但它似乎同样限于直接运行 WebAssembly。(Browsix 好像也没有维护???)

我提出了两种潜在的替代解决方案,它们都有一些缺点,使我无法承诺:

解决方案 1

移动 SSH 客户端客户端。我的理想解决方案是这种形式,但为了绕过浏览器限制,我需要客户端下载一个可以从客户端 Web 应用程序调用的小型 SSH 客户端。实际上,如果我支持不运行 Chromium 的浏览器,并且如果我避免为我选择支持的每个浏览器维护浏览器扩展,这意味着我无法在浏览器和本机应用程序之间进行交互。为了解决这个问题,我认为我可以将 SSH 客户端包装在一个旨在在特定端口上运行的 Web 服务器中,比如:9090。然后,Web 应用程序可以调用 localhost:9090/listDirectories,这会将请求正文汇集到 SSH 客户端,后者将在客户端拥有的服务器上执行相关调用。唯一的缺点是它需要用户下载并运行安装程序,并且程序必须不断地运行并监听客户端计算机上的端口。这不像我想要的那样无缝的用户体验,并且在机器启动时容易出现程序终止或无法正确启动等错误。我还必须维护多个主要操作系统的安装程序。

解决方案 2

抛弃 SSH。创建一个小型网络服务器(可能在 Go 中)以侦听客户端拥有的服务器上的端口。然后,客户端 Web 应用程序可以直接对客户端拥有的服务器进行 API 调用,通过 HTTPS 加密,并且在客户端拥有的服务器上侦听的网络服务器将处理请求。这样做的缺点:我需要创建一个用户管理系统(而不是依赖于客户端拥有的服务器的身份验证系统)并实施某种方法来确定客户端拥有的服务器实际上是由客户端拥有的。

大家可能提出的问题

你可能会问,“为什么不直接制作一个原生应用程序?如果你喜欢制作 Web 应用程序,你可以使用 Electron 之类的东西!” 确实如此——但为了更新和易于访问,我真的更希望应用程序存在于浏览器中。我知道,这听起来像我只是很难。

请让我知道我是否可以提供更多详细信息,或者您是否有任何线索了解我接下来应该研究的内容。

标签: sshbrowsersftpclient-sidewebassembly

解决方案


好的——我假设这个问题对我的用例来说是非常具体的,所以这里可能没有太多的答案需求,但如果有人走上了同样的路……

我所做的是在 Go 中创建一个简单的 HTTP 服务器,客户端可以安装在他们自己的服务器上,然后我的 Web 应用程序可以与之通信。这一切都比我想象的要简单。使用 SSH 是不可能的,所以我创建了一个简单的身份验证系统,然后客户端服务器上的所有操作都由来自 Web 应用程序的 HTTP 请求提示。

这里的一个问题是您将违反 CORS 政策。幸运的是,您可以完全控制服务器应用程序,因此可以管理跨域请求。您需要阅读如何在您的服务器上允许跨源请求(请参阅优秀的 MDN 参考),特别注意所有可能的 Access-Control-* 标头。

还要注意(这让我卡了很长时间)CORS 请求是成对出现的——一个带有 OPTIONS 方法(作为浏览器飞行前检查的一部分),一个带有您分配的任何方法(GET、POST、 ETC。)。OPTIONS 方法是最先发送的,而且非常挑剔。在 MDN 上再次阅读 OPTIONS 和浏览器的预检,以及在其他任何可以找到所需信息的地方。

身份验证是 OPTIONS 的障碍,因为 200 以外的状态码可能会导致预飞行失败并暂停您的请求;但是对 OPTIONS 请求的全面批准可能会导致 OPTIONS 和您的方法之间的内容大小不匹配,从而导致您的请求也失败。为了克服这个障碍,我让我的初始身份验证始终返回状态 200,然后在返回正文中设置“已验证”或“未验证”,以便我的客户端应用程序知道请求是否成功。我还在服务器端设置了一个会话变量,这样用户就不需要继续发送身份验证信息,我可以正常使用 HTTP 状态响应。

这是一个很大的概述。如果有人有具体问题,请随时问他们——我不是 CORS 专家,但我也许可以为您指明正确的方向!


推荐阅读