nginx-proxy-manager Stream 完全指南:用 TCP/UDP 四层转发托管游戏、FTP 与 SSH 服务器
2026/9/10 3:47:00 网站建设 项目流程

nginx-proxy-manager Stream 完全指南:用 TCP/UDP 四层转发托管游戏、FTP 与 SSH 服务器

【免费下载链接】nginx-proxy-managerDocker container for managing Nginx proxy hosts with a simple, powerful interface项目地址: https://gitcode.com/GitHub_Trending/ng/nginx-proxy-manager

Stream 是 nginx-proxy-manager 中相对较新的一项功能,它借助 Nginx 的 stream 模块,把进入指定端口的 TCP/UDP 数据流原样转发到网络中另一台主机,让非 HTTP 协议的服务也能统一纳入可视化代理管理。读完本文,你将掌握 Stream 的适用场景与局限、Web 界面中的完整配置方法、每个字段的取值约束,以及它在后端如何生成 Nginx 配置、通过 REST API 完成增删改查的底层实现原理,并了解仓库中用于验证 TCP、UDP、TCP+UDP 与 SSL 终止四条链路的自动化测试证据。

本文以 前端帮助文档 cs/Streams.md 为主体骨架展开,并结合 backend/internal/stream.js、backend/templates/stream.conf、frontend/src/modals/StreamModal.tsx 等源码进行纵深讲解。

什么是 Stream

按官方帮助文档的定义:

Stream 是 Nginx 相对较新的一项功能,用于把 TCP/UDP 数据流直接转发到网络中另一台计算机。

与 nginx-proxy-manager 中其他"Host"(如 Proxy Host)不同,Stream不做 HTTP 协议解析、也不按域名路由。它工作在传输层(第四层),只关心"哪个端口进来、转发到哪台机器的哪个端口"。在 backend/internal/stream.js 的创建逻辑中可以看到一段关键注释:

// streams aren't routed by domain name so don't store domain names in the DB const data_no_domains = structuredClone(data); delete data_no_domains.domain_names;

源码直接印证了 Stream 的核心定位:不以域名路由,因此域名数据根本不会写入数据库

适用场景:游戏、FTP 与 SSH 服务器

帮助文档明确指出了 Stream 最典型的三种使用场景:

  • 游戏服务器:多数游戏使用自定义 TCP/UDP 端口通信(如 Minecraft 的 25565、CS 系游戏的 27015 等),协议并非 HTTP,普通反向代理无法处理;
  • FTP 服务器:FTP 控制连接(21 端口)与数据连接都是原始 TCP 流,需要四层透传;
  • SSH 服务器:SSH(22 端口)是二进制协议,同样需要原样转发。

凡是"非 HTTP/HTTPS 的 TCP 或 UDP 服务",都可以用 Stream 统一入口、集中管理。它的本质是 Nginx 的stream {}模块能力,由 nginx-proxy-manager 在前端表单与配置模板中做了封装,让用户无需手写 Nginx 配置即可完成端口级转发。

Stream 与 Proxy Host 的区别

理解两者差异有助于判断何时该用哪种"Host":

对比维度StreamProxy Host
工作层次四层(TCP/UDP),透传原始数据流七层(HTTP/HTTPS),解析应用协议
路由依据端口(incoming_port)域名(domain_names)
适用协议游戏、FTP、SSH、RDP 等任意 TCP/UDPHTTP、WebSocket 等 Web 服务
是否支持 SSL 终止支持(TCP 场景)支持
是否支持访问控制/路径转发不支持支持

从数据库模型 backend/models/stream.js 看,stream 表的核心字段包括incoming_portforwarding_hostforwarding_porttcp_forwardingudp_forwardingenabledcertificate_idmeta,完全没有域名列,进一步佐证了"按端口路由"的设计。

Stream 配置字段详解

创建 Stream 时,Web 表单(StreamModal.tsx)与后端校验 stream-object.json 共同定义了以下字段:

字段类型取值范围说明
incoming_port整数1~65535监听入口端口,示例 9090
forwarding_host字符串域名 / IPv4 / IPv6目标主机地址,示例example.com
forwarding_port整数1~65535目标端口,示例 80
tcp_forwarding布尔true / false是否启用 TCP 转发
udp_forwarding布尔true / false是否启用 UDP 转发
enabled布尔true / false是否启用该 Stream
certificate_id整数证书 ID 或 0用于 TCP 的 SSL 终止,可选
meta对象任意 JSON附加元数据,默认{}
owner_user_id整数用户 ID归属用户,由后端自动写入

