「工具推荐」inlets —— 利用 WebSocket 隧道实现内网穿透

使用 inlets 搞 HTTP 服务内网穿透蛮久了,从最早几百 Stars 就开始关注。相比于另一个流行的内网穿透工具 frp,inlets 隧道协议基于 WebSocket,也就是说可以完全享受到 TLS 的安全性,还可以通过各类反向代理、甚至是 Kubernetes 的 Ingress。另外该项目设计时就考虑到了与 Kubernetes 集成,作者也比较注重云原生方向,并且还打包了多个架构的 Docker 镜像,因此非常符合我的需求。

可惜 inlets 在国内似乎并没有像 frp 那么高的知名度,我认为缺少中文 README 是原因之一。所以最近为此项目翻译了文档;不过 PR 还没有合并,我猜可能是因为项目作者不具备中文技能,担心翻译质量等问题。有兴趣的同学欢迎在 PR 内发起建议、讨论,或是为它点个 :+1: Reaction,帮助 PR 尽早合并,谢谢。

PR 地址:https://github.com/inlets/inlets/pull/142

以下是中文文档。


inlets

将你的本地服务暴露到公网。

简介

inlets 利用反向代理和 Websocket 隧道,将内部、或是开发中的服务通过「出口节点」暴露到公网。出口节点可以是几块钱一个月的 VPS,也可以是任何带有公网 IPv4 的电脑。

为什么需要这个项目?类似的工具例如 ngrok 和由 Cloudflare 开发的 Argo Tunnel 皆为闭源,内置了一些限制,并且价格不菲,以及对 arm/arm64 的支持很有限。Ngrok 还经常会被公司防火墙策略拦截而导致无法使用。而其它开源的隧道工具,基本只考虑到静态地配置单个隧道。inlets 旨在动态地发现本地服务,通过 Websocket 隧道将它们暴露到公网 IP 或域名,并自动化配置 TLS 证书。

当开启 SSL 时,inlets 可以通过任何支持 CONNECT 方法的 HTTP 代理服务器。

inlets 概念示意图

协议与条款

重要

如您需要在企业网络中使用 inlets,建议先征求 IT 管理员的同意。下载、使用或分发 inlets 前,您必须同意 协议 条款与限制。本项目不提供任何担保,亦不承担任何责任。

幕后的开发者是谁?

inlets 由 Alex Ellis 开发。Alex 是一名 CNCF 大使,同时是 OpenFaaS 的创始人。

OpenFaaS® 使得开发者将由事件驱动的函数和微服务部署到 Kubernetes 更加容易,而无需编写重复的样板代码。把代码或现成的二进制文件打包进 Docker 镜像,即可获得带有自动扩容和监控指标的服务入口。该项目目前已有接近 19k GitHub stars,超过 240 名贡献者;越来越多的用户已将它应用到生产环境。

待办事项与目标

