OlcRTC: туннель через WebRTC-сервисы: различия между версиями

Материал из wolfram
Перейти к навигации Перейти к поиску
Новая страница: «= olcRTC: туннелирование SOCKS5-трафика через публичные WebRTC-сервисы = == Введение == === Что такое olcRTC === olcRTC — это инструмент для построения SOCKS5-туннеля поверх инфраструктуры публичных сервисов видеосвязи. В отличие от классических VPN-протоколов, которые работ...»
 
 
Строка 71: Строка 71:


=== Поток данных ===
=== Поток данных ===
<pre>
[[Файл:Olcrtc-data-flow.png|центр|мини|841x841пкс|Поток данных olcRTC: от клиентского приложения через SOCKS5, smux + ChaCha20-Poly1305, транспорт и WebRTC peer-connection в видеоконференции, до серверного outbound и целевого сайта]]
[Браузер / приложение клиента]
        │
        │  SOCKS5 на 127.0.0.1:1080
        ▼
[olcRTC client (cnc)]
        │
        │  smux мультиплексирование
        │  ChaCha20-Poly1305 шифрование
        ▼
[Транспорт: datachannel / vp8 / sei / video]
        │
        │  WebRTC (peer-connection через SFU)
        ▼
[Видеоконференция WB Stream / Telemost / Jazz]
        │
        ▲
        │  тот же канал, серверная сторона
        │
[Транспорт] ← [olcRTC server (srv)]
        │
        │  SOCKS5 (опционально, через локальный xray)
        ▼
[Outbound: прямой выход в интернет ИЛИ xray + WARP]
        │
        ▼
[Целевой сайт]
</pre>


=== Слои кода ===
=== Слои кода ===

Текущая версия от 07:56, 9 мая 2026

olcRTC: туннелирование SOCKS5-трафика через публичные WebRTC-сервисы

Введение

Что такое olcRTC

olcRTC — это инструмент для построения SOCKS5-туннеля поверх инфраструктуры публичных сервисов видеосвязи. В отличие от классических VPN-протоколов, которые работают через выделенные UDP/TCP-порты на собственном сервере, olcRTC использует уже существующие WebRTC-каналы общедоступных видеоконференц-сервисов в качестве несущей среды.

Поддерживаются три сервиса:

Carrier Сервис Адрес
wbstream WB Stream stream.wb.ru
telemost Yandex Telemost telemost.yandex.ru
jazz SaluteJazz salutejazz.ru

Сервер olcRTC выступает в роли участника виртуальной видеоконференции (с автоматически сгенерированным именем). Клиент подключается к той же конференции и через peer-connection обменивается с сервером данными — поверх этого канала идёт трафик SOCKS5-туннеля.

Когда применяется

  • Канал между клиентом и обычными VPN-серверами нестабилен или имеет высокий процент потерь.
  • Требуется передать данные через инфраструктуру, использующую WebRTC, а не выделенные транспортные порты.
  • Нужна альтернатива, которая работает поверх стандартного для пользовательских устройств WebRTC-стека (браузер, мобильное приложение).

Сравнение с другими методами туннелирования

Метод Транспорт Особенность
WireGuard UDP Прямое соединение с собственным сервером, минимальные накладные расходы
OpenVPN UDP/TCP Универсальная совместимость, выше накладные расходы
Shadowsocks TCP Шифрованный поток, маскировка под произвольный TCP
VLESS + REALITY TCP (TLS) TLS-handshake донора (стороннего сайта)
NaiveProxy TCP (HTTP/2) Использует Chrome-стек, неотличим от обычного HTTPS
Hysteria2 UDP (QUIC) QUIC поверх UDP с маскировкой под HTTPS
olcRTC WebRTC поверх QUIC/UDP Использует существующую инфраструктуру SFU публичного сервиса видеосвязи в качестве несущей

Принципы работы

Поток данных

Поток данных olcRTC: от клиентского приложения через SOCKS5, smux + ChaCha20-Poly1305, транспорт и WebRTC peer-connection в видеоконференции, до серверного outbound и целевого сайта

Слои кода

Кодовая база разделена на четыре независимых слоя с реестром расширений:

Слой Ответственность Реализации
Carrier Сессия в видеосервисе: вход в конференцию, регистрация участника, peer-connection через SFU wbstream, telemost, jazz
Link Поверх транспорта; в текущей версии только direct (passthrough) direct
Transport Способ кодирования байтов внутри WebRTC-канала datachannel, vp8channel, seichannel, videochannel
Application Сервер (приём входящих туннелей) или клиент (локальный SOCKS5) server, client

Транспорты

Транспорт Несущая в WebRTC Идея Скорость Пинг WB Stream Telemost Jazz
datachannel RTCDataChannel Прямые байты в датаканале Максимум Минимум
vp8channel VP8-видеотрек KCP-пакеты внутри валидных VP8-кадров Высокая Средний
seichannel H.264 SEI NAL Полезные данные в SEI-сообщениях видеопотока Низкая Низкий
videochannel Видеокадры QR-коды или тайлы Reed-Solomon в видеопотоке Минимум Максимум

Рекомендуемая универсальная связка: wbstream + vp8channel. Она используется в данной статье как основная конфигурация.

Шифрование

Параметр Значение
Алгоритм XChaCha20-Poly1305 (AEAD)
Длина ключа 32 байта (64 hex-символа)
Nonce 24 байта, случайный, на каждый фрейм, префиксится к шифротексту
Идентификация клиента Поле client_id в открытом JSON, сравнивается с серверным значением

Реальная криптографическая защита — это ключ. client_id — лишь дополнительная проверка от случайных подключений в той же конференции, не криптографическая.

Smux: мультиплексирование

Поверх единого WebRTC-канала работает xtaci/smux v2. Один физический канал содержит много логических TCP-стримов: каждый запрос SOCKS5 от приложения открывается как отдельный smux-стрим.

Архитектурное ограничение: один сервер — один клиент

Это важное свойство кода, которое необходимо понимать.

Один экземпляр сервера olcRTC обслуживает ровно одного клиента в момент времени. Это не упущение, а сознательное архитектурное решение.

