资讯动态

libwebsockets 的 lws-sshd-demo 插件:基于 lws-ssh-base 的轻量 SSH 服务端实战剖析

发布时间:2026/10/14 7:34:00 来源:尧图企业网站定制
后端网络通信【免费下载链接】libwebsocketscanonical libwebsockets.org networking library项目地址https://gitcode.com/gh_mirrors/li/libwebsockets点击查看免费下载导读lws-sshd-demo是 libwebsockets 仓库中一个用于演示的 SSH 服务端插件它以抽象 SSH 协议插件 lws-ssh-base 为基础启动时自动加载缺失时原生生成SSH 服务器私钥使用ssh-rsa用户公钥完成本地认证并在 SSH shell 执行后启动一个简单的按键回显交互循环。本文以该插件为主线逐层拆解它的启动流程、认证逻辑、通道 IO 状态机与 ops 回调实现并给出基于 lwsws 配置或自有应用的接入方式帮助读者掌握在 libwebsockets 事件循环内落地一个自定义 SSH 服务端的最小可行方案。一、插件定位作为 lws-ssh-base 的子类1.1 抽象基类 lws-ssh-baselibwebsockets 的 lws-ssh-base 是一个抽象基类式的协议插件把所有 SSH 底层机制封装在内部密钥交换KEX、算法协商、chacha20-poly1305加解密、ssh-rsa用户认证、消息解析状态机等全部由它实现而除网络连接之外的一切业务 IO 都通过一张struct lws_ssh_ops回调表交给用户代码。该插件自身不做任何危险默认行为——不检查~/.ssh/authorized_keys、不把认证用户名当系统用户、不替你启动 shell见 README.plugin-sshd-base.md 的 Introduction 小节。lws-sshd-demo正是官方给出的子类化示例它由 protocol_lws_sshd_demo.c 实现在 vhost 初始化时把一张完整的lws_ssh_ops回调表挂到协议实例上剩余所有 SSH 协议工作都转交给lws-ssh-base完成。1.2 构建约束插件的构建定义在 plugins/protocol_lws_sshd_demo/CMakeLists.txt 中set(PLUGIN_NAME protocol_lws_sshd_demo) set(requirements 1) require_lws_config(LWS_WITH_SSL 1 requirements) if (requirements) create_plugin(${PLUGIN_NAME} ../protocol_lws_ssh_base/include protocol_lws_sshd_demo.c ) ... endif()可见它依赖LWS_WITH_SSL为 RSA/哈希运算提供 mbedTLS 或 OpenSSL 后端并把protocol_lws_ssh_base/include加入头文件搜索路径以引用lws-ssh.h。插件可被静态编入应用也可以.so动态加载当以插件方式运行时导出符号lws_sshd_demo的命名必须与共享库后缀完全一致因为 lwsws 启动时会用该字符串直接做dlsym()查找源码中注释对此有明确说明见 protocol_lws_sshd_demo.c。二、协议注册与 ops 回调表2.1 协议描述符插件通过宏注册名为lws-sshd-demo的协议#define LWS_PLUGIN_PROTOCOL_LWS_SSHD_DEMO \ { \ lws-sshd-demo, \ callback_lws_sshd_demo, \ 0, \ 1024, /* rx buf size must be permessage-deflate rx size */ \ 0, (void *)ssh_ops, 0 \ }见 protocol_lws_sshd_demo.c这里最关键的字段是user (void *)ssh_ops它把struct lws_ssh_ops实例地址作为协议用户指针挂载供lws-ssh-base通过ops-fromPVO 按协议名取回。rx 缓冲区大小 1024 是为兼容 permessage-deflate 扩展而设的下限。2.2 demo 实现的 ops 全集插件在 protocol_lws_sshd_demo.c 中定义了一张完整的 ops 表static const struct lws_ssh_ops ssh_ops { .channel_create ssh_ops_channel_create, .channel_destroy ssh_ops_channel_destroy, .tx_waiting ssh_ops_tx_waiting, .tx ssh_ops_tx, .rx ssh_ops_rx, .get_server_key ssh_ops_get_server_key, .set_server_key ssh_ops_set_server_key, .set_env NULL, .pty_req NULL, .child_process_io NULL, .child_process_terminated NULL, .exec NULL, .shell ssh_ops_shell, .is_pubkey_authorized ssh_ops_is_pubkey_authorized, .banner ssh_ops_banner, .disconnect_reason ssh_ops_disconnect_reason, .server_string SSH-2.0-Libwebsockets, .api_version 2, };与 test-apps/test-sshd.c 中真实 shell版本相比demo 刻意把set_env、pty_req、child_process_io、child_process_terminated、exec置为 NULL只保留shell入口——这正好呼应lws-ssh-base的按需回调设计sshd.c中所有调用点都做了 NULL 守卫例如 shell 请求只有在ops-shell非空且返回 0 时才回复SSH_WT_CHRQ_SUCC见 sshd.cpty 与 env 请求同理sshd.c、sshd.c。因此 demo 的 ops 表是最小可用子集读者可在此基础上补齐 pty/exec 等回调以实现完整终端服务。2.3 回调转发机制demo 的协议回调函数callback_lws_sshd_demo只处理四个事件LWS_CALLBACK_PROTOCOL_INIT初始化 vhost 私有数据、打开服务器密钥文件、LWS_CALLBACK_PROTOCOL_DESTROY关闭 fd、以及两个空实现的VHOST_CERT_AGING/EVENT_WAIT_CANCELLED。其余所有回调default 分支都通过lws_vhost_name_to_protocol()找到同 vhost 上的lws-ssh-base协议并用lws_adjust_protocol_psds()把 per-session 数据尺寸调整为 ssh-base 的大小后直接调用其回调完成转发见 protocol_lws_sshd_demo.c。这即是子类化的本质demo 只提供 ops 业务逻辑SSH 协议状态机全部由父类执行。三、Per-Vhost OptionsPVOdemo 插件本身不消费配置关联文档明确指出demo 插件不查找任何自定义 PVO它只是继承了处理 SSH 服务端端点所需的所有操作。真正消费 PVO 的是lws-ssh-base它支持两个配置项定义与解析见 lws-plugin-ssh.h 与 sshd.cPVO 名称说明ops直接指向const struct lws_ssh_ops的裸指针由实例化 vhost 的 C 代码原生传入ops-from替代方案另一个已加载协议的名称字符串lws-ssh-base从该协议的.user指针取出 ops 表demo 场景走的是ops-from把协议名lws-sshd-demo告诉lws-ssh-base后者通过lws_vhost_name_to_protocol()找到 demo 协议并取用其user指针指向的ssh_ops。若 vhost 上配置了两个以上lws-ssh-base实例每个实例都可以用不同的ops/ops-from绑定各自的服务器密钥与认证逻辑见 lws-plugin-ssh.h 的设计说明。此外lws-ssh-base在 PROTOCOL_INIT 时强制校验ops-api_version LWS_SSH_OPS_VERSION当前为 2LWS_SSH_OPS_VERSION定义于 lws-plugin-ssh.h版本不匹配会在运行时直接报 FATAL 并拒绝启动sshd.c。四、启动期特权处理与服务器密钥4.1 密钥文件路径与双模式打开demo 的服务器密钥固定存放在#define TEST_SERVER_KEY_PATH /etc/lws-test-sshd-server-key见 protocol_lws_sshd_demo.c由于服务器密钥文件位于/etc下需要 root 权限读写插件利用LWS_CALLBACK_PROTOCOL_INIT仍保留启动特权capabilities的窗口期先以只读方式lws_open()打开若文件不存在则改用O_CREAT | O_TRUNC | O_RDWR, 0600创建protocol_lws_sshd_demo.c并把 fd 存进 vhost 私有数据struct per_vhost_data__lws_sshd_demo的privileged_fd字段供后续get_server_key/set_server_key在降权后依然能读写密钥。协议销毁时再close()释放protocol_lws_sshd_demo.c。4.2 密钥的读写回调ops 表中的get_server_key/set_server_key直接基于privileged_fd做lseekread/write见 protocol_lws_sshd_demo.c。这两者的配合遵循lws-ssh-base的约定若get_server_key返回 0无密钥插件会生成随机密钥对再通过set_server_key持久化供后续连接复用见 lws-plugin-ssh.h 对这两个回调的注释。在sshd.c中KEX 阶段通过get_gen_server_key_25519()取回密钥并用ed25519_key_parse()解析sshd.c随后参与curve25519-sha256libssh.org的 ECDH 交换kex_ecdh见 sshd.c。lws-ssh-base为保持体积只实现了单一best of breed密码组合KEX 用 curve25519-sha256、服务器主机密钥用 ssh-rsa (4096b)、加密用 chacha20-poly1305、压缩关闭见 README.plugin-sshd-base.md 的 crypto 小节。4.3 生成自有密钥若要在真实部署中替换 demo 的内置测试密钥可参照 README.plugin-sshd-base.md 的说明生成 RSA 4096 密钥对ssh-keygen -t rsa -b 4096 -f mykeys仓库在 test-apps/lws-ssh-test-keys 与 test-apps/lws-ssh-test-keys.pub 内置了官方测试密钥对安装后会放到/usr[/local]/share/libwebsockets-test-server/下。这两把密钥仅用于测试——demo 源码注释特别警告任何持有 libwebsockets 副本的人都能用这把密钥登录你的测试服务切勿用于真实系统见 protocol_lws_sshd_demo.c。五、ssh-rsa 用户认证demo 的内置授权实现5.1 认证入口客户端发起ssh-rsa公钥认证后lws-ssh-base解析出类型、公钥 blob并调用 ops 的is_pubkey_authorized回调sshd.c。demo 的实现ssh_ops_is_pubkey_authorized完整逻辑见 protocol_lws_sshd_demo.c流程如下检查公钥类型字符串必须为ssh-rsa否则拒绝从内置authorized_key字符串中剥离类型前缀与空格用lws_b64_decode_string()解出 Base64 编码的公钥 blob比对解码长度与客户端上报的peer_len是否一致用lws_timingsafe_bcmp()常量时间比较防时序侧信道声明见 lws-ssh.h逐字节比较公钥因子一致则放行返回 0任一步失败则拒绝并打印lwsl_notice级日志。5.2 内置测试公钥demo 把官方测试公钥硬编码在源码中protocol_lws_sshd_demo.c其内容与 test-apps/lws-ssh-test-keys.pub 对应格式为标准的ssh-rsa AAAA... ssh-test-keylws。这正是lws-ssh-base文档所强调的设计哲学它不做任何内置授权策略是否接受某把公钥完全由用户 ops 代码决定——demo 只是选择接受内置测试公钥而已。生产环境应替换为查询自己的 authorized_keys 存储。5.3 认证超时保护lws-ssh-base在 RAW_ADOPT 时会为连接设置认证超时lws_set_timeout(wsi, SSH_PENDING_TIMEOUT_CONNECT_TO_SUCCESSFUL_AUTH, 10 * 60)即在 10 分钟内未完成认证则断开sshd.c这是 RFC 4252 的推荐做法。六、通道生命周期与按键回显状态机6.1 通道创建/销毁客户端打开 session 通道时lws-ssh-base调用channel_createdemo 在其中为每个通道malloc一个struct sshd_instance_priv记录wsi、当前状态枚举与待发送字符串指针protocol_lws_sshd_demo.cstruct sshd_instance_priv { struct lws *wsi; enum states state; const char *ptr; int pos; int len; };channel_destroy负责free该结构。每次进入新状态时enter_state()会重置ptr/pos/len并调用lws_callback_on_writable(priv-wsi)触发下一次可写通知protocol_lws_sshd_demo.c。6.2 shell 请求不启动真实 shell客户端请求shell时demo 的ssh_ops_shell不做任何 fork/exec而是直接进入SSH_TEST_GREET状态protocol_lws_sshd_demo.c。之后lws-ssh-base在 ops-shell 返回 0 的前提下回复SSH_WT_CHRQ_SUCCsshd.c。作为对比test-apps/test-sshd.c 的ssh_ops_shell会调用lws_cgi()真正 spawn 一个 bash shell——这正是补全 ops 回调即可从 demo 升级为完整终端的直接证据。6.3 三态交互欢迎 → 按键回显 → 再见demo 定义了一个极简的枚举状态机protocol_lws_sshd_demo.cenum states { SSH_TEST_GREET, /* Thanks for logging to lws sshd server demo... Press a key... */ SSH_TEST_PRESSED, /* Thanks for pressing a key. Press another to exit. */ SSH_TEST_DONE, /* Bye! 之后退出 */ };对应的三段文案以\n\r行结束演示了 SSH 通道上最基本的按需拉取式输出机制tx_waiting返回LWS_STDOUT位表示 stdout 有待发送数据当处于SSH_TEST_DONE且文本已全部送出时返回-1通知lws-ssh-base关闭到客户端的 TCP 连接protocol_lws_sshd_demo.c。返回位掩码的语义LWS_STDOUT/LWS_STDERR位或 0定义在 lws-plugin-ssh.h。tx只处理stdch LWS_STDOUT的写请求从priv-ptr priv-pos拷贝至多len字节并推进pos在发送SSH_TEST_DONE的最后一段前再次触发lws_callback_on_writable()确保再见文案送达后再由下一次tx_waiting()返回 -1 收尾protocol_lws_sshd_demo.c。rx每收到任意按键数据就推进一个状态GREET → PRESSED → DONE到达SSH_TEST_DONE后仍有输入则返回 -1 断开protocol_lws_sshd_demo.c。这一pull 式IO 模型是lws-ssh-base与 libwebsockets 事件循环融合的关键业务侧永远不主动写 socket而是通过lws_callback_on_writable()请求调度由 base 在可写时机回调tx_waiting()/tx()取数据天然契合 libwebsockets 的 event loop 架构。七、横幅banner与断开原因通知ssh_ops_banner向客户端输出 ASCII 猫脸横幅lws-ssh Test Server / SSH Terminal Server并设置语言标签en/USprotocol_lws_sshd_demo.c。lws-ssh-base在 KEX/认证阶段以 UTF-8 形式将该文本作为SSH_MSG_USERAUTH_BANNER发送sshd.c若不需要横幅将 ops 的.banner置 NULL 即可lws-plugin-ssh.h。ssh_ops_disconnect_reason以lwsl_notice记录断开原因码SSH_DISCONNECT_*系列常量定义于 lws-ssh.h与描述文本。八、如何运行三种接入方式8.1 方式一lwsws 配置对应关联文档场景lws-sshd-demo设计目标之一就是可直接从 lwsws 配置文件启用。完整配置示例见 README.plugin-sshd-base.md 的 Example code 小节要点如下{ name: sshd, port: 2222, onlyraw: 1, ws-protocols: [{ lws-ssh-base: { status: ok, ops-from: lws-sshd-demo }, lws-sshd-demo: { status: ok, raw: 1 } }] }对应关联文档的 PVO 说明这里通过ops-from: lws-sshd-demo让lws-ssh-base从 demo 协议的.user指针取 ops 表。三条关键语义onlyraw: 1该 vhost 只提供 RAW非 HTTP服务lws-ssh-base提供 SSH 服务但不标记为 raw 端点lws-sshd-demo标记raw: 1作为 raw 连接实际派发到的协议即 SSH 流量挂在 demo 协议上再由 demo 转发给 ssh-base。8.2 方式二test-sshd 应用带真实 shell 的对照仓库自带的 test-apps/test-sshd.c 是完整版参考实现它注册lws-sshd-demo1协议用 PVO 直接把ssh_ops以ops键传给 vhosttest-apps/test-sshd.c以LWS_SERVER_OPTION_ONLY_RAWinfo.port 2200创建 vhosttest-apps/test-sshd.c并在 ops 中实现pty_req、exec、shelllws_cgi spawn bash等完整回调。运行与连接命令# 服务器密钥存 /etc需要 root sudo libwebsockets-test-sshd # 客户端使用官方测试私钥连接注意 test-sshd 监听 2200 端口 ssh -p 2200 -i /usr/local/share/libwebsockets-test-server/lws-ssh-test-keys anyuser127.0.0.18.3 方式三自带协议实例接入参考 README.plugin-sshd-base.md 的 Integration to other apps 三步骤把 ssh-base 插件/静态构建包含进应用 → 按 lws-plugin-ssh.h 定义自己的struct lws_ssh_ops→ 在 vhost 创建时通过 PVO 传入 opsC 代码中直接传ops或用ops-from指向其他协议的 user 指针。demo 插件正是Integration to other plugins一节的完整范例。8.4 客户端连接与端口约定由于服务器密钥位于/etcdemo 场景同样需要以 root 运行 lwsws。连接命令为ssh -p 2222 -i /usr/local/share/libwebsockets-test-server/lws-ssh-test-keys anyuser127.0.0.1其中-p 2222对应 lwsws 配置的监听端口SSH 命令行应用可用-p指定非 22 端口-i指定官方测试私钥 test-apps/lws-ssh-test-keys。登录后会看到 banner、欢迎文案随后按一个键得到回显、再按一个键退出——这套交互就是 demo 状态机GREET → PRESSED → DONE的直接体现。九、源码对照速查表关注点位置demo 插件主体与 ops 实现plugins/protocol_lws_sshd_demo/protocol_lws_sshd_demo.cdemo 插件 CMake 构建plugins/protocol_lws_sshd_demo/CMakeLists.txtSSH ops 接口定义全部回调签名plugins/protocol_lws_ssh_base/include/lws-plugin-ssh.hSSH 协议/状态机内部头文件plugins/protocol_lws_ssh_base/include/lws-ssh.hSSH 服务端协议状态机与 PVO 解析plugins/protocol_lws_ssh_base/sshd.cssh-base 插件文档plugins/protocol_lws_ssh_base/sshd.mdssh-base 集成指南READMEs/README.plugin-sshd-base.md完整版对照测试应用真实 shelltest-apps/test-sshd.c官方测试密钥对test-apps/lws-ssh-test-keys、test-apps/lws-ssh-test-keys.pub十、小结从 demo 出发的自定义 SSH 服务lws-sshd-demo以极小的代码量展示了在 libwebsockets 中构建 SSH 服务端的完整骨架协议名注册 ops 表挂载 回调转发 密钥生命周期管理。它把 SSH 协议层KEX、加密、认证、消息解析完全交给lws-ssh-base自身只保留三件业务事密钥文件的特权期打开与读写、内置测试公钥的授权判定、以及三态按键回显的通道 IO。任何希望在产品中落地自定义 SSH 服务的开发者都可以把本插件当作起点替换authorized_key为自有授权存储、补全pty_req/exec/set_env/child_process_io回调参考 test-apps/test-sshd.c、更换服务器密钥路径即可在保持lws-ssh-base全部协议能力不变的前提下得到属于自己的 SSH 终端服务。赞分享后端网络通信【免费下载链接】libwebsocketscanonical libwebsockets.org networking library项目地址https://gitcode.com/gh_mirrors/li/libwebsockets点击查看免费下载相关推荐libwebsockets lws-ssh-base 插件用 ops 回调打造嵌入式 SSH 服务器libwebsockets lws ssh base 插件用 ops 回调打造嵌入式 SSH 服务器 导读 lws ssh base 是 libwebsock后端网络通信libwebsockets lws-ssh-base 插件深度解析在事件循环内内建 SSH 服务器的抽象基座libwebsockets lws ssh base 插件深度解析在事件循环内内建 SSH 服务器的抽象基座 导读 lws ssh base 是 libweb后端网络通信libwebsockets lws-ssh-base 插件详解以 ops 结构体驱动的可嵌入 SSH 服务器框架libwebsockets lws ssh base 插件详解以 ops 结构体驱动的可嵌入 SSH 服务器框架 本文基于 third_party/libwe人工智能AI Agent多模态语音AI 应用上一篇Windows 10 玩红警2联机救星IPXWrapper 协议转换保姆级上手指南下一篇PyPTO-Gym Pro 文档检索实践PyPTO-Pro API 索引导航与本地缓存装配机制解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