已完成

  • 基于客户端的定义,自动在出口节点创建服务入口
    • 通过 DNS / 域名实现单端口、单 Websocket 承载多站点
  • 利用 SSL over Websockets 实现链路加密(wss://
  • 自动重连
  • 通过 Service Account 或 HTTP Basic Auth 实现权限认证
  • 使用 cert-magic 自动申请 TLS 证书
    • 通过 HTTP01 challenge 使用 LetsEncrypt Staging 或 Production 签发证书
  • 原生跨平台支持,包括 ARMHF 和 ARM64 架构
  • 提供 Dockerfile 以及 Kubernetes YAML 文件
  • 自动发现并实例化 Kubernetes 集群内 LoadBalancer 类型的 Service - inlets-operator
  • 除 HTTP(s) 以外,还支持在隧道内传输 Websocket 流量
  • 为该项目制作一枚 Logo

延伸目标

  • 自动配置 DNS / A 记录。
  • 基于 Azure ACI 和 AWS Fargate,以 Serverless 容器的方式运行「出口节点」。
  • 通过 DNS01 challenge 使用 LetsEncrypt Staging 或 Production 签发证书

非本项目的目标

  • 通过 Websocket 隧道传输原始 TCP 流量。

    inlets-pro 涵盖了该使用场景,您可以向我咨询 inlets-pro 的内测事宜(English Only)。

项目状态

与 HTTP 1.1 遵循同步的请求/响应模型不同,Websocket 使用异步的发布/订阅模型来发送和接收消息。这带来了一些挑战 —— 通过 异步总线 隧道化传输 同步协议

inlets 2.0 带来了性能上的提升,以及调用部分 Kubernetes 和 Rancher API 的能力。本项目使用了 Rancher 的 K3s 项目 实现节点间通讯同样的隧道依赖包。它非常适用于开发,在生产环境中也很实用。不过在部署 inlets 到生产环境中之前,建议先做好充足的测试。

如果您有任何评论、建议或是贡献想法,欢迎提交 Issue 讨论。

  • 隧道链路通过 --token 选项指定的共享密钥保证安全
  • 默认配置使用不带 SSL 的 Websocket ws://,但支持开启加密,即启用 SSL wss://
  • 可通过服务器端选项设定请求超时时间
  • 服务发现机制完成前,在服务端和客户端都必须配置上游 URL 客户端可发布其可提供服务的上游 URLs
  • 默认情况下,隧道传输会移除响应内的 CORS 头,但你可以在服务端使用 --disable-transport-wrapping 关闭该特性

相关项目

Inlets 作为服务代理 已被列入 Cloud Native Landscape

  • inlets - 开源的七层 HTTP 隧道和反向代理
  • inlets-pro - 四层 TCP 负载均衡
  • inlets-operator - 深度集成 Inlets 和 Kubernetes,实现 LoadBalancer 类型的 Service
  • inletsctl - 配置出口节点的 CLI 工具,配合 inlets 和 inlets-pro 使用

大家如何评论 inlets?

你可以在社交媒体使用 @inletsdev#inletsdevhttps://inlets.dev 分享 inlets 的相关内容。

inlets 曾两次登上 Hacker News 首页推荐:

相关教程(英文):

推文:

提示:欢迎提交 PR 添加你的故事或是使用场景,很期待听到你的声音!

阅读 ADOPTERS.md 查看哪些公司正在使用 inlets。

开始使用

你可以使用 curl 下载安装脚本,或是用 brew 安装,或者直接在 Releases 页面直接下载二进制文件。安装完成后即可使用 inlets 命令。

安装 CLI

提示:虽然 inlets 是一款免费工具,但你也可以在 GitHub Sponsors 页面支持后续的开发 💪

使用 curl 和辅助脚本:

# 安装到当前目录
curl -sLS https://get.inlets.dev | sh

# 安装到 /usr/local/bin/
curl -sLS https://get.inlets.dev | sudo sh

使用 brew

brew install inlets

提示:brew 分发的版本由 Homebrew 团队维护,因此可能会与 GitHub releases 存在一定延迟。

二进制文件可在 Releases 页面 找到;包含 Linux(x86_64、armhf、arm64),Windows(实验性)以及 Darwin(MacOS)版本。如果你想要验证你的下载,也可以查看 SHA 校验值。

鼓励 Windows 用户使用 Git bash 来安装 inlets。

入门教程

你可以在任何两台互相连接的「电脑」之间运行 inlets,「电脑」可以是两个容器,虚拟机,物理机,甚至你笔记本的环回网络也可以。

推荐阅读 how to provision an "exit-node" with a public IPv4 address using a VPS

  • 以下步骤在 出口节点(又称服务端)执行。

首先在任何有公网 IP 的机器上(例如 VPS)启动隧道服务端。

例子如下,生成客户端认证的 Token 并启动服务端:

export token=$(head -c 16 /dev/urandom | shasum | cut -d" " -f1)
inlets server --port=8090 --token="$token"

提示:同时在服务端和客户端配置 --token 选项和密钥,可避免未授权地连接到隧道。

inlets server --port=8090

也可以像上面这样完全无保护地运行,但是并不推荐。

随后记下你的公网 IP。

  • 接下来到运行 HTTP 服务的机器。

你可以使用我开发的 hash-browns 服务作为测试,该服务可生成哈希值。

export GO111MODULE=off
export GOPATH=$HOME/go/

go get -u github.com/alexellis/hash-browns
cd $GOPATH/src/github.com/alexellis/hash-browns

port=3000 go run server.go

如果你没安装 Golang,也可以运行 Python 内置的 HTTP 服务

mkdir -p /tmp/inlets-test/
cd /tmp/inlets-test/
touch hello-world
python -m SimpleHTTPServer 3000
  • 在同一台机器上,启动 inlets 客户端。

启动隧道客户端:

export REMOTE="127.0.0.1:8090"    # 替换成刚刚记下的公网 IP
export TOKEN="CLIENT-TOKEN-HERE"  # Token 的值可在刚刚启动 "inlets server" 时找到
inlets client
 --remote=$REMOTE
 --upstream=http://127.0.0.1:3000
 --token $TOKEN
  • 务必替换 --remote 的值为运行 inlets server (即出口节点)的 IP。
  • 务必将 --token 的值与服务端保持一致。

我们现在总开启了三个进程:

  • 用于测试的 HTTP 服务(运行 hash-browns 或是 Python Web 服务器)
  • 出口节点运行着的隧道服务(inlets server
  • 隧道客户端(inlets client

接下来是时候给 inlets 服务端发请求了,用指向它的域名或 IP 均可:

假设你的服务端位于 127.0.0.1,使用 /etc/hosts 文件或是 DNS 服务将域名 gateway.mydomain.tk 指向 127.0.0.1

curl -d "hash this" http://127.0.0.1:8090/hash -H "Host: gateway.mydomain.tk"
# 或
curl -d "hash this" http://127.0.0.1:8090/hash
# 或
curl -d "hash this" http://gateway.mydomain.tk/hash

你会看到有流量通过隧道客户端到出口节点,如果你运行的是 hash-browns 服务,会出现类似下面的日志:

~/go/src/github.com/alexellis/hash-browns$ port=3000 go run server.go
2018/12/23 20:15:00 Listening on port: 3000
"hash this"

顺便还可以看看 hash-browns 服务内置的 Metrics 数据:

curl $REMOTE/metrics | grep hash

此外你还可以使用多个域名,并将它们分别绑定到多个内网服务。

这里我们在两个端口上启动 Python Web 服务,分别将两个本地目录作为服务内容,并将它们映射到不同的 Host 头,也就是域名:

mkdir -p /tmp/store1
cd /tmp/store1/
touch hello-store-1
python -m SimpleHTTPServer 8001 &

mkdir -p /tmp/store2
cd /tmp/store2/
touch hello-store-2
python -m SimpleHTTPServer 8002 &
export REMOTE="127.0.0.1:8090"    # 替换成刚刚记下的公网 IP
export TOKEN="CLIENT-TOKEN-HERE"  # Token 的值可在刚刚启动 "inlets server" 时找到
inlets client
 --remote=$REMOTE
 --token $TOKEN
 --upstream="store1.example.com=http://127.0.0.1:8001,store2.example.com=http://127.0.0.1:8002"

随后修改 store1.example.comstore2.example.com 的 DNS 指向或设置 /etc/hosts 文件,即可通过浏览器访问了。

继续深入

文档与特色教程

教程:HTTPS for your local endpoints with inlets and Caddy

文档:Inlets & Kubernetes recipes

文档:Run Inlets on a VPS

教程:Get a LoadBalancer for your private Kubernetes cluster with inlets-operator

视频 Demo

使用 inlets 实现为我的 JavaScript & Webpack 应用配置公开的服务入口,以及自定义的域名:Create React App

https://img.youtube.com/vi/jrAqqe8N3q4/hqdefault.jpg

Docker

适用于多种架构的 Docker 镜像已发布,支持 x86_64, arm64 and armhf

  • `inlets/inlets:2.6.3

单出口节点多服务

你可以通过 inlets 暴露 OpenFaaS 或 OpenFaaS Cloud deployment,只需要将 --upstream=http://127.0.0.1:3000 改为 --upstream=http://127.0.0.1:8080 或是 --upstream=http://127.0.0.1:31112 即可。甚至可以指向任何内网或是外网 IP 地址,例如:--upstream=http://192.168.0.101:8080

为控制平面设定独立端口

你可以为用户访问和隧道传输分别指定不同的端口。

  • --port - 指定用户访问、提供对外服务的端口,又称 数据平面
  • --control-port - 指定底层 Websocket 隧道连接的端口,又称 控制平面

开发指引

首先需要在出口节点和客户端都安装 Golang 1.10 或 1.11。

使用类似如下命令获取代码:

go get -u github.com/inlets/inlets
cd $GOPATH/src/github.com/inlets/inlets

另外,你也可以使用 Gitpod 一键在浏览器中配置好开发环境:

Open in Gitpod

附录

其它 Kubernetes 端口转发工具:

  • kubectl port-forward - built into the Kubernetes CLI, forwards a single port to the local computer.
  • kubefwd - Kubernetes utility to port-forward multiple services to your local computer.
  • kurun - Run main.go in Kubernetes with one command, also port-forward your app into Kubernetes.
本作品采用《CC 协议》,转载必须注明作者和本文链接
Former WinForm and PHP engineer. Now prefer Golang and Rust, and mainly working on DevSecOps and Kubernetes.
本帖由系统于 4年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 4

隧道协议使用websocket我觉得反而会有所限制;图示inlets是穿透http协议,如果需要扩展支持其它比如mysql,redis之类就很困难;client和server之间应该开辟两个连接:

一个connection用于控制,client和server之间的协商, 自定义tcp协议即可;
另一个connection用于传输原生报文,server收到来自internet的请求只要透明的将报文pipe到这个连接上,client收到来自内网服务节点的响应报文也发送到这个connection;

这样整个隧道只起到管道的作用,由用户的浏览器和内网的服务自行解析报文内容;这样理论上就可以做到所有tcp协议的穿透;

4年前 评论

@slince

  1. 如果需要支持 TCP 协议的内网穿透可以考虑 inlets-pro,在文档里也有提到。
  2. 现在 inlets 的架构允许设置两个端口分别用于 Control Plane 和 Data Plane,文档里也有提到。
  3. 理想中的确可以实现「整个隧道只起到管道的作用」,但是例如有企业防火墙拦截流量就不行了。另外我的需求是跟 Kubernetes 集成,包括能够通过 Ingress 等,所以 inlets 是个很棒的选择哈。

其实像近几年诞生的 WireGuard 这类隧道设计有开始避免「两条连接」的倾向。一来因为 Control Plane 很容易被 DPI 识别,二来一旦 Control Plane 断开,而 Data Plane 正常的话,就会产生死链。

4年前 评论

很好的工具,请问如果需要在生产环境使用的话,怎么样的高可用配置会比较好?

我的实际需求是会有多个 hostNode Client 来提供不同的服务, hostNode 订阅到 exitNode 的 Server 后 , Server 根据 不同的业务使用不同的 hostNode Client 的服务

我希望 hostNode 和 exitNode 都能够有高可用的配置,怎么支持会比较好?

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
67
粉丝
590
喜欢
1235
收藏
1133
排名:13
访问:32.4 万
私信
所有博文
社区赞助商