Metadata-Version: 2.4
Name: websocket-tunnel
Version: 0.3.0
Summary: A frp-like intranet penetration tool with WebSocket transport (TCP passthrough).
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: websockets>=14
Description-Content-Type: text/markdown

# websocket-tunnel

一个类似 frp 的内网穿透工具：Python + uv 实现，传输层使用 WebSocket 多路复用，
支持任意 TCP 服务（HTTP/HTTPS/SSH/MySQL 等）的字节流透传。构建产物通过
`uv build` 生成，安装后提供 `wtunnel` CLI，分为 `server` 与 `client` 两种模式。

与 frp 的主要差异：**代理不区分服务端/客户端**。代理条目可以声明在任意节点的
配置中，监听端（listen）与后端（backend）分别用 `local` / `peer` 指定在哪一侧，
因此以下四种组合全部支持：

| listen 侧 | backend 侧 | 用途 |
| --- | --- | --- |
| server | client | 经典 frp：把内网服务暴露到公网服务器端口 |
| client | server | 反向：把服务端网络里的服务映射到客户端本地端口 |
| server | server | 服务端本机端口转发（不占用隧道） |
| client | client | 客户端本机端口转发（不占用隧道） |

## 安装

要求 Python >= 3.11 与 [uv](https://docs.astral.sh/uv/)。

```bash
uv sync            # 开发环境（含测试依赖）
uv build           # 构建 sdist + wheel
uv run wtunnel --version
```

也可以把 wheel 安装到任意环境：

```bash
uv pip install dist/websocket_tunnel-0.1.0-py3-none-any.whl
wtunnel --version
```

## 快速开始

### 1. 启动服务端

```bash
wtunnel server -c examples/server.toml
```

### 2. 启动客户端

```bash
wtunnel client -c examples/client.toml
```

客户端默认把本机 `127.0.0.1:3000` 暴露到服务端的 `0.0.0.0:8080`，访问
`http://<server>:8080` 即可到达客户端内网的服务。

## CLI 参数

```text
wtunnel server -c server.toml [--listen HOST:PORT] [--token TOKEN]
                       [--tls-cert PATH --tls-key PATH] [-v]
wtunnel client -c client.toml [--server HOST:PORT] [--token TOKEN]
                       [--tls] [--tls-skip-verify] [-v]
```

CLI 参数覆盖配置文件中的同名项；`-v` 输出 DEBUG 日志。

## 配置参考

`server.toml`：

```toml
listen = "0.0.0.0:7000"          # 控制端口（ws/wss）
token = "secret"                 # 可选，共享认证 token
tls = { cert = "server.crt", key = "server.key" }   # 可选，启用 wss

# 最大并发控制连接数（0 = 不限）
max_connections = 10

# 允许 client 请求本节点连接的 backend IP 范围（CIDR 列表，空 = 允许所有）
allow_peer_backends = ["127.0.0.1/32", "10.0.0.0/8"]

# 允许 client 请求本节点绑定的 listen IP 范围（空 = 允许所有）
allow_peer_listens = ["0.0.0.0/0", "::/0"]

[[proxies]]
name = "web"
listen = "0.0.0.0:8080"
listen_side = "local"            # "local" | "peer"
backend = "127.0.0.1:3000"
backend_side = "peer"
```

`client.toml`：

```toml
server = "127.0.0.1:7000"        # 服务端地址
token = "secret"
tls = false                      # true 时使用 wss
tls_skip_verify = false          # 自签证书时设为 true

# 允许服务端请求本节点连接的 backend IP 范围（CIDR 列表，空 = 允许所有）
allow_peer_backends = ["127.0.0.1/32"]

# 允许服务端请求本节点绑定的 listen IP 范围（空 = 允许所有）
allow_peer_listens = ["127.0.0.1/32"]

[[proxies]]
name = "web"
listen = "0.0.0.0:8080"
listen_side = "peer"
backend = "127.0.0.1:3000"
backend_side = "local"
```

`listen` / `backend` 均为 `host:port`，IPv6 使用 `[::1]:8080` 形式。`allow_peer_*`
白名单中的地址必须使用 IP（不支持主机名），CIDR 掩码可省略（如 `"10.0.0.1"`
等同于 `"10.0.0.1/32"`）。

## 传输与协议概要

- 每对节点一条 WebSocket 长连接（client 主动连接 server），控制消息与所有数据流
  在其上多路复用；每条 TCP 流分配独立 stream id。
- 二进制帧：首字节为消息类型；控制消息为 JSON，数据帧为
  `stream_id`(4 字节大端) + 数据块（默认 32 KiB）。
- 支持半关闭：任一方向 EOF 只关闭对应方向，保证 HTTP/1.0 与 keep-alive 响应完整。
- 握手时校验共享 token（失败即断开）；服务端可选 wss，客户端可跳过证书校验。
- client 断线自动重连（指数退避 1s→30s），重连后双方重新注册代理并重新绑定监听。

## 开发与测试

```bash
uv run pytest          # 单元 + 集成测试
uv build               # 构建发布产物
```

## 发布到 PyPI

打上 `v` 开头的 tag 并推送即触发 GitHub Actions 发布：

```bash
git tag v0.1.0
git push origin v0.1.0
```

工作流（`.github/workflows/release.yml`）会校验 tag 与包版本一致、运行测试、
`uv build` 构建后通过 trusted publishing（OIDC）发布到 PyPI。首次使用前需要在
PyPI 项目页配置 Trusted Publisher：仓库 `8DE4732A/websocket-tunnel`、
工作流名 `Publish to PyPI`。若改用 API token，删除 workflow 中 `id-token: write`
权限，并配置 `PYPI_TOKEN` secret 传给 `uv publish`。

## 安全说明

**连接数限制**：服务端 `max_connections` 防止恶意客户端通过大量控制连接耗尽资源。
建议生产部署时设置一个合理上限（如 `10`）。

**后端 / 监听白名单**：默认配置下，持有 token 的对端可请求本节点连接任意 IP 或
绑定任意端口，这在可信局域网内往往是期望行为。若部署于半信任网络，应配置
`allow_peer_backends` 将出站连接限定为必要的内网 CIDR，并用 `allow_peer_listens`
约束监听 IP，以防内网探测（SSRF）与端口滥用。

**传输加密**：token 在握手时以明文 JSON 传输。公网部署请务必启用 wss（配置
`tls.cert` / `tls.key`）；`tls_skip_verify = true` 仅适用于受控的自签证书环境，
生产环境应使用受信任 CA 签发的证书。

v1 暂不提供 mTLS、证书固定与配置热加载。