Причины:

  • Сервер хранит одну переменную clientID и сравнивает с присланным значением (см. internal/server/server.go:399-401).
  • Сервер держит один link и одну smux-сессию, не словарь по идентификатору.
  • Провайдер carrier держит один RTCPeerConnection.
Сценарий Поведение
Один владелец, разные устройства, по очереди (выключил ноутбук, включил телефон) Работает с одной ссылкой
Один владелец, разные устройства, одновременно Устройства конкурируют за peer-connection, рвут друг друга
Несколько разных людей, одна ссылка на всех, одновременно То же — рвут друг друга
Несколько пользователей, у каждого своя ссылка Работает; требуется отдельный контейнер на каждого пользователя

Для предоставления доступа нескольким людям одновременно необходимо запустить несколько независимых контейнеров — каждый со своими room_id, key и client_id. В разделе «Управление несколькими пользователями» описан скрипт, который автоматизирует это.


Что должно быть на сервере перед установкой

Минимально необходимая конфигурация

Перед началом установки olcRTC сервер должен находиться в следующем состоянии:

Требование Зачем
1 Установлена операционная система Linux (Ubuntu 22.04+, Debian 12+, Fedora 38+ или сравнимая) Среда выполнения Docker
2 Доступ по SSH с правами root (либо аккаунт с sudo без пароля) Установка пакетов и управление контейнерами
3 Установлены Docker Engine 24.0+ и Docker Compose v2.20+ Запуск контейнера olcRTC
4 Установлена утилита git Скачивание исходного кода olcRTC с репозитория
5 Установлена утилита openssl Генерация ключа шифрования

Опциональная (но настоятельно рекомендуемая) конфигурация: egress через WARP

По умолчанию olcRTC сервер выходит в интернет напрямую с публичного IP вашей виртуальной машины. Все целевые сайты будут видеть именно этот адрес.

Чтобы выходной трафик уходил через Cloudflare WARP, требуется промежуточная цепочка через локальный SOCKS5-прокси. Самая распространённая реализация — панель 3x-ui (web-интерфейс над xray-core).

Что должно быть подготовлено в 3x-ui

Параметр Значение для нашей статьи Комментарий
1 Сама панель 3x-ui установлена и доступна Установка описана в отдельной статье (Установка 3x-ui); если её ещё нет — выполните установку до начала
2 Outbound в WARP активен и протестирован tag warp или аналогичный Должен быть настроен как WireGuard-outbound к Cloudflare WARP. Проверка: маршрут с outbound отдаёт IPv6 из диапазона 2a09:bac5::/29 или IPv4 104.28.x.x
3 Inbound типа mixed (SOCKS5 + HTTP), слушающий только на 127.0.0.1 порт 24365 Этот порт мы будем указывать в конфигурации olcRTC. Выберите любой свободный, мы используем 24365 как пример
4 Аутентификация (логин/пароль) на этом mixed-inbound отключена olcRTC-сервер умеет работать только с режимом NOAUTH; если включена авторизация, цепочка не будет работать
5 Routing-rule в xray: трафик с inbound mixed → outbound warp В 3x-ui это настраивается в разделе «Настройки xray → Маршрутизация»

Как проверить, что цепочка работает

Команда ниже выполняется на самом сервере (не на компьютере пользователя). Она запрашивает свой IP-адрес через локальный SOCKS5-прокси, который должен направить запрос в WARP.

Замените 24365 на ваш реальный номер порта mixed-inbound.

curl --socks5-hostname 127.0.0.1:24365 https://icanhazip.com

Ожидаемый результат: IP-адрес из диапазона Cloudflare WARP (например, 104.28.218.x или IPv6 2a09:bac5:...). Если возвращается публичный IP вашего сервера — значит цепочка не сработала и трафик идёт мимо WARP. В этом случае проверьте настройки routing в 3x-ui.

Если egress через WARP вам не нужен (готовы выходить с публичного IP сервера), можно пропустить этот раздел. В дальнейших шагах указания для случая «без WARP» помечены явно.


Параметры, которые нужно подготовить заранее

Перед началом установки заполните таблицу ниже своими значениями. На каждом шаге, где встретится плейсхолдер, мы будем ссылаться на эту таблицу.

Плейсхолдер Что это Где взять Пример
YOUR_SERVER_HOST Адрес сервера для подключения по SSH: либо публичный IP, либо имя из ~/.ssh/config, либо доменное имя У хостинг-провайдера в панели управления виртуальной машиной 198.51.100.42 или my-vps
YOUR_SSH_USER Имя пользователя SSH В письме от хостинга при создании VM. Чаще всего root root
YOUR_KEY_HEX 32-байтный ключ шифрования olcRTC в hex-формате (64 символа) Будет сгенерирован командой на шаге 4. Сохраните в надёжное место 1c3f8a2b...d4e5f6a7 (64 hex-символа)
YOUR_CLIENT_ID Идентификатор клиента; должен совпадать на сервере и в клиентском приложении Любое имя на латинице длиной 1–32 символа: буквы, цифры, _, - my-laptop
YOUR_ROOM_ID Идентификатор виртуальной видеоконференции Будет получен из логов сервера на шаге 7 019e07b8-e292-73ec-a40b-6a6e4957ce01
YOUR_SOCKS_PORT Порт mixed-inbound в 3x-ui для egress через WARP (опционально) См. раздел «Что должно быть на сервере перед установкой» 24365

Сохраните эту таблицу в текстовом файле — заполните по мере прохождения шагов. Особенно важно сохранить YOUR_KEY_HEX и YOUR_ROOM_ID — без них нельзя восстановить доступ к серверу после перезапуска.


Установка сервера: пошаговая инструкция

В этом разделе все команды выполняются на сервере (через SSH). Каждое пояснение — одна команда. Если команда выдала ошибку — остановитесь, не выполняйте следующую, и сверьтесь с предыдущим шагом.

Шаг 1. Подключение к серверу по SSH

Откройте терминал на своём компьютере. На Windows можно использовать встроенный PowerShell или Windows Terminal, на macOS — Terminal, на Linux — любой эмулятор терминала.