其中tcp_forwardingudp_forwardingenabledis_deleted是布尔字段,在模型层通过convertBoolFieldsToInt/convertIntFieldsToBool在数据库(0/1)与 API(true/false)之间转换(见 stream.js 模型)。

关于协议开关的交互细节:前端表单保证 TCP/UDP 至少启用其一——当你关闭 TCP 开关时,UDP 会被自动置为开启,反之亦然(见 StreamModal.tsx 的onChange逻辑)。这是因为一个 Stream 至少要转发一种协议才有意义。

通过 Web 界面创建 Stream 的步骤

在 nginx-proxy-manager 管理界面中,进入Streams页面(该页面受STREAMS权限节控制,见 pages/Nginx/Streams/index.tsx),点击新增即可打开表单,包含两个标签页:

Details(详情)标签页,需要填写:

  1. Incoming Port(入口端口):对外监听端口,范围 1~65535,例如8080
  2. Forward Host(转发主机):目标机器的主机名或 IP,例如内网服务器地址192.168.1.10,长度 1~255 字符;
  3. Forward Port(转发端口):目标端口,范围 1~65535;
  4. Protocols(协议):TCP / UDP 两个开关,至少开启一个。

SSL 标签页,可选配置:

  • SSL Certificate(SSL 证书):下拉选择已有证书,或现场新建(allowNew);
  • SSL 选项:包括 HTTP/2、HSTS 等针对 TCP 流场景的 TLS 选项(forHttp={false}表明这是为四层流量而非 HTTP 场景准备)。

保存后,前端通过useSetStream调用后端 API 完成创建或更新(见 StreamModal.tsx),成功后会弹出提示并关闭弹窗。

底层原理:生成的 Nginx 配置长什么样

Stream 的配置由模板 backend/templates/stream.conf 渲染生成。当enabled为真时,按协议开关分别生成两个独立的server块:

# 入站端口 TCP: 1 UDP: 1 server { listen {{ incoming_port }} reuseport {%- if certificate %} ssl {%- endif %}; # listen [::]:{{ incoming_port }} reuseport ssl; # 启用 IPv6 时取消注释 # SSL 相关配置(_certificates_stream.conf) proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; access_log /data/logs/stream-{{ id }}_access.log stream; error_log /data/logs/stream-{{ id }}_error.log warn; # Custom include /data/nginx/custom/server_stream[.]conf; include /data/nginx/custom/server_stream_tcp[.]conf; }

UDP 块结构类似,但listen指令带有udp关键字:

server { listen {{ incoming_port }} udp reuseport; proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; }

几个值得注意的实现细节:

  • reuseport:允许同一端口上的多个 worker 进程共享监听 socket,提升并发接入能力;
  • IPv6:模板默认注释掉[::]监听,仅当ipv6参数启用时才会写入 IPv6 地址;
  • 可扩展性:模板预留了/data/nginx/custom/server_stream[.]confserver_stream_tcp[.]confserver_stream_udp[.]conf三个自定义 include 点,高级用户可以注入额外的 Nginx 指令;
  • 日志分离:每个 Stream 独立记录stream-{id}_access.logstream-{id}_error.log,便于问题排查。

当为 TCP Stream 配置了证书后,backend/templates/_certificates_stream.conf 会被引入,根据证书类型写入证书路径:

  • Let's Encrypt 证书:引用/etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pemprivkey.pem,并 includessl-cache-stream.confssl-ciphers.conf
  • 自定义证书:引用/data/custom_ssl/npm-{{ certificate_id }}/下的 fullchain 与 privkey。

这意味着TCP Stream 支持 TLS 终止(SSL 终结在 Nginx 上,与后端之间仍是明文或自选协议),UDP Stream 不支持 SSL。

后端实现:创建、更新、启停与审计

