Files
harbor-net/README.md
T
dokril 0b39211fbd
Build and Deploy Gateway / build-and-push (push) Successful in 22s
Build and Deploy Gateway / deploy (push) Successful in 7s
Refactor Harbor client connection flow
2026-08-17 12:19:26 +03:00

371 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Harbor
Harbor помогает пользоваться несколькими VPN-подписками дома и на Mac без ручной настройки `sing-box`.
Проект работает в двух режимах:
| Режим | Где работает | Для чего нужен |
| --- | --- | --- |
| **Harbor Gateway** | На отдельной Linux-машине | Проводит через VPN весь интернет-трафик домашних устройств или работает как общий HTTP/SOCKS5-прокси |
| **Harbor Connect** | На macOS | Даёт приложениям на Mac локальный HTTP/SOCKS5-прокси |
Основной экран Connect и Gateway всегда показывает фактически применённые подписку и сервер. Управление подписками открывается отдельной верхней кнопкой в правой панели; Home, «Устройства» и «Диагностика» Gateway доступны и без подписки.
## Что понадобится
- ссылка на подписку от VPN-провайдера, если Harbor должен направлять трафик через VPN;
- Docker с командой `docker compose`;
- для ручной установки Gateway — Git;
- для Gateway — Linux-машина в одной локальной сети с устройствами;
- для Connect — Mac с запущенным Docker Desktop.
Harbor не является VPN-провайдером и не создаёт подписки самостоятельно.
## Что выбрать
Используйте **Harbor Connect**, если VPN нужен только приложениям на одном Mac.
Используйте **Harbor Gateway**, если нужно подключить телевизор, телефон, игровую приставку или сразу несколько устройств. Устройства можно направить через Gateway целиком либо настроить в отдельных приложениях общий прокси.
Оба режима можно использовать вместе. Дома Connect автоматически распознаёт настроенный Harbor Gateway и не запускает второй VPN-маршрут. В другой сети Connect возвращается к локальному VPN.
## Установка Harbor Gateway
### 1. Скачайте проект
```bash
git clone https://git.dokops.ru/dokril/vpn-proxy.git
cd vpn-proxy
```
### 2. Создайте настройки
```bash
cp .env.example .env
```
Стандартные значения подходят для обычной домашней сети. При необходимости откройте `.env` в текстовом редакторе и измените порты.
### 3. Запустите Gateway
```bash
docker compose -f docker-compose.gateway.yml up -d --build
```
Откройте в браузере:
```text
http://АДРЕС-GATEWAY:3456
```
Например, если Linux-машина имеет адрес `192.168.1.20`, интерфейс будет доступен по адресу `http://192.168.1.20:3456`.
### 4. При необходимости добавьте подписку
1. Нажмите «Подписки» — верхнюю кнопку в правой панели Gateway.
2. Нажмите «Добавить подписку», задайте понятное имя и вставьте ссылку VPN-провайдера.
3. Выберите сервер внутри добавленной группы.
4. При нескольких группах выберите нужную действием «Сделать активной».
5. Включите VPN.
После подключения Harbor покажет два варианта использования:
- **Gateway** — укажите IP-адрес Linux-машины как основной шлюз устройства. Через VPN пойдёт весь его интернет-трафик;
- **Gateway Proxy** — укажите адрес Linux-машины и порт `8080` в приложении. Поддерживаются HTTP и SOCKS5 на одном порту.
Приватные и локальные адреса не отправляются в VPN, поэтому устройства сохраняют доступ к домашней сети. Общий прокси по умолчанию принимает подключения только из приватных сетей.
### Устройства Gateway
Откройте «Устройства» в правой панели Gateway — подписка для просмотра списка не требуется. Harbor раз в 15 секунд читает локальную таблицу соседей и показывает каждое устройство одной компактной строкой: заданное название, hostname или IP, последний контакт, выбранный график трафика и иконку применённого маршрута. По умолчанию график показывает приблизительный выход `VPN`/`Direct`; переключатель `Вход` возвращает накопленную разбивку `Gateway`/`Прокси`. Наведите курсор на имя или переведите на него фокус, чтобы открыть IP, MAC и доступный hostname; нажатие на значение копирует его. Hostname определяется через локальное обратное разрешение имён и может отсутствовать, если сеть его не публикует. Технические interface и manufacturer продолжают храниться для идентификации, но не занимают место в строке. Список разделён на «Закреплённые», «Остальные» и «Фоновые»: последняя группа сохраняется между перезапусками, показывает только identity/presence и кнопку возврата без графика, traffic и route controls. Название, закрепление, фоновое положение и накопленные totals сохраняются в volume Gateway, пока устройство остаётся в inventory.
Красная кнопка `Сбросить данные` после отдельного подтверждения обнуляет вход и выход всех устройств и начинает считать их заново. Общий график скорости на Home и уже сохранённая история Prometheus/Grafana не очищаются: входной counter выглядит для Prometheus как стандартный reset, а для выхода Harbor сохраняет только baseline отображения и не изменяет raw dataplane counters.
У однозначно распознанного устройства маршрут можно переключить последней иконкой между `VPN` и `Напрямую` независимо от закрепления; точное значение и следующее действие показаны в tooltip. `VPN` означает обработку через sing-box и правила Gateway: например, включённое локальное доменное правило всё равно может выбрать прямой выход внутри sing-box. `Напрямую` полностью обходит sing-box на уровне iptables. Traffic totals учитываются в обоих режимах. Если правило не удалось применить, Harbor сохраняет выбранный режим и отдельно показывает последний фактически применённый маршрут.
Список приблизительный: имя и пользовательские настройки привязаны к MAC и сохраняются при обычной смене IP, но новый private/randomized MAC считается новым устройством — переносить имя по одному только DHCP-адресу небезопасно. Запись автоматически удаляется после 30 дней без подтверждённого контакта независимо от имени, закрепления или фонового положения; временная ошибка чтения сети этот срок не продвигает. Один MAC с несколькими IP помечается как неоднозначный, а устройство появляется только после сетевого контакта с Gateway. Интерфейс самого Gateway не выдаётся за Wi-Fi/Ethernet устройства. Внешние сервисы распознавания производителя не используются. `Прокси` учитывает подключения устройства к общему proxy-порту Harbor, а `Gateway` — остальной публичный трафик через Gateway; трафик, который вообще не дошёл до Harbor, увидеть нельзя. Локальные, приватные и multicast-пакеты в totals не входят. При аварийном restart dataplane возможна потеря последних примерно 30 секунд; история по часам пока не хранится.
Home показывает фактически применённый VPN-сервер, накопленное `Учтено Harbor` и большой нижний график средней скорости Download/Upload за фактический интервал между снимками. `Учтено Harbor` — сумма `Gateway` и явного `Прокси` для всех наблюдавшихся устройств; это не лимит VPN-провайдера и не весь физический трафик Linux-машины. Накопленный total сохраняется при очистке старых устройств, а короткая история скорости после перезапуска начинает заполняться заново.
## Установка Harbor Connect на macOS
### 1. Запустите Docker Desktop
Установщик проверит наличие Docker, Docker Compose, `curl` и `tar`. Если Docker Desktop не запущен, установка остановится с понятным сообщением.
### 2. Запустите установщик
```bash
curl -fsSL https://git.dokops.ru/dokril/vpn-proxy/raw/branch/master/install.sh | sh
```
Установщик:
- сохранит рабочую копию в `~/.vpn-proxy-client`;
- предложит порт для локального прокси;
- соберёт и запустит контейнер Harbor Connect;
- добавит пользовательский LaunchAgent для определения текущего Gateway.
По умолчанию используются адреса:
| Назначение | Адрес |
| --- | --- |
| Интерфейс Harbor Connect | `http://127.0.0.1:3456` |
| HTTP-прокси | `127.0.0.1:8082` |
| SOCKS5-прокси | `127.0.0.1:8082` |
### 3. Добавьте подписку
Откройте `http://127.0.0.1:3456`, добавьте подписку с понятным именем, выберите сервер внутри её группы и включите VPN. Остальные подписки можно добавить через правую панель «Подписки»; у каждой сохраняются собственные серверы, лимит и выбор.
Сам по себе локальный прокси не перенаправляет приложения автоматически. Адрес `127.0.0.1:8082` нужно указать в настройках нужного приложения или в системных настройках macOS.
### Другие порты
Передайте нужные значения при повторном запуске установщика:
```bash
curl -fsSL https://git.dokops.ru/dokril/vpn-proxy/raw/branch/master/install.sh | \
VPN_PROXY_CLIENT_PORT=9080 \
VPN_PROXY_CLIENT_UI_PORT=3457 \
sh
```
Допустимы порты от `1024` до `65535`. Установщик не позволит выбрать занятый порт или один порт одновременно для интерфейса и прокси.
## Локальные правила маршрутизации
После добавления подписки откройте «Локальные правила» справа от основного экрана. При первом обновлении Harbor добавит обычное включённое правило `*.ru`, поэтому российские домены пойдут напрямую. Его, как и любое другое правило, можно выключить или удалить. Доступны точный домен, suffix домена и фрагмент имени; включённые правила обходят VPN, а остальной трафик идёт через выбранный сервер.
Полный URL можно вставить в поле точного домена, но Harbor сохранит только hostname. Путь и параметры HTTPS зашифрованы и недоступны sing-box на уровне маршрутизации. GeoSite, GeoIP и подключаемые списки пока не поддерживаются.
При сохранении Harbor проверяет фактическое состояние sing-box. Работающий процесс автоматически перезагружает новую конфигурацию. Если sing-box остановлен, правила сохраняются с признаком «ждут перезапуска» и начнут работать при следующем запуске или restart; этот статус виден в интерфейсе.
## Системный прокси macOS
Сначала посмотрите точное имя сетевого подключения:
```bash
networksetup -listallnetworkservices
```
Для подключения с именем `Wi-Fi` включите HTTP, HTTPS и SOCKS5-прокси:
```bash
networksetup -setwebproxy Wi-Fi 127.0.0.1 8082
networksetup -setsecurewebproxy Wi-Fi 127.0.0.1 8082
networksetup -setsocksfirewallproxy Wi-Fi 127.0.0.1 8082
```
Чтобы отключить их:
```bash
networksetup -setwebproxystate Wi-Fi off
networksetup -setsecurewebproxystate Wi-Fi off
networksetup -setsocksfirewallproxystate Wi-Fi off
```
Если сетевое подключение называется иначе, замените `Wi-Fi` его точным именем.
## Автоматическое использование домашнего Gateway
Harbor Connect раз в пять секунд узнаёт у macOS адрес текущего основного шлюза. Если по этому адресу работает Harbor Gateway с той же выбранной VPN-подпиской, Connect оставляет локальный прокси доступным для приложений, но не создаёт второй VPN-маршрут: трафик уже обрабатывает Gateway.
Для этого:
1. добавьте одну и ту же ссылку подписки в Gateway и Connect и выберите соответствующий профиль на обоих устройствах;
2. убедитесь, что Mac может открыть интерфейс Gateway на порту `3456`;
3. оставьте автоматический режим включённым в Harbor Connect.
Ссылка должна содержать персональный секрет или token длиной не менее 16 символов — обычные ссылки подписок уже соответствуют этому условию. Ссылка между устройствами не передаётся: она используется локально для проверки, что Connect нашёл именно ваш Gateway. До отдельного pairing-flow Connect не получает от Gateway имя фактически применённых подписки и сервера, поэтому в режиме Gateway честно показывает `Gateway · сервер не определён`. При смене сети или после трёх неудачных проверок Connect возвращается к локальному VPN.
## Повседневные команды
Все команды Gateway выполняются из каталога проекта. Команды Connect — из `~/.vpn-proxy-client`.
### Harbor Gateway
| Действие | Команда |
| --- | --- |
| Запустить или обновить после изменения файлов | `docker compose -f docker-compose.gateway.yml up -d --build` |
| Обновить только интерфейс и управление | `docker compose -f docker-compose.gateway.yml build vpn-proxy-control && docker compose -f docker-compose.gateway.yml up -d --no-deps vpn-proxy-control` |
| Показать состояние | `docker compose -f docker-compose.gateway.yml ps` |
| Смотреть журнал | `docker compose -f docker-compose.gateway.yml logs -f` |
| Перезапустить только интерфейс и управление | `docker compose -f docker-compose.gateway.yml restart vpn-proxy-control` |
| Перезапустить VPN dataplane | `docker compose -f docker-compose.gateway.yml restart vpn-proxy-dataplane` |
| Остановить | `docker compose -f docker-compose.gateway.yml down` |
| Удалить вместе с сохранёнными данными | `docker compose -f docker-compose.gateway.yml down -v` |
### Harbor Connect
```bash
cd ~/.vpn-proxy-client
```
| Действие | Команда |
| --- | --- |
| Обновить и снова запустить | `./scripts/install-macos-client.sh` |
| Показать состояние | `docker compose -f docker-compose.client.yml ps` |
| Смотреть журнал | `docker compose -f docker-compose.client.yml logs -f` |
| Перезапустить | `docker compose -f docker-compose.client.yml restart` |
| Остановить | `docker compose -f docker-compose.client.yml down` |
| Удалить вместе с сохранёнными данными | `docker compose -f docker-compose.client.yml down -v` |
Команда с `-v` удаляет подписку, выбранный сервер и другие сохранённые данные. Для обычной остановки используйте `down` без `-v`.
## Обновление
### Gateway
```bash
git pull --ff-only
docker compose -f docker-compose.gateway.yml up -d --build
```
### Connect
Повторно запустите однострочный установщик. Он обновит рабочую копию, снова спросит порт прокси и пересоберёт Connect:
```bash
curl -fsSL https://git.dokops.ru/dokril/vpn-proxy/raw/branch/master/install.sh | sh
```
Если раньше использовался нестандартный порт, укажите его снова через `VPN_PROXY_CLIENT_PORT`.
### Версии
Текущая версия всегда показана в правом нижнем углу интерфейса. Connect показывает строку `M` (Mac client). Gateway показывает `C` (Gateway client UI), `B` (текущий control-backend) и `D` (фактически развёрнутый dataplane). Поэтому после control-only deploy `B` обновится сразу, а `D` может намеренно остаться на прежней версии до следующего runtime-deploy. Наведите курсор или переведите клавиатурный фокус на цифру, чтобы увидеть смысл `major`, `minor` или `hotfix`; у `D` также указана фактическая версия `sing-box`.
Компонентные версии меняются в `src/shared/versions.ts`. У всех компонентов должен совпадать `major`, у Gateway client и backend — `major.minor`; `hotfix` может отличаться. Runtime-значения доступны через `GET /api/version`.
Для изменения версии используйте `npm run version:harbor -- affected HEAD`, затем `npm run version:harbor -- bump <major|minor|hotfix> [компонент]` и `npm run version:harbor -- check HEAD`. Правила выбора уровня закреплены в обязательном repo skill `manage-harbor-versions`.
## Настройки `.env`
Для большинства установок достаточно стандартных значений.
| Переменная | По умолчанию | Назначение |
| --- | --- | --- |
| `PORT` | `3456` | Внутренний порт веб-интерфейса Gateway |
| `CLIENT_UI_PORT` | `3456` | Порт интерфейса Connect на Mac |
| `PROXY_PORT` | `8080` | Порт общего прокси Gateway |
| `CLIENT_PROXY_PORT` | `8082` | Порт локального прокси Connect |
| `HARBOR_GATEWAY_CONTROL_PORT` | `3456` | Порт, на котором Connect проверяет домашний Gateway |
| `PROXY_BIND_IP` | `0.0.0.0` | Адрес, на котором Gateway принимает прокси-подключения |
| `PROXY_ALLOWED_CIDRS` | приватные IPv4-сети | Сети, которым разрешён доступ к Gateway Proxy |
| `GATEWAY_CLIENT_CIDRS` | приватные IPv4-сети | Сети, трафик которых Gateway может маршрутизировать |
| `DIRECT_TRAFFIC_MARK` | `0x40000000` | Зарезервированный одиночный connmark-бит учёта Direct; измените при конфликте с host QoS/firewall, не пересекаясь с `TPROXY_MARK` |
| `LOG_LEVEL` | `info` | Уровень подробности журнала |
Остальные значения в `.env.example` относятся к сборке контейнера и внутренней маршрутизации. Меняйте их только при нестандартном развёртывании.
После изменения `.env` пересоздайте контейнер командой `up -d` — обычного `restart` недостаточно.
## Prometheus и Grafana
Gateway публикует уже накопленные Harbor traffic counters по адресу `http://<gateway>:3456/metrics`. Scrape не запускает дополнительный netfilter read и не меняет сохранённое состояние. Harbor обновляет snapshot раз в 15 секунд, поэтому рекомендуемый начальный scrape interval и refresh dashboard — 30 секунд:
```yaml
scrape_configs:
- job_name: harbor_gateway
scrape_interval: 30s
scrape_timeout: 3s
metrics_path: /metrics
static_configs:
- targets: ["<gateway>:3456"]
```
`harbor_traffic_bytes_total` содержит общий накопленный объём по источникам Gateway/Proxy. `harbor_device_traffic_bytes_total` содержит upload/download по стабильному `device_id`; пользовательское название и текущий IP находятся в `harbor_device_info`. `harbor_device_domain_traffic_bytes_total` добавляет наблюдённые домен, сервис, источник и направление для каждого устройства. `harbor_domain_traffic_attribution_events_total{outcome}` помогает отличить нераспознанный hostname, неизвестное устройство и неподдерживаемый inbound без динамических high-cardinality labels.
Фактический выход экспортируется отдельно. `harbor_singbox_tracked_bytes_total{source,outbound,direction}` показывает наблюдённые sing-box байты с `outbound="vpn|direct|unknown"`; вариант с префиксом `harbor_device_...` добавляет `device_id`. `harbor_direct_ipv4_packet_bytes_total{direction}` считает IPv4-пакеты, которые Gateway направил напрямую вместо sing-box, включая policy Direct и работу при остановленном VPN runtime; вариант `harbor_device_...` содержит атрибутированную детализацию. `source="gateway|proxy"` по-прежнему означает место входа, а `outbound` — выбранный sing-box выход.
Dashboard начинает со скорости скачивания и отправки в конце выбранного периода, общего трафика и VPN / Direct внутри sing-box за этот период. Для стандартного диапазона, который заканчивается сейчас, карточки скорости показывают текущее значение. Единый фильтр `Устройства` по умолчанию охватывает все устройства, но позволяет выбрать одно; он управляет скоростью, общим трафиком, маршрутами, сервисами, доменами и технической детализацией. Таблица «Все устройства за период» намеренно остаётся общей: она показывает все устройства с ненулевым трафиком, сортируется в обе стороны и выбирает устройство в том же фильтре. Блок «Куда уходит трафик» показывает основные назначения и Top-15 доменов без пагинации. Свёрнутая техническая детализация отдельно показывает точки входа Gateway / Proxy и Direct IPv4 мимо sing-box. Автообновление настроено на 30 секунд; индикатор показывает возраст самого старого из контуров общего, domain / sing-box и Direct IPv4 трафика, предупреждает после 60 секунд и считает данные устаревшими после 120 секунд.
Domain и sing-box outbound counters снимаются с активных соединений раз в 2 секунды и хранятся в памяти dataplane до его перезапуска; историю и retention хранит Prometheus. Перед routing sing-box до 1 секунды распознаёт HTTP Host, TLS SNI и QUIC Server Name. YouTube и OpenAI / ChatGPT объединяются по известным связанным доменам в label `service`, остальные значения сохраняют домен как имя сервиса. Если устройство и Harbor source известны, но hostname недоступен (например, ECH или IP-only), трафик попадает в `domain="_unknown",service="Не распознано"` и не теряется. Новые domain series сверх process limit складываются в `_other`.
Direct IPv4 считает L3 packet bytes с IP-заголовками и retransmit, а sing-box tracker — логические TCP/UDP bytes без tunnel overhead. Эти семейства нельзя складывать в один «точный общий трафик». Snapshot polling может пропустить короткие соединения и финальный хвост; IPv6, трафик вне Gateway, назначения из `BYPASS_CIDRS` и quota провайдера не входят в новый route split.
Готовый dashboard: [`monitoring/grafana/harbor-gateway.json`](monitoring/grafana/harbor-gateway.json). При импорте Grafana попросит выбрать Prometheus data source. Та же конфигурация и dashboard доступны для копирования в Gateway drawer «Как использовать» → «Prometheus и Grafana».
## Если что-то не работает
### Интерфейс не открывается
Проверьте контейнер и журнал:
```bash
docker compose -f docker-compose.gateway.yml ps
docker compose -f docker-compose.gateway.yml logs --tail=100
```
Для Connect замените имя файла на `docker-compose.client.yml` и выполняйте команду из `~/.vpn-proxy-client`.
### Прокси не отвечает
Убедитесь, что Harbor включён в интерфейсе, а приложение использует правильные адрес и порт. Для Connect это обычно `127.0.0.1:8082`; для Gateway — IP Linux-машины и порт `8080`.
### Connect не распознаёт Gateway
Проверьте три условия:
- Gateway является текущим основным шлюзом Mac;
- на обоих устройствах сохранена одна и та же подписка;
- с Mac открывается `http://АДРЕС-GATEWAY:3456`.
### Проверка конфигурации без запуска
```bash
docker compose -f docker-compose.gateway.yml config
docker compose -f docker-compose.client.yml config
docker compose -f docker-compose.client.local.yml config
```
Эти команды только проверяют и показывают итоговую конфигурацию Docker Compose.
### Локальное тестирование Harbor Connect
Тестовый Connect запускается рядом с установленным клиентом и использует отдельные контейнер, volumes и порты:
```bash
docker compose -f docker-compose.client.local.yml up -d --build
```
Интерфейс доступен на `http://127.0.0.1:3457`, HTTP/SOCKS5-прокси — на `127.0.0.1:8083`. Остановить и удалить только тестовый стек можно командой:
```bash
docker compose -f docker-compose.client.local.yml down -v
```
Порты можно заменить через `LOCAL_CLIENT_UI_PORT` и `LOCAL_CLIENT_PROXY_PORT`.
## Служебные команды
Этот раздел нужен тем, кто собирает, проверяет или развёртывает сам проект. Для обычного использования он не требуется.
### Команды npm
| Команда | Назначение |
| --- | --- |
| `npm ci` | Установить точные версии зависимостей из `package-lock.json` |
| `npm test` | Запустить автоматические проверки |
| `npm run build` | Собрать веб-интерфейс в `dist/` |
| `npm run dev` | Запустить Vite для разработки интерфейса |
| `npm start` | Запустить управляющий Node.js-сервис в подготовленном окружении |
### Сборка и развёртывание
| Команда | Назначение |
| --- | --- |
| `./scripts/build-runtime-base.sh` | Собрать базовый runtime-образ с Node.js, сетевыми утилитами и `sing-box` |
| `./scripts/build-on-107-deploy-111.sh` | Собрать Gateway на хосте `107` и развернуть на хосте `111`; хосты меняются через `BUILD_HOST` и `DEPLOY_HOST` |
| `GATEWAY_IMAGE=<образ> ./scripts/deploy-gateway.sh` | Развернуть уже собранный образ в `/opt/vpn-proxy` |
| `./scripts/harbor-network-monitor.sh` | Один раз записать текущий Gateway macOS; обычно этот скрипт запускает установленный LaunchAgent |
Отправка изменений в ветку `master` также запускает автоматическую сборку и развёртывание Gateway через Gitea Actions. Каждый деплой пересоздаёт `vpn-proxy-control`, поэтому строка `B` соответствует текущему коду API. Процесс `sing-box` и сетевые правила остаются в `vpn-proxy-dataplane`; он пересоздаётся только при изменении его runtime-зависимостей, а его фактическая версия показывается отдельно как `D`.
## Хранение данных
Подписка, выбранный сервер и состояние подключения хранятся в именованных Docker volumes. Поэтому обычные команды `restart`, `down`, обновление проекта и повторная сборка не удаляют настройки.
Не публикуйте файл `.env`, ссылку подписки и содержимое Docker volumes. `.env` уже исключён из Git.