Замените YOUR_SSH_USER на имя пользователя из подготовленной таблицы (обычно root), а YOUR_SERVER_HOST — на адрес или имя сервера.

ssh YOUR_SSH_USER@YOUR_SERVER_HOST

Если подключение прошло успешно — вы увидите приглашение командной строки сервера. Все следующие команды выполняются именно в нём.

Шаг 2. Проверка наличия Docker

Прежде чем что-то ставить, проверим, есть ли Docker уже на сервере.

docker --version

Если команда вывела что-то вроде Docker version 24.0.5, build ... — Docker установлен, переходите к шагу 3.

Если команда выдала command not found — Docker нужно установить. Выберите один из подразделов ниже в зависимости от вашего дистрибутива.

2a. Установка Docker на Ubuntu / Debian

Обновите список пакетов.

apt update

Установите вспомогательные утилиты.

apt install -y ca-certificates curl gnupg

Подготовьте каталог для GPG-ключей репозитория Docker.

install -m 0755 -d /etc/apt/keyrings

Скачайте GPG-ключ официального репозитория Docker.

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Дайте права на чтение всем.

chmod a+r /etc/apt/keyrings/docker.gpg

Добавьте репозиторий Docker в систему.

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

Снова обновите список пакетов (теперь уже с новым репозиторием).

apt update

Установите Docker и плагины.

apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

2b. Установка Docker на Fedora / RHEL

Установите менеджер репозиториев.

dnf -y install dnf-plugins-core

Подключите репозиторий Docker.

dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo

Установите Docker.

dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Запустите и включите автозапуск Docker.

systemctl enable --now docker

2c. Проверка установки Docker (любая ОС)

Проверьте, что Docker установлен и работает.

docker --version

Должно вывести что-то вроде Docker version 24.0.5. Проверьте, что плагин Compose доступен.

docker compose version

Должно вывести что-то вроде Docker Compose version v2.20.2.

Если обе команды отработали без ошибок — переходите к шагу 3.

Шаг 3. Создание рабочего каталога

Создайте каталог, в котором будет лежать репозиторий olcRTC.

mkdir -p /opt/olcrtc

Перейдите в этот каталог.

cd /opt/olcrtc

Шаг 4. Скачивание исходного кода olcRTC

Скачайте репозиторий вместе с подмодулями (флаг --recurse-submodules обязателен — без него транспорт videochannel не соберётся).

git clone --depth 1 --recurse-submodules --branch master https://github.com/openlibrecommunity/olcrtc.git .

Точка в конце команды означает «скачать в текущий каталог», не создавая вложенной папки. Если вы пропустили шаг 3 (cd /opt/olcrtc), команда создаст репозиторий не в том месте. Проверьте, что скачивание прошло успешно.

ls /opt/olcrtc

В выводе должны быть файлы Dockerfile, go.mod, каталоги cmd, internal, script и другие.

Шаг 5. Генерация ключа шифрования

Сгенерируйте ключ командой openssl.

openssl rand -hex 32

Команда выведет строку из 64 hex-символов. Например:

1c3f8a2b3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f7a8b9c0d1e2

Скопируйте этот ключ и впишите его в свою таблицу подготовки как YOUR_KEY_HEX. Этот же ключ потом понадобится для настройки клиента.

Шаг 6. Создание конфигурационного файла .env

Файл .env хранит параметры запуска. Создадим его, подставив ваши значения.

Замените:

  • ВАШ_КЛЮЧ — на значение YOUR_KEY_HEX из шага 5.
  • my-laptop — на значение YOUR_CLIENT_ID из таблицы подготовки.

Если вы используете egress через WARP (раздел «Что должно быть на сервере перед установкой»), также замените 24365 на свой YOUR_SOCKS_PORT. Если не используете WARP — удалите две строки OLCRTC_SOCKS_PROXY=....

nano /opt/olcrtc/.env

В открывшемся редакторе вставьте следующее содержимое:

OLCRTC_CARRIER=wbstream
OLCRTC_TRANSPORT=vp8channel
OLCRTC_ROOM_ID=any
OLCRTC_CLIENT_ID=my-laptop
OLCRTC_KEY=ВАШ_КЛЮЧ
OLCRTC_DNS=1.1.1.1:53
OLCRTC_SOCKS_PROXY=127.0.0.1
OLCRTC_SOCKS_PROXY_PORT=24365
OLCRTC_VP8_FPS=60
OLCRTC_VP8_BATCH=64
OLCRTC_DEBUG=false

Сохраните файл: нажмите Ctrl+O, затем Enter, затем Ctrl+X. Защитите файл от чтения посторонними (он содержит ваш ключ).

chmod 600 /opt/olcrtc/.env

Проверьте, что значения подставились правильно.

cat /opt/olcrtc/.env

В строке OLCRTC_KEY=... должен быть ваш ключ, в строке OLCRTC_CLIENT_ID=... — ваше имя пользователя.

Шаг 7. Замена docker-compose.server.yml

Стандартный compose-файл из репозитория не использует режим host, который нужен для доступа к локальному xray. Заменим его на нашу версию.

Откройте файл для редактирования.

nano /opt/olcrtc/docker-compose.server.yml

Удалите всё содержимое (зажмите Ctrl+K до полной очистки) и вставьте следующее:

services:
  olcrtc-server:
    build:
      context: .
    image: olcrtc/server:local
    container_name: olcrtc-server
    restart: unless-stopped
    network_mode: host
    environment:
      OLCRTC_MODE: srv
      OLCRTC_CARRIER: "${OLCRTC_CARRIER:?set OLCRTC_CARRIER}"
      OLCRTC_TRANSPORT: "${OLCRTC_TRANSPORT:?set OLCRTC_TRANSPORT}"
      OLCRTC_ROOM_ID: "${OLCRTC_ROOM_ID:?set OLCRTC_ROOM_ID}"
      OLCRTC_CLIENT_ID: "${OLCRTC_CLIENT_ID:?set OLCRTC_CLIENT_ID}"
      OLCRTC_KEY: "${OLCRTC_KEY:?set OLCRTC_KEY}"
      OLCRTC_DNS: "${OLCRTC_DNS:-1.1.1.1:53}"
      OLCRTC_SOCKS_PROXY: "${OLCRTC_SOCKS_PROXY:-}"
      OLCRTC_SOCKS_PROXY_PORT: "${OLCRTC_SOCKS_PROXY_PORT:-1080}"
      OLCRTC_VP8_FPS: "${OLCRTC_VP8_FPS:-60}"
      OLCRTC_VP8_BATCH: "${OLCRTC_VP8_BATCH:-64}"
      OLCRTC_DEBUG: "${OLCRTC_DEBUG:-false}"
    volumes:
      - olcrtc-state:/var/lib/olcrtc
    init: true

