「工具推荐」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://
,但支持开启加密,即启用 SSLwss://
- 可通过服务器端选项设定请求超时时间
服务发现机制完成前,在服务端和客户端都必须配置上游 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
、#inletsdev
和https://inlets.dev
分享 inlets 的相关内容。
inlets 曾两次登上 Hacker News 首页推荐:
- inlets 1.0 - 146 points, 48 评论
- inlets 2.0 - 218 points, 66 评论
相关教程(英文):
- Get a LoadBalancer for your private Kubernetes cluster with inlets-operator by Alex Ellis
- Blog post - webhooks, great when you can get them by Alex Ellis
- Micro-tutorial inlets with KinD by Alex Ellis
- The Awesomeness of Inlets by Ruan Bekker
- K8Spin - What does fit in a low resources namespace? Inlets
- Exposing Magnificent Image Classifier with inlets
- "Securely access external applications as Kubernetes Services, from your laptop or from any other host, using inlets"
- Using local services in Gitpod with inlets
推文:
- "I just transferred a 70Gb disk image from a NATed NAS to a remote NATed server with @alexellisuk inlets tunnels and a one-liner python web server" by Roman Dodin
- "Really amazed by inlets by @alexellisuk - "Up and running in 15min - I will be able to watch my #RaspberryPi servers running at home while staying on the beach 🏄♂️🌴🍸👏👏👏" by Florian Dambrine
- Testing an OAuth proxy by Vivek Singh
- inlets used at KubeCon to power a live IoT demo at a booth
- PR to support Risc-V by Carlos Eduardo
- Recommended by Michael Hausenblas for use with local Kubernetes
- 5 top facts about inlets by Alex Ellis
- "Cool! I hadn't heard of inlets until now, but I love the idea of exposing internal services this way. I've been using TOR to do this!" by Stephen Doskett, Tech Field Day
- "Learn how to set up HTTPS for your local endpoints with inlets, Caddy, and DigitalOcean thanks to @alexellisuk!" by @DigitalOcean
- "See how Inlets helped me to expose my local endpoints for my homelab that sits behind a Carrier-Grade NAT"
提示:欢迎提交 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.com
和 store2.example.com
的 DNS 指向或设置 /etc/hosts
文件,即可通过浏览器访问了。
继续深入
文档与特色教程
教程:HTTPS for your local endpoints with inlets and Caddy
文档:Inlets & Kubernetes recipes
教程:Get a LoadBalancer for your private Kubernetes cluster with inlets-operator
视频 Demo
使用 inlets 实现为我的 JavaScript & Webpack 应用配置公开的服务入口,以及自定义的域名:Create React App。
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 一键在浏览器中配置好开发环境:
附录
其它 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 协议》,转载必须注明作者和本文链接
隧道协议使用websocket我觉得反而会有所限制;图示inlets是穿透http协议,如果需要扩展支持其它比如mysql,redis之类就很困难;client和server之间应该开辟两个连接:
一个connection用于控制,client和server之间的协商, 自定义tcp协议即可;
另一个connection用于传输原生报文,server收到来自internet的请求只要透明的将报文pipe到这个连接上,client收到来自内网服务节点的响应报文也发送到这个connection;
这样整个隧道只起到管道的作用,由用户的浏览器和内网的服务自行解析报文内容;这样理论上就可以做到所有tcp协议的穿透;
@slince
其实像近几年诞生的 WireGuard 这类隧道设计有开始避免「两条连接」的倾向。一来因为 Control Plane 很容易被 DPI 识别,二来一旦 Control Plane 断开,而 Data Plane 正常的话,就会产生死链。
很好的工具,请问如果需要在生产环境使用的话,怎么样的高可用配置会比较好?
我的实际需求是会有多个 hostNode Client 来提供不同的服务, hostNode 订阅到 exitNode 的 Server 后 , Server 根据 不同的业务使用不同的 hostNode Client 的服务
我希望 hostNode 和 exitNode 都能够有高可用的配置,怎么支持会比较好?
收费了