# Harbor Harbor помогает пользоваться одной VPN-подпиской дома и на Mac без ручной настройки `sing-box`. Проект работает в двух режимах: | Режим | Где работает | Для чего нужен | | --- | --- | --- | | **Harbor Gateway** | На отдельной Linux-машине | Проводит через VPN весь интернет-трафик домашних устройств или работает как общий HTTP/SOCKS5-прокси | | **Harbor Connect** | На macOS | Даёт приложениям на Mac локальный HTTP/SOCKS5-прокси | В Harbor Connect подписка и выбор сервера остаются на основном экране. Harbor Gateway открывается как панель маршрутизации даже без подписки: Home, «Устройства» и «Диагностика» доступны сразу, а VPN-подписка настраивается отдельной верхней кнопкой в правой панели. ## Что понадобится - ссылка на подписку от 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 секунд читает локальную таблицу соседей и показывает каждое устройство одной компактной строкой: название, IP и последний контакт, общий трафик с раскрываемой по hover/focus разбивкой `Gateway`/`Прокси`, затем иконку применённого маршрута. Нажмите IP, чтобы скопировать его с feedback «Скопировано». Технические MAC, interface и manufacturer продолжают храниться для идентификации, но не занимают место в строке. Список разделён на «Закреплённые», «Остальные» и «Убраны вниз»: последняя группа сохраняется между перезапусками, показывает только identity/presence и кнопку возврата без графика, traffic и route controls. Название, закрепление, положение в нижней группе и накопленные totals сохраняются в volume Gateway. У однозначно распознанного устройства маршрут можно переключить последней иконкой между `VPN` и `Напрямую` независимо от закрепления; точное значение и следующее действие показаны в tooltip. `VPN` означает обработку через sing-box и правила Gateway: например, включённое локальное доменное правило всё равно может выбрать прямой выход внутри sing-box. `Напрямую` полностью обходит sing-box на уровне iptables. Traffic totals учитываются в обоих режимах. Если правило не удалось применить, Harbor сохраняет выбранный режим и отдельно показывает последний фактически применённый маршрут. Список приблизительный: имя и пользовательские настройки привязаны к MAC и сохраняются при обычной смене IP, но новый private/randomized MAC считается новым устройством — переносить имя по одному только DHCP-адресу небезопасно. Один 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. При смене сети или после трёх неудачных проверок 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 [компонент]` и `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 может маршрутизировать | | `LOG_LEVEL` | `info` | Уровень подробности журнала | Остальные значения в `.env.example` относятся к сборке контейнера и внутренней маршрутизации. Меняйте их только при нестандартном развёртывании. После изменения `.env` пересоздайте контейнер командой `up -d` — обычного `restart` недостаточно. ## Prometheus и Grafana Gateway публикует уже накопленные Harbor traffic counters по адресу `http://: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: [":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. Dashboard отделяет текущую скорость от значений за выбранный период и накопленных счётчиков. Единый фильтр `Устройства` по умолчанию охватывает все устройства, но позволяет выбрать одно; список показывает `name · ip`, сохраняя стабильный `device_id` как значение. Он управляет графиками скорости, накопленным трафиком, сервисами и доменами. Отдельный график скорости по устройствам показывает одну суммарную линию на каждое активное устройство; нулевые устройства и source/direction series скрыты. Top-10 устройств за период отсортирован по убыванию и выбирает устройство в том же фильтре. Domain table показывает только сервис, домен и трафик. Автообновление настроено на 30 секунд; freshness предупреждает после 60 секунд и считает данные устаревшими после 120 секунд. Domain counters снимаются с активных соединений sing-box раз в 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`. В метрики не входит физический трафик вне Harbor, устройства с policy Direct, соединения между двумя снимками и байты после последнего снимка перед закрытием или quota провайдера. Готовый 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.