volumes:
  olcrtc-state:

Сохраните файл: Ctrl+O, Enter, Ctrl+X.

Шаг 8. Сборка Docker-образа olcRTC

Перейдите в каталог проекта (если ещё не там).

cd /opt/olcrtc

Запустите сборку.

docker compose -f docker-compose.server.yml --env-file .env build

Сборка длится 1–3 минуты. Скачиваются зависимости Go и компилируется бинарник. Если в процессе возникает ошибка вида «context canceled» или «network unreachable» — повторите команду; первый прогон бывает прерывистым из-за нестабильности зеркал.

Когда сборка успешно завершится, в выводе появится строка Image olcrtc/server:local Built.

Шаг 9. Запуск контейнера

Запустите контейнер в фоновом режиме.

docker compose -f docker-compose.server.yml --env-file .env up -d

Подождите 15–20 секунд, чтобы сервер успел подключиться к WB Stream и создать виртуальную конференцию.

sleep 20

Посмотрите логи.

docker logs olcrtc-server

Ожидаемый вывод:

Connecting link via direct/vp8channel/wbstream...
WB Stream room created: 019e07b8-e292-73ec-a40b-6a6e4957ce01
To connect client use: -id 019e07b8-e292-73ec-a40b-6a6e4957ce01
Link connected

Скопируйте идентификатор после WB Stream room created: и впишите его в таблицу подготовки как YOUR_ROOM_ID. Без этого значения невозможно настроить клиента.

Шаг 10. Фиксация room_id в .env

Сейчас в файле .env стоит OLCRTC_ROOM_ID=any. Это значение означает «при каждом запуске создавать новую конференцию». Если оставить его — после следующего перезапуска контейнера все ссылки клиентов перестанут работать, потому что комната окажется новой.

Зафиксируйте полученный идентификатор.

Замените YOUR_ROOM_ID в команде ниже на скопированное значение из шага 9.

sed -i 's/^OLCRTC_ROOM_ID=any$/OLCRTC_ROOM_ID=YOUR_ROOM_ID/' /opt/olcrtc/.env

Перезапустите контейнер с новым значением.

docker compose -f /opt/olcrtc/docker-compose.server.yml --env-file /opt/olcrtc/.env up -d

Подождите 10 секунд и снова проверьте логи.

sleep 10 && docker logs --tail 5 olcrtc-server

Теперь в логе не должно быть строки WB Stream room created — должна быть только Connecting link via direct/vp8channel/wbstream... и Link connected. Это означает, что сервер заходит в существующую комнату, а не создаёт новую.

Шаг 11. Формирование ссылки для клиента

Ссылка имеет фиксированный текстовый формат:

olcrtc://CARRIER?TRANSPORT@ROOM_ID#KEY%CLIENT_ID$ПОДПИСЬ

Подставьте свои значения. Подпись — произвольная строка, которая отобразится в названии локации в клиентском приложении (она не влияет на работу).

Пример итоговой ссылки:

olcrtc://wbstream?vp8channel@019e07b8-e292-73ec-a40b-6a6e4957ce01#1c3f8a2b...d4e5f6a7%my-laptop$home laptop, vp8 over wbstream
Часть ссылки Откуда берётся
wbstream Из OLCRTC_CARRIER в .env
vp8channel Из OLCRTC_TRANSPORT в .env
019e07b8-... YOUR_ROOM_ID из шага 9
1c3f8a2b... YOUR_KEY_HEX из шага 5
my-laptop YOUR_CLIENT_ID из шага 6
Подпись после $ Любой описательный текст

Сохраните полученную ссылку в надёжное место. Её нужно будет ввести в клиентское приложение.

Шаг 12. Подключение клиента

12a. Olcbox (Android, рекомендуется для смартфона)

Скачайте APK из репозитория alananisimov/olcbox (раздел Releases) и установите его на телефон.

Откройте приложение. Перейдите в раздел Locations. Нажмите Add → Import from clipboard.

Перед нажатием убедитесь, что в буфере обмена телефона лежит ваша ссылка olcrtc://... из шага 11. Скопируйте её любым способом — например, отправив самому себе в Telegram-секретный чат.

После импорта откройте созданную локацию и проверьте поля:

  • Carrier должен быть wbstream.
  • Transport должен быть vp8channel.
  • VP8 FPS — установите 60 вручную (при импорте URI этот параметр сбрасывается на дефолт приложения).
  • VP8 Batch — установите 64.

Включите тумблер VPN.

12b. CLI-клиент (Linux, macOS, Windows)

Скачайте бинарник olcrtc для своей операционной системы из релизов репозитория или соберите из исходников.

Запустите туннель. Замените все плейсхолдеры YOUR_* на свои значения из таблицы подготовки.

./olcrtc -mode cnc -carrier wbstream -transport vp8channel -id YOUR_ROOM_ID -client-id YOUR_CLIENT_ID -key YOUR_KEY_HEX -link direct -data data -dns 1.1.1.1:53 -vp8-fps 60 -vp8-batch 64 -socks-host 127.0.0.1 -socks-port 1080

После запуска клиент поднимет локальный SOCKS5-прокси на 127.0.0.1:1080. Проверьте, что туннель работает.

curl --socks5-hostname 127.0.0.1:1080 https://icanhazip.com

Если egress настроен через WARP — должен вернуть IP Cloudflare WARP. Если не настроен — IP вашего сервера. В обоих случаях это не должен быть IP вашего домашнего интернета.


Команды управления одним сервером