backend/internal/stream.js 是 Stream 的后端业务核心,围绕数据表操作与 Nginx 配置同步实现了完整生命周期:

  • create:先做streams:create权限校验,剥离域名数据后insertAndFetch入库;若certificate_id === "new",会先走internalCertificate.createQuickCertificate快速签发证书再回填;随后调用internalNginx.configure(streamModel, "stream", row)重新生成 Nginx 配置,最后写入审计日志(action 为created);
  • updatestreams:update权限校验 → 查回原记录做 ID 一致性兜底 → 更新数据库 → 重新 configure 并刷新meta→ 审计日志(updated);
  • delete:软删除(is_deleted: 1),随后internalNginx.deleteConfig("stream", row)删除配置并reload()重载 Nginx,审计日志记录deleted
  • enable / disable:更新enabled标志;启用时重新生成配置,停用时删除配置并重载 Nginx,分别记录enabled/disabled审计动作;
  • getAll:按incoming_port升序返回全部未删除 Stream,支持expand(如ownercertificate)与按端口模糊搜索。

值得留意的两点:

  1. 软删除设计is_deleted标志而非物理删除,所有查询(get / getAll / getCount)都带where("is_deleted", 0)过滤;
  2. 端口冲突预检的 TODO:create 与 update 流程中都有 "existing ports should have been checked" 的 TODO 注释,说明业务层显式端口占用预检仍留待完善,实际冲突会在配置写入与 Nginx 重载环节暴露。

REST API 一览

路由定义在 backend/routes/nginx/streams.js,全部接口需要 JWT 鉴权:

方法路径说明
GET/api/nginx/streams列出所有 Stream,支持expandquery参数
POST/api/nginx/streams新建 Stream,返回 201
GET/api/nginx/streams/:stream_id获取单个 Stream
PUT/api/nginx/streams/:stream_id更新 Stream
DELETE/api/nginx/streams/:stream_id删除 Stream
POST/api/nginx/streams/:host_id/enable启用 Stream
POST/api/nginx/streams/:host_id/disable停用 Stream

以创建一个 TCP Stream 为例,等价于发送如下请求(字段与 Streams.cy.js 测试中的 payload 一致):

{ "incoming_port": 1500, "forwarding_host": "127.0.0.1", "forwarding_port": 80, "certificate_id": 0, "meta": {}, "tcp_forwarding": true, "udp_forwarding": false }

自动化测试如何验证 Stream 可用性

仓库在 test/cypress/e2e/api/Streams.cy.js 中为 Stream 提供了完整的端到端验证,是理解功能行为的直接证据:

  1. TCP Stream(端口 1500):创建后执行curl --noproxy -- http://website1.example.com:1500,断言返回默认站点内容yay it works,证明 TCP 转发链路真实可用;
  2. UDP Stream(端口 1501):创建tcp_forwarding: false, udp_forwarding: true的组合并断言响应字段;
  3. TCP+UDP 双协议(端口 1502):两个开关同时为 true,随后同样用 curl 验证 TCP 通路;
  4. SSL TCP Stream(端口 1503):先上传自定义证书(provider: "other"+/upload接口),再创建绑定该证书的 Stream,最后调用testssl.shwebsite1.example.com:1503做 TLS 安全检查,验证 SSL 终止配置正确。

这些测试同时也验证了 API 响应符合 Swagger Schema(validateSwaggerSchema),即前后端契约一致。

使用注意事项

结合源码与测试,使用 Stream 时建议留意:

  • 每个端口对应一个 Stream:由于按端口监听,同一入口端口不能被多个 Stream 复用(测试中 1500~1503 各自独立);
  • 端口范围受限于 1~65535,且监听端口不能与容器内其他服务冲突;
  • UDP 不支持 SSL:只有 TCP 块支持证书终止;
  • Stream 不做协议级处理:没有重写、路径转发、访问列表等七层能力,如需这些功能应改用 Proxy Host;
  • 高级定制:可在/data/nginx/custom/下放置server_stream.confserver_stream_tcp.confserver_stream_udp.conf注入自定义 Nginx 指令,模板会自动 include。

综上,Stream 是 nginx-proxy-manager 面向四层流量场景的关键能力。无论是把游戏服、FTP、SSH 暴露到公网,还是为内网非 HTTP 服务统一入口,它都能以可视化表单 + 自动生成 Nginx 配置的方式,显著降低手写stream {}配置的维护成本。

【免费下载链接】nginx-proxy-managerDocker container for managing Nginx proxy hosts with a simple, powerful interface项目地址: https://gitcode.com/GitHub_Trending/ng/nginx-proxy-manager

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询