mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-05-26 07:08:11 +00:00
278 lines
11 KiB
Markdown
278 lines
11 KiB
Markdown
<div align="center">
|
||
|
||
<img src="https://github.com/openlibrecommunity/material/blob/master/olcrtc.png" width="250" height="250">
|
||
|
||

|
||

|
||
|
||
</div>
|
||
|
||
|
||
|
||
# olcRTC - общее описание
|
||
|
||
`olcRTC` (OpenLibreCommunity RTC) - зашифрованный TCP-over-WebRTC туннель. Он маскирует трафик под обычное участие в WebRTC/SFU-сервисе: Jitsi Meet, Yandex Telemost или WbStream.
|
||
|
||
Проект: [github.com/openlibrecommunity/olcrtc](https://github.com/openlibrecommunity/olcrtc)
|
||
Лицензия: WTFPL
|
||
Статус: **Beta**
|
||
|
||
## Зачем это нужно
|
||
|
||
В сценариях, где прямой доступ к произвольному VPS / IP заблокирован, приходится переносить трафик через сервисы, которые уже доступны у пользователя. Для внешнего наблюдателя соединение выглядит как обычный WebRTC-звонок по разрешенному IP сервиса, а полезная нагрузка внутри дополнительно шифруется общим ключом `crypto.key`.
|
||
|
||
Базовая схема:
|
||
|
||
```text
|
||
приложение
|
||
-> SOCKS5 127.0.0.1:8808
|
||
-> olcrtc cnc
|
||
-> WebRTC/SFU сервис
|
||
-> olcrtc srv
|
||
-> интернет
|
||
```
|
||
|
||
## Как это работает
|
||
|
||
Клиентский режим `cnc` поднимает локальный SOCKS5. Браузер, curl, sing-box, olcbox или другое приложение подключается к нему как к обычному proxy.
|
||
|
||
Серверный режим `srv` подключается к той же комнате/сессии, принимает зашифрованный smux stream и от своего имени открывает TCP-соединения к целевым адресам.
|
||
|
||
Внутри туннеля:
|
||
|
||
```text
|
||
SOCKS CONNECT
|
||
-> smux stream
|
||
-> XChaCha20-Poly1305
|
||
-> transport
|
||
-> engine
|
||
-> WebRTC/SFU
|
||
```
|
||
|
||
## Режимы
|
||
|
||
| Режим | Назначение |
|
||
|---|---|
|
||
| `srv` | серверная сторона, принимает tunnel streams и делает TCP dial к целям |
|
||
| `cnc` | клиентская сторона, слушает локальный SOCKS5 |
|
||
| `gen` | создаёт Room ID для провайдеров, которые умеют создавать комнаты |
|
||
|
||
CLI принимает один YAML-файл:
|
||
|
||
```bash
|
||
olcrtc server.yaml
|
||
olcrtc client.yaml
|
||
```
|
||
|
||
## Auth Providers
|
||
|
||
`auth.provider` выбирает сервис и способ получения credentials.
|
||
|
||
| Provider | Engine | Комментарий |
|
||
|---|---|---|
|
||
| `jitsi` | `jitsi` | URL комнаты Jitsi, без отдельной регистрации |
|
||
| `telemost` | `goolom` | credentials через Yandex Telemost API, с отдельной регистрацией |
|
||
| `wbstream` | `livekit` | credentials через WbBStream API, с отдельной регистрацией |
|
||
| `none` | задаётся в `engine.name` | прямой engine-режим с `engine.url` и `engine.token`, с отдельной регистрацией |
|
||
|
||
Термин `carrier` ещё встречается во внутреннем API и логах как историческое имя для выбранного auth/provider пути. В YAML актуальное поле - `auth.provider`.
|
||
|
||
## Engines
|
||
|
||
`engine` - низкоуровневый протокол конкретного SFU/signaling:
|
||
|
||
| Engine | Пакет | Возможности |
|
||
|---|---|---|
|
||
| `livekit` | `internal/engine/livekit` | data packets/video tracks/LiveKit SDK |
|
||
| `goolom` | `internal/engine/goolom` | Telemost/Goolom signaling, publisher/subscriber PeerConnection |
|
||
| `jitsi` | `internal/engine/jitsi` | Jitsi MUC/Jingle/colibri-ws, datachannel/best-effort video |
|
||
|
||
`internal/engine/builtin` связывает `auth.provider` с нужным engine. Отдельного пакета `internal/carrier` в текущем проекте нет.
|
||
|
||
## Transports
|
||
|
||
`net.transport` определяет, как tunnel bytes помещаются в WebRTC primitive.
|
||
|
||
| Transport | Как передаёт данные | Основной сценарий |
|
||
|---|---|---|
|
||
| `datachannel` | нативный byte/data path engine | самый простой и быстрый путь, стабильно с Jitsi |
|
||
| `vp8channel` | KCP поверх VP8-like video frames | основной video-path для WB Stream и Telemost |
|
||
| `seichannel` | payload в H264 SEI NAL units, ACK/retry | fallback для WB Stream / Jitsi|
|
||
| `videochannel` | QR/tile кадры через ffmpeg, ACK/retry | экспериментальный визуальный транспорт |
|
||
|
||
Рекомендуемый старт: `jitsi + datachannel`. Альтернатива: `wbstream + vp8channel`.
|
||
|
||
## Шифрование и handshake
|
||
|
||
`internal/crypto` использует XChaCha20-Poly1305. Общий ключ задаётся как 64 hex-символа:
|
||
|
||
```bash
|
||
openssl rand -hex 32
|
||
```
|
||
|
||
Поверх зашифрованного `muxconn` запускается `smux`. Первый smux stream занят handshake и control protocol:
|
||
|
||
```text
|
||
CLIENT_HELLO -> SERVER_WELCOME
|
||
CONTROL_PING <-> CONTROL_PONG
|
||
```
|
||
|
||
Если control pong не приходит несколько раз подряд, runtime пересобирает smux-сессию или отдаёт управление failover supervisor.
|
||
|
||
## YAML
|
||
|
||
Минимальный сервер:
|
||
|
||
```yaml
|
||
mode: srv
|
||
auth:
|
||
provider: jitsi
|
||
room:
|
||
id: "https://meet.cryptopro.ru/REPLACE_ME_WITH_ROOM_ID"
|
||
crypto:
|
||
key: "REPLACE_ME_WITH_64_HEX_CHARS"
|
||
net:
|
||
transport: datachannel
|
||
dns: "8.8.8.8:53"
|
||
data: data
|
||
```
|
||
|
||
Минимальный клиент:
|
||
|
||
```yaml
|
||
mode: cnc
|
||
auth:
|
||
provider: jitsi
|
||
room:
|
||
id: "https://meet.cryptopro.ru/REPLACE_ME_WITH_ROOM_ID"
|
||
crypto:
|
||
key: "REPLACE_ME_WITH_64_HEX_CHARS"
|
||
net:
|
||
transport: datachannel
|
||
dns: "8.8.8.8:53"
|
||
socks:
|
||
host: "127.0.0.1"
|
||
port: 8808
|
||
data: data
|
||
```
|
||
|
||
Подробнее: [configuration.md](configuration.md), [settings.md](settings.md).
|
||
|
||
## Failover
|
||
|
||
`profiles[]` позволяет запускать несколько конфигураций по порядку. Например, сначала `wbstream + vp8channel`, потом `jitsi + datachannel`. Верхнеуровневые поля работают как defaults, профиль переопределяет только нужные части.
|
||
|
||
Активные smux streams при смене профиля не мигрируют. Новые подключения смогут подняться на следующем профиле.
|
||
|
||
## Структура репозитория
|
||
|
||
| Путь | Что внутри |
|
||
|---|---|
|
||
| `cmd/olcrtc` | CLI entrypoint |
|
||
| `cmd/olcrtc-cgo` | c-shared entrypoint |
|
||
| `pkg/olcrtc` | embeddable client/engine API |
|
||
| `pkg/olcrtc/tunnel` | embeddable server tunnel API |
|
||
| `mobile` | gomobile bindings для Android |
|
||
| `internal/config` | YAML parsing, `crypto.key_file` |
|
||
| `internal/app/session` | defaults, validation, routing в `srv`/`cnc`/`gen` |
|
||
| `internal/auth` | provider-specific credential flows |
|
||
| `internal/engine` | SFU/signaling implementations |
|
||
| `internal/transport` | datachannel/vp8/sei/video transports |
|
||
| `internal/server` | server-side smux, handshake, TCP dial |
|
||
| `internal/client` | SOCKS5 listener, client-side smux |
|
||
| `internal/control` | liveness ping/pong |
|
||
| `internal/supervisor` | failover profiles |
|
||
| `script` | интерактивные launchers и Docker entrypoint |
|
||
| `docs` | документация и примеры YAML |
|
||
|
||
## Сборка
|
||
|
||
```bash
|
||
go install github.com/magefile/mage@latest
|
||
|
||
mage build
|
||
mage cross
|
||
mage test
|
||
mage lint
|
||
mage mobile
|
||
mage docker
|
||
mage podman
|
||
```
|
||
|
||
Go версия в сборочных скриптах: `1.25`. Для `videochannel` нужен `ffmpeg`; для `codec: tile` требуется разрешение `1080x1080`.
|
||
|
||
## Public API
|
||
|
||
`pkg/olcrtc` возвращает `net.Conn`-подобный объект поверх auth/engine:
|
||
|
||
```go
|
||
sess, err := olcrtc.New(ctx, olcrtc.Config{
|
||
Auth: "jitsi",
|
||
RoomID: "https://meet.cryptopro.ru/myroom",
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
conn, err := sess.Dial(ctx)
|
||
```
|
||
|
||
`pkg/olcrtc/tunnel` встраивает серверную сторону и даёт hooks:
|
||
|
||
```go
|
||
srv := tunnel.New(tunnel.Config{
|
||
Transport: "datachannel",
|
||
Carrier: "jitsi",
|
||
RoomURL: "https://meet.cryptopro.ru/myroom",
|
||
KeyHex: "<64-char hex>",
|
||
DNSServer: "8.8.8.8:53",
|
||
})
|
||
err := srv.Run(ctx)
|
||
```
|
||
|
||
В этом API поле `Carrier` сохранено ради совместимости с существующими интеграциями; по смыслу это имя `auth.provider`.
|
||
|
||
## Mobile / Android
|
||
|
||
`mobile/mobile.go` предоставляет gomobile API:
|
||
|
||
- `SetProtector` для Android VPN `protect(fd)`;
|
||
- `SetTransport`, `SetDNS`, `SetVP8Options`, `SetLivenessOptions`;
|
||
- `Start`, `StartWithTransport`, `Stop`;
|
||
- `Check`/ping helpers для проверки доступности.
|
||
|
||
По умолчанию mobile-клиент использует `vp8channel`; `datachannel` тоже поддерживается.
|
||
|
||
## Тесты
|
||
|
||
```bash
|
||
go test -count=1 ./...
|
||
mage test
|
||
mage e2e
|
||
```
|
||
|
||
Real-provider E2E включаются через переменные:
|
||
|
||
```bash
|
||
E2E_CARRIERS=wbstream E2E_TRANSPORTS= vp8channel mage e2e
|
||
```
|
||
|
||
## Частые проблемы
|
||
|
||
| Симптом | Что проверить |
|
||
|---|---|
|
||
| `key required` или `invalid key` | на обеих сторонах одинаковый 64-символьный hex key |
|
||
| SOCKS5 не слушает | `mode: cnc`, `socks.host`, `socks.port`, логи клиента |
|
||
| Jitsi не соединяется без второго участника | сервер и клиент должны быть в одной комнате |
|
||
| WB Stream + datachannel не работает | в guest flow нет `canPublishData`; используй `vp8channel`, `seichannel` или `videochannel` |
|
||
| `seichannel ack timeout` | провайдер режет/не маршрутизирует video path; смени transport/provider |
|
||
| `ffmpeg` not found | установи ffmpeg или задай `ffmpeg: /path/to/ffmpeg` |
|
||
|
||
## Ссылки
|
||
|
||
- [Быстрый старт](fast.md)
|
||
- [Ручная сборка](manual.md)
|
||
- [Настройка YAML](configuration.md)
|
||
- [Матрица совместимости](settings.md)
|
||
- [URI формат](uri.md)
|
||
- [Формат подписки](sub.md)
|