Все команды выполняются на сервере по SSH.

Посмотреть логи в реальном времени (для выхода нажать Ctrl+C).

docker logs -f olcrtc-server

Посмотреть статус контейнера.

docker ps --filter name=olcrtc-server

Перезапустить сервер (например, после изменения .env).

docker compose -f /opt/olcrtc/docker-compose.server.yml --env-file /opt/olcrtc/.env up -d

Остановить сервер.

docker compose -f /opt/olcrtc/docker-compose.server.yml --env-file /opt/olcrtc/.env down

Обновить исходный код до свежей версии master.

cd /opt/olcrtc && git pull --recurse-submodules

Пересобрать образ после обновления кода.

docker compose -f /opt/olcrtc/docker-compose.server.yml --env-file /opt/olcrtc/.env build

Применить новый образ.

docker compose -f /opt/olcrtc/docker-compose.server.yml --env-file /opt/olcrtc/.env up -d

Управление несколькими пользователями: скрипт olcrtc-users

Зачем он нужен

Из-за архитектурного ограничения «один сервер — один клиент» (раздел «Принципы работы → Архитектурное ограничение») для каждого пользователя нужен отдельный контейнер со своим room_id, ключом и client_id. Чтобы не повторять шаги установки руками каждый раз, написан bash-скрипт olcrtc-users, который автоматизирует:

  • Создание пользователя: генерация ключа, создание контейнера с уникальным client_id, ловля room_id из логов, формирование готовой ссылки.
  • Удаление пользователя: остановка и удаление контейнера, удаление volume и каталога.
  • Просмотр списка и информации: таблица всех пользователей с возможностью получить ссылку по выбору номера.

Что получает каждый пользователь

Атрибут Значение
Контейнер olcrtc-user-<имя>
Каталог конфигурации /opt/olcrtc-users/<имя>/
Файл с переменными /opt/olcrtc-users/<имя>/.env
Compose project olcrtc-user-<имя>
Docker volume olcrtc-user-<имя>_state
Виртуальная конференция Уникальная, создаётся при первом запуске
Ключ шифрования Уникальный, 32 случайных байта
Ссылка вида olcrtc://... Уникальная

Структура каталогов

/opt/olcrtc/                           # репозиторий + Dockerfile (build context)
└── (код, не меняется после установки)

/opt/olcrtc-users/                     # каталог менеджера пользователей
├── shared/
│   ├── docker-compose.user.yml        # один шаблон compose для всех пользователей
│   └── defaults.env                   # значения по умолчанию для новых пользователей
├── alice/
│   └── .env                           # личные данные пользователя alice
├── bob/
│   └── .env
└── carol/
    └── .env

/usr/local/bin/olcrtc-users            # сам скрипт-меню

Установка скрипта

Все команды выполняются на сервере по SSH.

Шаг 1. Создание общего каталога

Создайте корневой каталог для пользователей.

mkdir -p /opt/olcrtc-users/shared

Защитите каталог.

chmod 700 /opt/olcrtc-users

Шаг 2. Создание общего шаблона docker-compose

Откройте файл для редактирования.

nano /opt/olcrtc-users/shared/docker-compose.user.yml

Вставьте следующее содержимое.

services:
  olcrtc:
    image: olcrtc/server:local
    container_name: "olcrtc-user-${OLCRTC_CLIENT_ID}"
    restart: unless-stopped
    network_mode: host
    environment:
      OLCRTC_MODE: srv
      OLCRTC_CARRIER: "${OLCRTC_CARRIER}"
      OLCRTC_TRANSPORT: "${OLCRTC_TRANSPORT}"
      OLCRTC_ROOM_ID: "${OLCRTC_ROOM_ID}"
      OLCRTC_CLIENT_ID: "${OLCRTC_CLIENT_ID}"
      OLCRTC_KEY: "${OLCRTC_KEY}"
      OLCRTC_DNS: "${OLCRTC_DNS}"
      OLCRTC_SOCKS_PROXY: "${OLCRTC_SOCKS_PROXY}"
      OLCRTC_SOCKS_PROXY_PORT: "${OLCRTC_SOCKS_PROXY_PORT}"
      OLCRTC_VP8_FPS: "${OLCRTC_VP8_FPS}"
      OLCRTC_VP8_BATCH: "${OLCRTC_VP8_BATCH}"
    volumes:
      - state:/var/lib/olcrtc
    init: true

volumes:
  state:

Сохраните: Ctrl+O, Enter, Ctrl+X.

Шаг 3. Создание файла значений по умолчанию

Откройте файл для редактирования.

nano /opt/olcrtc-users/shared/defaults.env

Вставьте следующее (если используете egress через WARP — оставьте порт 24365 или замените на свой YOUR_SOCKS_PORT).

OLCRTC_CARRIER=wbstream
OLCRTC_TRANSPORT=vp8channel
OLCRTC_DNS=1.1.1.1:53
OLCRTC_SOCKS_PROXY=127.0.0.1
OLCRTC_SOCKS_PROXY_PORT=24365
OLCRTC_VP8_FPS=60
OLCRTC_VP8_BATCH=64

Сохраните файл: Ctrl+O, Enter, Ctrl+X. Защитите файл.

chmod 600 /opt/olcrtc-users/shared/defaults.env

Если egress через WARP вам не нужен, выполните дополнительную команду — она очистит поле SOCKS-прокси.

sed -i 's|OLCRTC_SOCKS_PROXY=127.0.0.1|OLCRTC_SOCKS_PROXY=|' /opt/olcrtc-users/shared/defaults.env

Шаг 4. Создание самого скрипта

Откройте файл скрипта в редакторе.

nano /usr/local/bin/olcrtc-users

Вставьте полное содержимое скрипта (исходный код целиком — в спойлере «Исходный код скрипта olcrtc-users» в конце статьи).

Сохраните файл: Ctrl+O, Enter, Ctrl+X.

Сделайте файл исполняемым.

chmod 0755 /usr/local/bin/olcrtc-users

Проверьте корректность синтаксиса.

bash -n /usr/local/bin/olcrtc-users && echo OK

Если в выводе OK — скрипт установлен правильно.

