426 lines
48 KiB
Markdown
426 lines
48 KiB
Markdown
# 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
|
||
|
||
После добавления подписок откройте «Резерв» — вторую кнопку в правой панели. Выберите основной и резервный серверы (они могут быть из одной или разных подписок), сервисы для проверки и отдельный таймаут каждого сервиса. Там же настраиваются длительность сбоя и восстановления, порог активного трафика, период тишины и защита от частых переключений.
|
||
|
||
При включении Harbor заранее проверяет dual-конфигурацию. Если VPN остановлен, она начнёт работать только после следующего обычного нажатия питания; сохранение само VPN не включает. Переключение меняет маршрут только для новых соединений — уже открытые соединения не закрываются. Если через VPN идёт активный трафик или его активность нельзя надёжно определить, Harbor ждёт и показывает скорость, число передающих соединений и безопасные подписи основных блокирующих потоков.
|
||
|
||
Выключенный резерв полностью пассивен: Harbor не запускает проверки, таймер выбора и отдельный подсчёт активности. Если dual-конфигурация уже загружена, отключение не перезапускает VPN и не меняет текущий маршрут; обычный stop и следующий запуск вернут single-channel config. Последние важные события — включение VPN, обновления подписок, переключения и ошибки — доступны в последней кнопке «Журнал» и хранятся 30 дней без ссылок подписок и сырых диагностических ответов.
|
||
|
||
### Устройства Gateway
|
||
|
||
Откройте «Устройства» в правой панели Gateway — подписка для просмотра списка не требуется. Harbor раз в 15 секунд читает локальную таблицу соседей и показывает каждое устройство одной компактной строкой: заданное название, hostname или IP, последний контакт, выбранный график трафика и иконку применённого маршрута. По умолчанию график показывает приблизительный выход `VPN`/`Direct`; переключатель `Вход` возвращает накопленную разбивку `Gateway`/`Прокси`. Наведите курсор на имя или переведите на него фокус, чтобы открыть IP, MAC и доступный hostname; нажатие на значение копирует его. Hostname определяется через локальное обратное разрешение имён и может отсутствовать, если сеть его не публикует. Технические interface и manufacturer продолжают храниться для идентификации, но не занимают место в строке. Список разделён на «Закреплённые», «Остальные» и «Фоновые»: последняя группа сохраняется между перезапусками, показывает только identity/presence и кнопку возврата без графика, traffic и route controls. Название, закрепление, фоновое положение и накопленные totals сохраняются в volume Gateway, пока устройство остаётся в inventory.
|
||
|
||
Левая панель списка ищет по имени, hostname, IP, MAC и тегам, фильтрует новые, закреплённые, фоновые или устройства без тегов и позволяет выбрать несколько тегов по правилу «хотя бы один». Каталог тегов общий для Gateway: в нём можно создать до 32 тегов и назначить устройству до 8. Назначения сохраняются в документе устройств внутри `harbor.sqlite`, но маршруты не меняют. После удаления устройства по 30-дневному retention его назначения удаляются, сам каталог остаётся; вернувшееся позже устройство появляется без тегов. Если Mac-клиент подключён к старой версии Gateway, список продолжает работать, а управление тегами скрывается до обновления Gateway.
|
||
|
||
Красная кнопка `Сбросить данные` после отдельного подтверждения обнуляет вход и выход всех устройств и начинает считать их заново. Общий график скорости на Home и уже сохранённая история Prometheus/Grafana не очищаются: входной counter выглядит для Prometheus как стандартный reset, а для выхода Harbor сохраняет только baseline отображения и не изменяет raw dataplane counters.
|
||
|
||
Устройство, впервые замеченное после обновления Gateway, по умолчанию идёт `Напрямую` и первые семь дней отмечается `NEW`; исчезновение метки маршрут не меняет. Уже известные при обновлении устройства сохраняют текущий VPN, даже если метка ещё видна по их `firstSeenAt`. VPN разрешается существующей последней иконкой маршрута. Если новый device пока распознан неоднозначно, Harbor сохраняет Direct-намерение, временно оставляет фактический VPN и применяет Direct после однозначного наблюдения.
|
||
|
||
У однозначно распознанного устройства маршрут можно переключить последней иконкой между `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.
|
||
|
||
Кнопка «Трафик» в правой панели показывает активные соединения, которые прошли через Harbor Connect. Данные о приложениях macOS недоступны, потому что sing-box работает внутри Docker.
|
||
|
||
### Другие порты
|
||
|
||
Передайте нужные значения при повторном запуске установщика:
|
||
|
||
```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` либо `Напрямую`, выключить правило или удалить его. Правила проверяются сверху вниз, первое совпадение выбирает маршрут. Чтобы изменить порядок, возьмите строку за три точки слева и перетащите; с клавиатуры нажмите на этом хвате `Space` или `Enter`, переместите правило стрелками и повторно нажмите для размещения.
|
||
|
||
Правила применяются только к трафику, который вошёл в VPN-маршрутизацию Harbor. Устройство Gateway в режиме «Напрямую» и Connect при активном Harbor Gateway обходят локальный список; «Напрямую» внутри правила — результат уже найденного совпадения. Для устройства Gateway с маршрутом `VPN` и при обычном локальном VPN список применяется.
|
||
|
||
Полный URL можно вставить в поле точного домена, но Harbor сохранит только hostname. Путь и параметры HTTPS зашифрованы и недоступны sing-box на уровне маршрутизации. GeoSite, GeoIP и подключаемые списки пока не поддерживаются.
|
||
|
||
При сохранении Harbor проверяет фактическое состояние sing-box. Работающий процесс применяет новую конфигурацию, только если она изменилась. Если sing-box остановлен, правила сохраняются с признаком «ждут запуска» и начнут работать при следующем запуске или restart; в Connect с активным Harbor Gateway они сохраняются как желаемые, но локально не применяются.
|
||
|
||
## Системный прокси 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` |
|
||
| `SING_BOX_TRAFFIC_SOURCE` | `native` | Источник Gateway traffic counters: `snapshot`, `shadow` или `native` |
|
||
| `LOG_LEVEL` | `info` | Уровень подробности журнала |
|
||
|
||
Остальные значения в `.env.example` относятся к сборке контейнера и внутренней маршрутизации. Меняйте их только при нестандартном развёртывании.
|
||
|
||
После изменения `.env` пересоздайте контейнер командой `up -d` — обычного `restart` недостаточно.
|
||
|
||
Production Gateway по умолчанию использует `native`: новый счётчик видит полный жизненный цикл соединений, включая короткие соединения и последние байты перед закрытием. `shadow` оставляет основным старый счётчик и запускает новый только для сравнения. `snapshot` полностью выключает инспектор и опрашивает активные соединения раз в 2 секунды. Режим меняется только при пересоздании обоих Gateway-контейнеров и не переключается автоматически при ошибке, чтобы одни байты не были посчитаны дважды.
|
||
|
||
Rollback сохраняет volumes и возвращает прежний writer:
|
||
|
||
```bash
|
||
SINGBOX_VERSION=1.13.18 \
|
||
SING_BOX_TRAFFIC_SOURCE=snapshot \
|
||
docker compose -f docker-compose.gateway.yml up -d --build
|
||
```
|
||
|
||
## Локальная история трафика
|
||
|
||
Harbor полностью работает без Prometheus. В существующем drawer «Трафик» режимы `Сейчас / История` разделяют текущие соединения и локальные суммы. История поддерживает `24 часа / 7 дней / 30 дней / 90 дней`, поиск, маршрут и устройство на Gateway; строки раскрываются как сервис → домен → полное имя → IP. Например, `www.yandex.ru` и `mail.yandex.com` остаются разными именами внутри группы «Яндекс». IP без наблюдённого домена не выдаётся за распознанный сайт.
|
||
|
||
`traffic.sqlite` хранит рабочие данные за 90 дней: завершённые минуты за последние 7 дней, далее часы. Текущая история отстаёт не более чем на минуту при исправном сборе; API сообщает фактически доступный период, детализацию и пропуски. История начинается с включения нового native-сбора. Данные по доменам относятся только к соединениям, наблюдаемым sing-box, и не восстанавливают ранее накопленные общие счётчики.
|
||
|
||
Запись и запросы выполняются в отдельном рабочем потоке. Ошибка базы или переполнение ограниченной очереди отмечает историю как неполную, но не останавливает VPN или экспорт метрик. Для защиты от повторного учёта сохраняются позиции счётчиков: активные — пока нужны их исходные значения, закрытые — до 90 дней либо смены процесса sing-box. Размер зависит не только от доменов, но и от числа соединений; это не база фиксированного размера. Освобождённые страницы переиспользуются без обязательного немедленного уменьшения файла.
|
||
|
||
## Prometheus и Grafana
|
||
|
||
Prometheus необязателен. Он хранит только экспортируемые метрики по политике своего владельца, а не копию всей SQLite. Полную доменную/IP-детализацию внешнего архива этот релиз не обещает. Нет синхронизации баз, автоматического восполнения пропущенных scrape, восстановления SQLite из Prometheus или переключения интерфейса на него. Очистка локальной истории не удаляет внешнюю; отсутствие Prometheus не продлевает локальные 90 дней.
|
||
|
||
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`. Gauge `harbor_device_applied_policy{device_id}` показывает последнюю применённую policy: `0` для Direct и `1` для VPN. История начинается с первого Prometheus scrape после обновления и не восстанавливается задним числом. `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 за этот период. Под обзором полоса `Применённый режим` показывает applied policy устройства, а график `Фактический VPN / Direct` независимо показывает маршрут наблюдённых байтов. Поэтому компьютер с policy Direct, браузер которого использует Harbor Proxy, остаётся Direct на полосе режима, но его proxy-соединения учитываются в VPN. Единый фильтр `Устройства` управляет режимом, скоростью, общим трафиком, сервисами, доменами и технической детализацией. Таблица «Все устройства за период» намеренно остаётся общей и выбирает устройство в том же фильтре. Блок «Куда уходит трафик» показывает основные назначения и Top-15 доменов без пагинации. Свёрнутая техническая детализация показывает `source × outbound`, включая `proxy · vpn`, и раздельные Direct-пути через sing-box и Linux мимо sing-box. Автообновление настроено на 30 секунд; индикатор предупреждает после 60 секунд и считает данные устаревшими после 120 секунд.
|
||
|
||
В `snapshot` и `shadow` domain и sing-box outbound counters снимаются с активных соединений раз в 2 секунды. В `native` dataplane получает полный lifecycle, включая короткие соединения и финальный хвост; существующая проекция экспортируемых domain/outbound counters хранится в памяти до перезапуска, а необязательный Prometheus независимо сохраняет полученные метрики. Полный поток также поступает в отдельную локальную `traffic.sqlite`; её очистка не сбрасывает эту проекцию. Перед 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`.
|
||
|
||
Состояние collector и сравнение `shadow` экспортируются отдельными bounded gauges `harbor_traffic_collector_*` и `harbor_traffic_shadow_*`. Они не содержат UUID, IP, домены или пользовательские имена и не заменяют canonical traffic counters.
|
||
|
||
Direct IPv4 считает L3 packet bytes с IP-заголовками и retransmit, а sing-box tracker считает логические TCP/UDP bytes без tunnel overhead. Основные Grafana panels складывают их только как приблизительную пользовательскую оценку непересекающихся Direct-путей; техническая секция сохраняет значения раздельными. Эту сумму нельзя считать точным provider или wire total. Snapshot polling может пропустить короткие соединения и финальный хвост; native lifecycle закрывает этот разрыв только для трафика, вошедшего в sing-box. 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`. Остановить тестовый стек с сохранением его volumes можно командой:
|
||
|
||
```bash
|
||
docker compose -f docker-compose.client.local.yml down
|
||
```
|
||
|
||
Порты можно заменить через `LOCAL_CLIENT_UI_PORT` и `LOCAL_CLIENT_PROXY_PORT`.
|
||
|
||
Для rollback canary на стабильный sing-box без инспектора используйте:
|
||
|
||
```bash
|
||
SINGBOX_VERSION=1.13.18 \
|
||
SING_BOX_TRAFFIC_SOURCE=disabled \
|
||
docker compose -f docker-compose.client.local.yml up -d --build
|
||
```
|
||
|
||
Не добавляйте `-v` к `down`, если хотите сохранить тестовые подписки и настройки.
|
||
|
||
## Служебные команды
|
||
|
||
Этот раздел нужен тем, кто собирает, проверяет или развёртывает сам проект. Для обычного использования он не требуется.
|
||
|
||
### Команды npm
|
||
|
||
Для сборки и backend закреплён Node **24.21.0** (`.node-version`); используется встроенная SQLite без ORM. `npm run check:runtime` проверяет точную Node-версию, движок SQLite не старше 3.51.3 и точность 64-битных счётчиков. Та же проверка выполняется в сборочных и конечных Docker-образах. При использовании fnm: `fnm use 24.21.0`.
|
||
|
||
| Команда | Назначение |
|
||
| --- | --- |
|
||
| `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`, обновление проекта и повторная сборка не удаляют настройки.
|
||
|
||
На каждом Mac/Gateway свои `harbor.sqlite` (настройки, подписки, устройства, правила, накопленные счётчики и журнал) и `traffic.sqlite` (ограниченная история). Журнал сохраняет прежний предел 30 дней/10 000 событий; настройки не подчиняются retention истории. Секреты, hardware ID, генерируемый конфиг и кеш sing-box остаются файлами.
|
||
|
||
Первый запуск транзакционно импортирует прежние JSON, сохраняя IDs, revisions и исходные значения счётчиков. После успеха SQLite становится единственным рабочим хранилищем; исходные JSON остаются неизменными резервными копиями, без параллельной записи. Повреждение или неизвестная версия останавливает миграцию без обнуления. Старый бинарник не читает новые данные: простой downgrade вернул бы устаревшие JSON. Правила backup и восстановления описаны в [state recovery](docs/recovery/state-recovery.md).
|
||
|
||
Не публикуйте файл `.env`, ссылку подписки и содержимое Docker volumes. `.env` уже исключён из Git.
|