从客户端浏览器通过 SSH 连接到服务器,无需中间人服务器
Connecting to a server via SSH from the client-side browser, without a middle man server
没有具体的代码可以看——只是希望讨论我面临的问题,列出我知道的选项,看看是否有我遗漏的选项。如果这不合适,请告诉我,我会删除问题。
问题
我正在开发一个基于 Web 的应用程序,该应用程序具有在用户拥有的服务器上执行 运行ning 命令的方法,最好是通过 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 客户端,SSH 客户端将在客户端拥有的服务器上执行相关调用。唯一的缺点是它需要用户下载并 运行 安装程序,并且程序必须不断地 运行 并监听客户端计算机上的端口。这并不像我想要的那样无缝的用户体验,并且容易出现程序终止或机器启动时无法正常启动等错误。我还必须维护多个主要操作系统的安装程序。
解决方案 2
放弃 SSH。创建一个小型网络服务器(可能在 Go 中)以侦听客户端拥有的服务器上的端口。然后,客户端网络应用程序可以 API 直接调用客户端拥有的服务器,通过 HTTPS 加密,监听客户端拥有的服务器的网络服务器将处理请求。这样做的缺点:我需要创建一个用户管理系统(而不是依赖于客户端拥有的服务器的身份验证系统)并实施某种方法来确定客户端拥有的服务器实际上由客户端拥有。
大家可能的问题
您可能会问,“为什么不直接制作一个本机应用程序?如果您喜欢制作网络应用程序,您可以使用像 Electron 这样的东西!”这是真的——但为了更新和易于访问,我真的更希望应用程序存在于浏览器中。我知道,听起来我只是很难相处。
请告诉我是否可以提供更多详细信息,或者您是否对我接下来应该研究的内容有任何线索。
好的——我假设这个问题非常针对我的用例,所以这里可能没有太多的答案需求,但如果有人走同样的路...
我所做的是用 Go 创建一个简单的 HTTP 服务器,客户端可以将其安装在自己的服务器上,然后我的 Web 应用程序可以与之通信。一切都比我想象的要简单。使用 SSH 是不可能的,所以我创建了一个简单的身份验证系统,然后客户端服务器上的所有操作都由来自 Web 应用程序的 HTTP 请求提示。
这里的一个问题是您将 运行 违反 CORS 政策。值得庆幸的是,您可以完全控制服务器应用程序,因此 cross-origin 请求是可管理的。您需要阅读如何在您的服务器上允许跨源请求(参见 the excellent MDN reference),特别注意所有可能的 Access-Control-* headers.
还要注意(这让我卡住了很长时间)CORS 请求是成对出现的——一个使用 OPTIONS 方法(作为浏览器 pre-flight 检查的一部分),另一个使用任何方法您分配(GET、POST 等)。 OPTIONS 方法首先发送,并且非常挑剔。阅读 OPTIONS 和浏览器的 pre-flight again at MDN,以及您可以在其他任何地方找到所需信息。
身份验证是 OPTIONS 的障碍,因为 200 以外的状态代码可能导致 pre-flight 失败并停止您的请求;但是全面批准 OPTIONS 请求可能会导致 OPTIONS 和您的方法之间的内容大小不匹配,从而导致您的请求也失败。为了克服这个障碍,我的初始身份验证总是 return 状态 200,然后在 return body 中设置“已验证”或“未验证”,以便我的客户端应用程序知道是否请求是否成功。我还在服务器端设置了一个 session 变量,这样用户就不需要继续发送授权信息,我可以正常使用 HTTP 状态响应。
这是一个大概览。如果有人有具体问题,请随时提问——我不是 CORS 专家,但我也许可以为您指明正确的方向!
没有具体的代码可以看——只是希望讨论我面临的问题,列出我知道的选项,看看是否有我遗漏的选项。如果这不合适,请告诉我,我会删除问题。
问题
我正在开发一个基于 Web 的应用程序,该应用程序具有在用户拥有的服务器上执行 运行ning 命令的方法,最好是通过 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 客户端,SSH 客户端将在客户端拥有的服务器上执行相关调用。唯一的缺点是它需要用户下载并 运行 安装程序,并且程序必须不断地 运行 并监听客户端计算机上的端口。这并不像我想要的那样无缝的用户体验,并且容易出现程序终止或机器启动时无法正常启动等错误。我还必须维护多个主要操作系统的安装程序。
解决方案 2
放弃 SSH。创建一个小型网络服务器(可能在 Go 中)以侦听客户端拥有的服务器上的端口。然后,客户端网络应用程序可以 API 直接调用客户端拥有的服务器,通过 HTTPS 加密,监听客户端拥有的服务器的网络服务器将处理请求。这样做的缺点:我需要创建一个用户管理系统(而不是依赖于客户端拥有的服务器的身份验证系统)并实施某种方法来确定客户端拥有的服务器实际上由客户端拥有。
大家可能的问题
您可能会问,“为什么不直接制作一个本机应用程序?如果您喜欢制作网络应用程序,您可以使用像 Electron 这样的东西!”这是真的——但为了更新和易于访问,我真的更希望应用程序存在于浏览器中。我知道,听起来我只是很难相处。
请告诉我是否可以提供更多详细信息,或者您是否对我接下来应该研究的内容有任何线索。
好的——我假设这个问题非常针对我的用例,所以这里可能没有太多的答案需求,但如果有人走同样的路...
我所做的是用 Go 创建一个简单的 HTTP 服务器,客户端可以将其安装在自己的服务器上,然后我的 Web 应用程序可以与之通信。一切都比我想象的要简单。使用 SSH 是不可能的,所以我创建了一个简单的身份验证系统,然后客户端服务器上的所有操作都由来自 Web 应用程序的 HTTP 请求提示。
这里的一个问题是您将 运行 违反 CORS 政策。值得庆幸的是,您可以完全控制服务器应用程序,因此 cross-origin 请求是可管理的。您需要阅读如何在您的服务器上允许跨源请求(参见 the excellent MDN reference),特别注意所有可能的 Access-Control-* headers.
还要注意(这让我卡住了很长时间)CORS 请求是成对出现的——一个使用 OPTIONS 方法(作为浏览器 pre-flight 检查的一部分),另一个使用任何方法您分配(GET、POST 等)。 OPTIONS 方法首先发送,并且非常挑剔。阅读 OPTIONS 和浏览器的 pre-flight again at MDN,以及您可以在其他任何地方找到所需信息。
身份验证是 OPTIONS 的障碍,因为 200 以外的状态代码可能导致 pre-flight 失败并停止您的请求;但是全面批准 OPTIONS 请求可能会导致 OPTIONS 和您的方法之间的内容大小不匹配,从而导致您的请求也失败。为了克服这个障碍,我的初始身份验证总是 return 状态 200,然后在 return body 中设置“已验证”或“未验证”,以便我的客户端应用程序知道是否请求是否成功。我还在服务器端设置了一个 session 变量,这样用户就不需要继续发送授权信息,我可以正常使用 HTTP 状态响应。
这是一个大概览。如果有人有具体问题,请随时提问——我不是 CORS 专家,但我也许可以为您指明正确的方向!