Использование скрипта

Запустите.

olcrtc-users

Появится меню:

=== olcrtc users ===
  1) Список пользователей (выбор → ссылка)
  2) Создать пользователя
  3) Удалить пользователя
  0) Выход
> _

Пункт 1: список пользователей

После выбора отобразится таблица:

    #  name                           status
  ---  ------------------------------ ------------
    1) alice                          running
    2) bob                            running
    3) carol                          exited

Введите номер пользователя и нажмите Enter — появится полная карточка с готовой ссылкой olcrtc://...:

name  : alice status  : running carrier  : wbstream transport  : vp8channel room_id  : 019e07b8-e292-73ec-a40b-6a6e4957ce01 client_id  : alice key  : 1c3f8a2b...d4e5f6a7

URI for olcbox: olcrtc://wbstream?vp8channel@019e07b8-e292-73ec-a40b-6a6e4957ce01#1c3f8a2b...d4e5f6a7%alice$alice

Чтобы пропустить выбор и вернуться в меню — нажмите Enter без ввода номера.

Пункт 2: создание пользователя

Скрипт спросит имя.

Имя пользователя:

Введите имя на латинице (буквы, цифры, _, -, длина 1–32 символа), например alice. Затем спросит, как получить ключ.

Ключ шифрования: [a]uto / [m]anual (по умолчанию a):

Введите a (или просто нажмите Enter) — скрипт сгенерирует ключ автоматически.

Если хотите ввести ключ вручную — введите m и затем 64-символьный hex-ключ.

После этого скрипт сам:

  • создаст каталог пользователя;
  • запустит контейнер;
  • подождёт создания виртуальной конференции;
  • запишет полученный room_id в .env;
  • перезапустит контейнер с фиксированным room_id;
  • выведет готовую карточку с ссылкой.

Пункт 3: удаление пользователя

Скрипт покажет список.

    1) alice
    2) bob
    3) carol

Введите номер удаляемого пользователя, нажмите Enter. Скрипт спросит подтверждение.

Удалить пользователя "carol"? (y/N):

Введите y и нажмите Enter. Если введёте что-то другое или просто Enter — операция отменится.

После подтверждения:

  • контейнер будет остановлен и удалён;
  • docker volume будет удалён;
  • каталог пользователя будет удалён;
  • ссылка olcrtc://... у этого пользователя перестанет работать.

Исходный код скрипта olcrtc-users

Развернуть полный исходный код и подробное объяснение архитектуры скрипта

Полный исходный код

#!/usr/bin/env bash
# olcrtc-users — управление пользователями olcrtc через docker compose.
# UI: 3 пункта (список / создать / удалить), под капотом — отдельный контейнер на пользователя.

set -euo pipefail

USERS_DIR=/opt/olcrtc-users
SHARED=$USERS_DIR/shared
COMPOSE=$SHARED/docker-compose.user.yml
DEFAULTS=$SHARED/defaults.env

if [ ! -f "$COMPOSE" ] || [ ! -f "$DEFAULTS" ]; then
    echo "[X] $SHARED is not initialized. Aborting." >&2
    exit 1
fi

# ----- helpers -----
list_users() {
    find "$USERS_DIR" -mindepth 1 -maxdepth 1 -type d ! -name shared -printf "%f\n" | sort
}

container_status() {
    docker inspect -f "{{.State.Status}}" "olcrtc-user-$1" 2>/dev/null || echo "missing"
}

read_env() {
    local f=$1 k=$2
    grep -E "^${k}=" "$f" | cut -d= -f2-
}

build_uri() {
    local f=$1
    local carrier transport room key cid
    carrier=$(read_env "$f" OLCRTC_CARRIER)
    transport=$(read_env "$f" OLCRTC_TRANSPORT)
    room=$(read_env "$f" OLCRTC_ROOM_ID)
    key=$(read_env "$f" OLCRTC_KEY)
    cid=$(read_env "$f" OLCRTC_CLIENT_ID)
    printf "olcrtc://%s?%s@%s#%s%%%s\$%s\n" \
        "$carrier" "$transport" "$room" "$key" "$cid" "$cid"
}

show_user() {
    local user=$1
    local f=$USERS_DIR/$user/.env
    local status; status=$(container_status "$user")
    echo "name        : $user"
    echo "status      : $status"
    echo "carrier     : $(read_env "$f" OLCRTC_CARRIER)"
    echo "transport   : $(read_env "$f" OLCRTC_TRANSPORT)"
    echo "room_id     : $(read_env "$f" OLCRTC_ROOM_ID)"
    echo "client_id   : $(read_env "$f" OLCRTC_CLIENT_ID)"
    echo "key         : $(read_env "$f" OLCRTC_KEY)"
    echo
    echo "URI for olcbox:"
    build_uri "$f"
}

valid_name() { [[ "$1" =~ ^[a-zA-Z0-9_-]{1,32}$ ]]; }

compose_up() {
    local user=$1
    docker compose -f "$COMPOSE" --env-file "$USERS_DIR/$user/.env" \
        -p "olcrtc-user-$user" up -d
}

compose_down() {
    local user=$1
    docker compose -f "$COMPOSE" --env-file "$USERS_DIR/$user/.env" \
        -p "olcrtc-user-$user" down -v
}

