隆重介绍 SwiftNIO SSH
我很高兴地介绍一个针对 Swift 服务端生态系统的新开源项目:SwiftNIO SSH。SwiftNIO SSH 以 Swift 包的形式分发,旨在使 Swift 开发者能够与 SSH 网络协议进行交互。
什么是 SwiftNIO SSH?
SwiftNIO SSH 是 SSH 的程序化实现:也就是说,它是一组 API,允许程序员实现支持 SSH 协议的端点。至关重要的是,这意味着它更像 libssh2 而不是 openssh。SwiftNIO SSH 不提供生产就绪的 SSH 客户端和服务器,而是为构建此类客户端和服务器提供构建块。
提供程序化的 SSH 实现有许多原因。其中之一是 SSH 与用户交互的独特关系。技术用户非常习惯于以交互方式与 SSH 进行交互,无论是运行远程计算机上的命令还是运行交互式 shell。能够以编程方式响应这些请求,可以实现有趣的替代交互模式。作为先例,我们可以指出 Twisted 的 Manhole,它使用 一个名为 conch
的程序化 SSH 实现,在运行中的 Python 服务器中提供一个交互式 Python 解释器,或者 ssh-chat,一个提供聊天室而不是常规 SSH shell 功能的 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 提供了一个 SwiftNIO ChannelHandler
,即 NIOSSHHandler
。此处理程序实现了 SSH 协议的大部分功能。用户不需要直接生成 SSH 消息:相反,他们通过子通道和委托与 NIOSSHHandler
进行交互。
SSH 是一种多路复用协议:每个 SSH 连接都细分为多个双向通信通道,恰如其分地称为通道。SwiftNIO SSH 通过使用“子通道”抽象来反映这种构造。当对等方创建一个新的 SSH 通道时,SwiftNIO SSH 将创建一个新的 NIO Channel
,用于表示该 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
通道类型从服务器发送到客户端。
其他资源
更多文档和示例可以在 GitHub 上找到。
项目状态
该项目目前处于预发布状态。虽然它被认为是功能完整的,但我们希望在标记 1.0 版本之前给它更多的时间在公众视野中进行验证。但是,我们预计从现在到该版本之间不会有任何 API 破坏。
参与进来
如果您对 SwiftNIO SSH 感兴趣,请参与进来!SwiftNIO SSH 是一个完全开源的项目,在 GitHub 上开发。我们随时欢迎来自开源社区的贡献。我们鼓励在 Swift 论坛上进行讨论。对于错误报告、功能请求和拉取请求,请使用 GitHub 仓库。
我们非常期待看到您使用 SwiftNIO SSH 创造的惊人事物!