我很高兴为 Swift Server 生态系统介绍一个新的开源项目,即SwiftNIO SSH。作为一个 Swift 包,SwiftNIO SSH 旨在使 Swift 开发人员能够与 SSH 网络协议互动。
什么是 SwiftNIO SSH?
SwiftNIO SSH 是 SSH 的编程实现:也就是说,它是一个 API 的集合,允许程序员实现讲 SSH 的端点。关键是,这意味着它更像 libssh2 而不是 openssh。SwiftNIO SSH 并不提供可用于生产的 SSH 客户端和服务器,而是提供构建这类客户端和服务器的构建模块。
提供一个可编程的 SSH 实现有很多原因。一个原因是,SSH与用户的交互性有独特的关系。技术用户非常习惯于与SSH进行交互,要么在远程机器上运行命令,要么运行交互式外壳。如果有能力对这些请求做出程序化的响应,就可以实现有趣的替代性互动模式。作为现有技术,我们可以指出Twisted的Manhole,它使用一个名为conch 的程序化SSH实现,在运行的Python服务器中提供一个交互式Python解释器,或者ssh-chat,一个提供聊天室而不是常规SSH外壳功能的SSH服务器。还可以想象到TCP转发的创新用途。
提供编程式 SSH 的另一个很好的理由是,服务需要以涉及运行命令的方式与其他服务互动的情况并不少见。虽然Process 解决了本地用例的问题,但有时需要调用的命令是远程的。虽然Process 可以启动一个ssh 客户端作为一个子进程来运行这个调用,但简单地直接调用SSH会更直接。这就是 libssh2的目标用例。SwiftNIO SSH 提供了等同于 libssh2 的网络和加密层,允许积极的用户直接从 Swift 服务中驱动 SSH 会话。
SwiftNIO SSH 支持什么?
SwiftNIO SSH 支持具有以下功能集的 SSHv2:
- 所有会话通道功能,包括 shell 和 exec 通道请求
- 直接和反向 TCP 端口转发
- 仅限现代加密原语。用于非对称加密的 Ed25519 和 EDCSA 在主要 NIST 曲线(P256、P384、P521)上,用于对称加密的 AES-GCM,用于密钥交换的 x25519
- 密码和公钥用户认证
- 支持 SwiftNIO 和 Swift Crypto 所支持的所有平台
我如何使用 SwiftNIO SSH?
SwiftNIO SSH 提供一个 SwiftNIOChannelHandler,NIOSSHHandler 。该处理程序实现了 SSH 协议的大部分内容。预计用户不会直接生成 SSH 消息:相反,他们会通过子通道和委托与NIOSSHHandler 互动。
SSH 是一个多路复用协议:每个 SSH 连接都被细分为多个双向通信通道,称为通道,这很恰当。SwiftNIO SSH 通过使用 "子通道 "抽象来反映这一结构。当一个对等体创建一个新的 SSH 通道时,SwiftNIO SSH 将创建一个新的 NIOChannel ,用来表示该 SSH 通道上的所有流量。在这个子Channel ,所有的事件都是严格按照彼此的顺序排列的:但是,不同Channel的事件可以通过实现自由交错。
因此,一个活跃的SSH连接看起来像这样:
┌ ─ NIO Channel ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
│ ┌───────────────────────────┐ │
│ │
│ │ │ │
│ │
│ │ │ │
│ NIOSSHHandler │──────────────────────┐
│ │ │ │ │
│ │ │
│ │ │ │ │
│ │ │
│ └───────────────────────────┘ │ │
│
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │
│
│
│
│
▼
┌── SSH Child Channel ────────────────────────────────────────────────────┐
│ │
│ ┌───────────────────────────┐ ┌────────────────────────────┐ ├───┐
│ │ │ │ │ │ │
│ │ │ │ │ │ ├───┐
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ User Handler │ │ User Handler │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ └───────────────────────────┘ └────────────────────────────┘ │ │ │
│ │ │ │
└───┬─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │
└───┬─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
一个 SSH 通道被调用,有一个通道类型。SwiftNIO SSH 支持三种类型。session,directTCPIP, 和forwardedTCPIP 。最常见的通道类型是session ,它被用来代表一个程序的调用,无论是一个特定的命名程序还是一个 shell。另外两种通道类型与TCP端口转发有关,将在后面讨论。
一个SSH通道在一个单一的数据类型上操作:SSHChannelData 。这个结构封装了SSH支持常规和 "扩展 "通道数据的事实。常规通道数据 (SSHChannelData.DataType.channel) 被用于绝大多数的核心数据。在session 通道中,.channel 数据类型被用于标准输入和标准输出:.stdErr 数据类型被用于标准错误。在TCP转发通道中,.channel 数据类型是唯一使用的一种,它代表转发的数据。
通道事件
一个session 通道代表一个命令的调用。通道的确切操作方式在一些入站的用户事件中进行了交流。SwiftNIO SSH 支持的范围很广,它涵盖了最重要的用例,包括直接执行命令、请求 shell、请求伪终端、设置环境变量等。
用户认证
用户认证是 SSH 的一个重要组成部分。SwiftNIO SSH 通过一系列委托协议管理用户认证。这些协议是完全异步的,支持可能需要从磁盘读取以执行用户认证的用例。
直接端口转发
直接端口转发是指从客户端到服务器的端口转发。在这种模式下,传统上客户端将在本地端口上监听,并将入站连接转发到服务器上。它将要求服务器将这些连接作为出站连接转发到一个特定的主机和端口。
这些通道可以由客户端通过使用.directTCPIP 通道类型直接打开。
远程端口转发和全局请求
远程端口转发是一种不太常见的情况,客户要求服务器在一个特定的地址和端口上进行监听,并将所有的入站连接转发给客户。由于客户端需要请求这种行为,它使用 "全局请求",这是一个SSH功能,可以请求在连接范围内操作的功能。
全局请求是通过NIOSSHHandler.sendGlobalRequest ,并通过GlobalRequestDelegate 来接收和处理。目前支持两种全局请求:
GlobalRequest.TCPForwardingRequest.listen(host:port:):请求服务器在给定的主机和端口上进行监听。GlobalRequest.TCPForwardingRequest.cancel(host:port:)请求取消对指定主机和端口的监听。
服务器可以通过一个GlobalRequestDelegate 来通知和响应这些请求。在收到全局请求时,这个委托将被调用。一旦建立了监听器,然后使用.forwardedTCPIP 通道类型从服务器向客户端发送入站连接。