# ----- menu actions -----
action_list() {
    mapfile -t users < <(list_users)
    if [ ${#users[@]} -eq 0 ]; then
        echo "(пользователей нет)"
        return
    fi
    printf "  %3s  %-30s %-12s\n" "#" "name" "status"
    printf "  %3s  %-30s %-12s\n" "---" "------------------------------" "------------"
    local i
    for i in "${!users[@]}"; do
        printf "  %3d) %-30s %-12s\n" \
            "$((i+1))" "${users[$i]}" "$(container_status "${users[$i]}")"
    done
    echo
    read -rp "Введите номер чтобы показать ссылку (Enter — пропустить): " pick
    [ -z "$pick" ] && return
    if ! [[ "$pick" =~ ^[0-9]+$ ]] || [ "$pick" -lt 1 ] || [ "$pick" -gt "${#users[@]}" ]; then
        echo "(неверный номер)"
        return
    fi
    echo
    show_user "${users[$((pick-1))]}"
}

action_new() {
    read -rp "Имя пользователя: " name
    if ! valid_name "$name"; then
        echo "(имя должно быть 1..32 символа, только a-z A-Z 0-9 _ -)"
        return
    fi
    if [ -d "$USERS_DIR/$name" ]; then
        echo "(пользователь \"$name\" уже существует)"
        return
    fi

    local key
    read -rp "Ключ шифрования: [a]uto / [m]anual (по умолчанию a): " mode
    case "${mode:-a}" in
        m|M)
            read -rp "Введите 64-символьный hex-ключ: " key
            if ! [[ "$key" =~ ^[0-9a-fA-F]{64}$ ]]; then
                echo "(не выглядит как 64-hex)"
                return
            fi
            ;;
        *)
            key=$(openssl rand -hex 32)
            echo "[*] сгенерирован ключ: $key"
            ;;
    esac

    mkdir -p "$USERS_DIR/$name"
    # shellcheck disable=SC1090
    set -a; source "$DEFAULTS"; set +a
    cat > "$USERS_DIR/$name/.env" <<EOF
OLCRTC_CARRIER=$OLCRTC_CARRIER
OLCRTC_TRANSPORT=$OLCRTC_TRANSPORT
OLCRTC_ROOM_ID=any
OLCRTC_CLIENT_ID=$name
OLCRTC_KEY=$key
OLCRTC_DNS=$OLCRTC_DNS
OLCRTC_SOCKS_PROXY=$OLCRTC_SOCKS_PROXY
OLCRTC_SOCKS_PROXY_PORT=$OLCRTC_SOCKS_PROXY_PORT
OLCRTC_VP8_FPS=$OLCRTC_VP8_FPS
OLCRTC_VP8_BATCH=$OLCRTC_VP8_BATCH
EOF
    chmod 600 "$USERS_DIR/$name/.env"

    echo "[*] стартую контейнер..."
    compose_up "$name" >/dev/null
    echo "[*] жду создания комнаты WB Stream..."
    local room=""
    local i
    for i in $(seq 1 30); do
        sleep 2
        room=$(docker logs "olcrtc-user-$name" 2>&1 \
            | grep -oE "WB Stream room created: [0-9a-f-]+" \
            | head -1 | awk "{print \$NF}")
        [ -n "$room" ] && break
    done
    if [ -z "$room" ]; then
        echo "[!] не удалось получить room_id. Логи:"
        docker logs --tail 30 "olcrtc-user-$name"
        return
    fi
    echo "[+] комната создана: $room"
    sed -i "s/^OLCRTC_ROOM_ID=any\$/OLCRTC_ROOM_ID=$room/" "$USERS_DIR/$name/.env"
    compose_up "$name" >/dev/null
    sleep 3
    echo
    show_user "$name"
}

action_delete() {
    mapfile -t users < <(list_users)
    if [ ${#users[@]} -eq 0 ]; then
        echo "(пользователей нет)"
        return
    fi
    local i
    for i in "${!users[@]}"; do
        printf "  %3d) %s\n" "$((i+1))" "${users[$i]}"
    done
    echo
    read -rp "Номер удаляемого: " pick
    if ! [[ "$pick" =~ ^[0-9]+$ ]] || [ "$pick" -lt 1 ] || [ "$pick" -gt "${#users[@]}" ]; then
        echo "(неверный номер)"
        return
    fi
    local user=${users[$((pick-1))]}
    read -rp "Удалить пользователя \"$user\"? (y/N): " conf
    if ! [[ "$conf" =~ ^[yY]$ ]]; then
        echo "(отменено)"
        return
    fi
    compose_down "$user" >/dev/null 2>&1 || true
    rm -rf "${USERS_DIR:?}/$user"
    echo "[+] пользователь $user удалён"
}

# ----- main -----
while true; do
    echo
    echo "=== olcrtc users ==="
    echo "  1) Список пользователей (выбор → ссылка)"
    echo "  2) Создать пользователя"
    echo "  3) Удалить пользователя"
    echo "  0) Выход"
    read -rp "> " choice
    case "$choice" in
        1) action_list ;;
        2) action_new ;;
        3) action_delete ;;
        0|q|Q|"") exit 0 ;;
        *) echo "(неверный выбор)" ;;
    esac
done

Устройство скрипта

Общий принцип

Скрипт — это интерактивная обёртка над docker compose. Никакой собственной бизнес-логики у него нет — все действия сводятся к запуску и остановке контейнеров с разными параметрами.

Каждый пользователь — это отдельный compose-проект с уникальным именем olcrtc-user-<имя>. Compose-файл единый для всех (shared/docker-compose.user.yml); различия задаются через --env-file, который указывает на личный .env пользователя.

Защитные механизмы

  • set -euo pipefail — прерывание при любой ошибке, защита от использования несуществующих переменных, корректная обработка пайпов.
  • Проверка наличия shared/ в самом начале — скрипт не запустится в неинициализированной системе.
  • Валидация имени по регулярному выражению ^[a-zA-Z0-9_-]{1,32}$ — защита от инъекций в имена контейнеров и путей файловой системы.
  • Валидация ключа по регулярному выражению ^[0-9a-fA-F]{64}$ — защита от неправильного формата при ручном вводе.
  • Подтверждение (y/N) при удалении — защита от случайного удаления.
  • Конструкция ${USERS_DIR:?} в rm -rf — защита от случайного rm -rf /, если переменная окажется пустой.

Хелперы

Функция Назначение
list_users() Возвращает список всех пользователей. Использует find с фильтром -mindepth 1 -maxdepth 1 -type d и исключает каталог shared.
container_status() Опрашивает Docker через docker inspect -f Шаблон:.State.Status. При отсутствии контейнера возвращает строку missing.
read_env() Читает значение переменной из .env через grep + cut. Не использует source, чтобы избежать побочных эффектов от загрузки переменных.
build_uri() Конкатенирует значения переменных в URI olcrtc://CARRIER?TRANSPORT@ROOM#KEY%CID$MIMO через printf.
show_user() Полная карточка пользователя: статус, все поля, готовый URI.
valid_name() Проверка имени пользователя по регулярному выражению.
compose_up() / compose_down() Запуск и остановка контейнера через docker compose с правильными флагами -f, --env-file, -p.

Действие «Список пользователей»

  1. Получает массив пользователей в переменной users через mapfile.
  2. Печатает заголовок таблицы.
  3. Для каждого пользователя печатает номер, имя и статус контейнера.
  4. Спрашивает номер. Пустой ввод — выход. Невалидный номер — сообщение об ошибке.
  5. При корректном выборе вызывает show_user.

Действие «Создать пользователя»

  1. Запрашивает имя, валидирует.
  2. Проверяет, что каталог $USERS_DIR/$name ещё не существует.
  3. Запрашивает режим ключа: a (auto, по умолчанию) или m (manual).
  4. При auto вызывает openssl rand -hex 32; при manual запрашивает и валидирует ввод.
  5. Создаёт каталог пользователя.
  6. Через set -a; source $DEFAULTS; set +a подгружает дефолты в окружение.
  7. Записывает .env пользователя через here-doc.
  8. Меняет права на .env на 0600 (только владелец).
  9. Запускает контейнер через compose_up.
  10. В цикле до 60 секунд (30 итераций × 2 сек) парсит логи контейнера регулярным выражением WB Stream room created: [0-9a-f-]+.
  11. При получении room_id обновляет .env через sed -i.
  12. Перезапускает контейнер с фиксированным room_id.
  13. Выводит карточку пользователя через show_user.

Действие «Удалить пользователя»

  1. Получает массив пользователей.
  2. Печатает пронумерованный список.
  3. Запрашивает номер, валидирует.
  4. Запрашивает подтверждение (y/N).
  5. Вызывает compose_down с флагом -v — это удалит и docker volume.
  6. Удаляет каталог пользователя через rm -rf.

Главный цикл

Бесконечный цикл с case-разбором ввода:

  • 1 → действие «Список»
  • 2 → действие «Создать»
  • 3 → действие «Удалить»
  • 0, q, Q, пустой ввод (Enter, Ctrl+D) → выход.
  • Любой другой ввод — сообщение «неверный выбор» и продолжение цикла.

Возможные расширения

Скрипт намеренно простой и легко расширяется. Возможные улучшения:

Что добавить Как
Перезапуск отдельного пользователя Новый пункт меню → compose_down + compose_up
Просмотр логов пользователя docker logs --tail 50 olcrtc-user-$name
Экспорт всех URI в файл подписки Цикл по list_users, на каждом — build_uri; добавить заголовок согласно docs/sub.md upstream-репозитория
Автоматическое обновление образа git -C /opt/olcrtc pull --recurse-submodules и docker compose build, затем перезапуск всех пользователей
HTTPS-эндпоинт /sub для раздачи конфигов Reverse proxy (caddy / nginx) на статический файл, который генерируется крон-задачей из build_uri

FAQ

Почему «один сервер ↔ один клиент»?

Это архитектурное свойство кода: сервер хранит одну сессию smux и одно поле clientID, не таблицу пиров. Изменение этой модели потребовало бы существенной переработки internal/server/server.go и поддержки множественных peer-connection в каждом провайдере.

Почему рекомендуется vp8channel, а не datachannel?

datachannel быстрее и имеет минимальный пинг, но: Telemost его не поддерживает, а Jazz может ограничивать IP-адреса серверов, использующих этот тип канала. vp8channel работает со всеми тремя сервисами и подходит для большинства сценариев.

Что такое «ограничение IP» со стороны carrier?

Некоторые carrier-сервисы используют автоматические эвристики против ботов. После срабатывания таких эвристик подключения с конкретного IP могут перестать пропускаться в виртуальные конференции. Лечится сменой IP сервера или ожиданием снятия ограничения. На момент написания статьи WB Stream таких эвристик не применяет.

Сколько контейнеров можно запустить на одной виртуальной машине?

В простое каждый контейнер потребляет приблизительно 10–15 МБ RAM и около 0% CPU. На VM с 4 GiB RAM реалистично запустить 50–100 пользователей. Узкими местами обычно становятся пропускная способность сетевого канала и допустимое количество одновременных WebRTC-сессий со стороны carrier-сервиса.

Можно ли использовать одну ссылку с нескольких устройств?

Технически — да, но не одновременно. Если включить туннель на двух устройствах с одной ссылкой, они будут конкурировать за один peer-connection и периодически разрывать друг друга. Для параллельной работы нужны разные пользователи (разные ссылки).

Виден ли реальный видеосигнал участникам конференции?

В транспортах vp8channel и seichannel поверх WebRTC отправляется минимально валидный видеопоток, в который встроены полезные данные. Внешне это выглядит как очень тихая «моностатичная» камера. В videochannel отправляется реальный видеоряд (QR-коды или тайлы) — формально это видимая «картинка». Если в виртуальной конференции, кроме сервера и клиента, никого нет — это никак не проявляется.

Что делать, если конкретный transport перестал работать?

Если carrier изменил проверки SFU и какой-то transport стал ненадёжен — переключитесь на другой transport (seichannel или videochannel) или другой carrier. Меняется в defaults.env для новых пользователей и в личном .env для существующих (с последующим перезапуском контейнера).

Можно ли совместить olcRTC с другими методами?

Да. С точки зрения клиентского приложения olcRTC — это локальный SOCKS5-прокси. Его можно использовать как один из outbound в xray, sing-box и подобных инструментах. На стороне сервера выходной трафик может быть направлен через xray + WARP (опциональный шаг 11 раздела установки) для дополнительной анонимизации.


Полезные ссылки

  • Репозиторий olcrtc на GitHub — основной upstream проекта.
  • Olcbox — мультиплатформенный клиент с UI (Android, macOS, Windows, Linux).
  • docs/uri.md — официальное описание формата URI.
  • docs/sub.md — формат файла подписки для нескольких локаций.
  • docs/settings.md — матрица совместимости carrier × transport, описание всех флагов.
  • 3x-ui — панель управления xray-core (для настройки egress через WARP).