SSD Nodes Learn 🎉 VPS от $5.50/мес
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-13

Как запустить Open Connector для AI-агентов на своем VPS

Разверните Open Connector как шлюз аутентификации для защиты API-токенов ваших AI-агентов. Инструкция по настройке TLS, OAuth-коллбэков и резервному копированию SQLite-базы.

Зачем AI-агенту нужен Open Connector

Самостоятельное размещение Open Connector позволяет создать единый шлюз аутентификации между вашими AI-агентами и всеми API сервисов (SaaS), к которым они обращаются. Благодаря этому агент никогда не хранит токены провайдеров. Это шлюз с открытым исходным кодом от OOMOL Lab, распространяемый по лицензии Apache 2.0. Он запускается как один контейнер, хранит состояние в единственном файле SQLite и предоставляет доступ к действиям провайдеров через HTTP и протокол MCP (model context protocol).

Проблемы начинаются уже на второй интеграции. У каждого провайдера свой процесс OAuth (open authorization), своё время жизни токена обновления и свои названия областей доступа (scopes). Ручная настройка пяти провайдеров для одного агента означает создание пяти обработчиков редиректов, пяти хранилищ учётных данных и пяти циклов обновления токенов, которые должны срабатывать до истечения срока их действия. Почти никто не пишет такой код самостоятельно. Обычно создают один долгоживущий персональный токен доступа для каждого сервиса и вставляют его в конфигурацию агента, файл переменных окружения или прямо в промпт. Этот токен становится доступен любому инструменту, который запускает агент, и попадает в историю переписки, что приводит к инцидентам, описанным в защите секретов от AI-агентов.

Шлюз аутентификации разделяет учётные данные на две части. Шлюз хранит учётные данные провайдера и выполняет процесс OAuth. Агент получает токен времени выполнения, который действителен только для взаимодействия со шлюзом. Когда агент вызывает действие, шлюз загружает сохранённые учётные данные, внедряет их в исходящий запрос на стороне сервера и возвращает только тело ответа. Агент никогда не получает токен доступа провайдера, поэтому в случае утечки истории переписки агента вы теряете лишь один отзывный токен времени выполнения, а не доступ к своей учётной записи GitHub.

В каталоге заявлено более 1 000 провайдеров и 10 000 готовых действий — это цифры самого проекта, которые невозможно проверить извне. Однако можно проверить архитектуру: одна HTTP-точка входа на каждое действие, одно сохранённое соединение на каждого провайдера и один токен на каждого агента.

Почему стоит использовать Open Connector на собственном сервере, а не облачный сервис-коннектор

Облачный сервис-коннектор выполняет ту же работу, но хранит refresh tokens для каждого подключенного провайдера. Refresh token для Google или GitHub — это долгоживущий ключ доступа к вашей почте и репозиториям, который обычно продолжает действовать даже после смены пароля. Компрометация такого сервиса означает компрометацию ваших данных. Развертывание на собственном сервере переносит эти записи в SQLite на машине, которую вы арендуете и администрируете, защищая их ключом, который никогда не покидает ваш сервер.

Оцените риски перед началом работы. Этот VPS становится самым важным сервером в вашей инфраструктуре. Он хранит рабочие учетные данные для десятка сервисов в одном файле, поэтому к нему нужно относиться так же, как к хосту менеджера паролей: настройте firewall, открыв только порт 443, исключите общие учетные записи, создайте резервную копию, которую вы хотя бы раз успешно восстанавливали, и настройте оповещения на случай, если сервер перестанет отвечать. Если вы не готовы разместить на этом сервере хранилище паролей, не стоит устанавливать на него и коннектор.

Фиксация версии перед установкой

Open Connector — молодой проект. Репозиторий появился 29 июня 2026 года, и по состоянию на 1 августа 2026 года новейшим помеченным релизом является v1.3.3, опубликованный 30 июля 2026 года и также имеющий тег latest. В реестре также публикуется тег tip, собранный из новейшего коммита в main.

В столь новых проектах плавающие теги часто меняются. Тег docker compose pull, который перескакивает через два релиза, может изменить endpoint, от которого зависит ваш агент, и вы потратите вечер на отладку, считая это проблемой агента. Фиксируйте образ на теге релиза и обновляйтесь только тогда, когда решите сами, предварительно ознакомившись с примечаниями к выпуску.

Развертывание Open Connector за TLS на собственном VPS

Перед запуском контейнера вам потребуются:

  • Docker с плагином Compose на Ubuntu 24.04 или аналогичном дистрибутиве
  • доменное имя, A-запись которого указывает на этот VPS, например connect.example.com
  • обратный прокси-сервер, который уже выполняет TLS termination (завершение TLS) для этого доменного имени
  • два случайных секретных ключа, сгенерированных ниже

В руководстве Traefik reverse proxy for multiple Docker Compose apps описана настройка прокси-сервера. Полный процесс настройки сертификатов для одного приложения описан в руководстве n8n on a VPS with Docker and HTTPS.

Сначала сгенерируйте секреты. Ключ шифрования защищает сохраненные учетные данные. Токен администратора защищает веб-консоль и всю поверхность /api. Значения по умолчанию отсутствуют, и среда выполнения запустится даже без них, что небезопасно.

mkdir -p ~/open-connector && cd ~/open-connector
umask 077
printf 'OOMOL_CONNECT_ENCRYPTION_KEY=%s\n' "$(openssl rand -base64 32)" > .env
printf 'OOMOL_CONNECT_ADMIN_TOKEN=%s\n' "$(openssl rand -base64 32)" >> .env
chmod 600 .env

Скопируйте оба значения в менеджер паролей прямо сейчас, до первого запуска. У ключа шифрования нет процедуры восстановления, причина этого указана в списке ошибок ниже.

Теперь создайте compose.yaml. Он отличается от исходного примера в двух местах, и оба важны.

services:
  connector:
    image: ghcr.io/oomol-lab/open-connector:v1.3.3
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    volumes:
      - connector-data:/app/data
    environment:
      OOMOL_CONNECT_DATA_DIR: /app/data
      OOMOL_CONNECT_ORIGIN: "https://connect.example.com"
      OOMOL_CONNECT_ENCRYPTION_KEY: "${OOMOL_CONNECT_ENCRYPTION_KEY:?set this in .env}"
      OOMOL_CONNECT_ADMIN_TOKEN: "${OOMOL_CONNECT_ADMIN_TOKEN:?set this in .env}"

volumes:
  connector-data:

Первое изменение — использование фиксированного тега вместо latest. Второе — порт. Исходный файл публикует 3000:3000, который привязывается ко всем интерфейсам на хосте. Docker записывает опубликованные порты в таблицу NAT (трансляция сетевых адресов) до того, как цепочка фильтров ufw увидит пакет, поэтому ufw deny 3000 не закрывает этот порт — это ловушка, описанная в why Docker ports bypass ufw. Указание 127.0.0.1:3000:3000 публикует порт только на интерфейсе loopback, а ваш обратный прокси-сервер подключается с того же хоста.

:? помечает каждую переменную как обязательную, поэтому стек отказывается запускаться при отсутствии .env, вместо того чтобы запускаться с незашифрованными учетными данными. Хранение значений в .env, а не в файле compose, соответствует шаблону из Docker Compose env files and secrets.

docker compose up -d
docker compose logs -n 30 connector
curl -s http://127.0.0.1:3000/health
sudo ss -tlnp | grep 3000

/health отвечает на { "ok": true } после запуска среды выполнения. ss должен вывести 127.0.0.1:3000. Строка с 0.0.0.0:3000 означает, что используется исходная привязка портов и шлюз отвечает всему интернету напрямую. Ошибка Connection refused при проверке работоспособности означает, что контейнер еще не начал прослушивание, поэтому изучите логи, прежде чем настраивать прокси.

Метки Traefik для того же сервиса
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.connector.rule=Host(`connect.example.com`)"
      - "traefik.http.routers.connector.entrypoints=websecure"
      - "traefik.http.routers.connector.tls.certresolver=le"
      - "traefik.http.services.connector.loadbalancer.server.port=3000"

Когда Traefik работает в Docker на том же хосте, подключите этот сервис к сети Traefik и удалите блок ports:, так как Traefik обращается к контейнеру через внутреннюю сеть и публикация портов на хост не требуется. certresolver=le должен совпадать с именем резолвера в статической конфигурации Traefik, иначе маршрутизатор запустится без сертификата.

Почему OAuth требует наличия реального имени хоста

OOMOL_CONNECT_ORIGIN — это настройка, которую часто пропускают, и её отсутствие нарушает работу OAuth таким образом, что это выглядит как ошибка на стороне провайдера. Среда выполнения формирует URI перенаправления на основе этого источника в формате <origin>/oauth/callback. Если значение не задано, по умолчанию используется http://localhost:3000, поэтому среда выполнения отправляет провайдеру URI перенаправления http://localhost:3000/oauth/callback, в то время как в вашем OAuth-приложении зарегистрирован https://connect.example.com/oauth/callback. Эти две строки различаются, поэтому GitHub возвращает ошибку:

The redirect_uri MUST match the registered callback URL for this application.

OAuth-провайдер перенаправляет браузер обратно на этот URI, а значит, он должен быть доступен из внешней сети. Провайдеры отклоняют обычный http:// для любых целей, кроме localhost. Именно по этой причине для развертывания требуются имя хоста и сертификат. Установите источник до первого запуска, так как значение считывается при старте: после редактирования .env или compose.yaml выполните docker compose up -d еще раз, чтобы применить изменения.

Подключение первого провайдера через OAuth

Сначала создайте OAuth-приложение на стороне провайдера. В GitHub путь следующий: Settings, затем Developer settings, далее OAuth Apps и New OAuth App. Установите URL обратного вызова авторизации (authorization callback URL) на https://connect.example.com/oauth/callback. Сохраните client ID и client secret.

Каждый вызов /api содержит токен администратора, поэтому экспортируйте его один раз для текущей сессии оболочки.

export ADMIN_TOKEN='paste-the-admin-token'
curl -s https://connect.example.com/api/oauth/configs \
  -H "authorization: Bearer $ADMIN_TOKEN"

Этот список показывает URI перенаправления, который ожидается средой выполнения для каждого провайдера. Это самый быстрый способ проверить, что ваши настройки источника (origin) вступили в силу. Если по-прежнему отображается localhost, значит, контейнер работает со старым значением, и процесс OAuth завершится ошибкой на последнем этапе.

Сохраните учетные данные клиента, затем запустите авторизацию.

curl -s -X PUT https://connect.example.com/api/oauth/configs/github \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"clientId":"...","clientSecret":"..."}'

curl -s -X POST https://connect.example.com/api/oauth/authorizations \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"service":"github"}'

Второй вызов возвращает authorizationUrl. Откройте этот адрес в браузере, подтвердите области доступа (scopes), и провайдер перенаправит браузер обратно на /oauth/callback, где среда выполнения обменяет код и сохранит учетные данные. Веб-консоль на вашем источнике выполняет те же действия через форму, используя тот же токен администратора. Провайдеры, использующие обычный API-ключ, пропускают этот процесс: PUT /api/connections/<service> с параметром {"authType":"api_key","values":{"apiKey":"..."}} сохраняет ключ напрямую.

Выдавайте каждому агенту токен времени выполнения, а не учетные данные

Агент проходит аутентификацию на шлюзе с помощью токена времени выполнения, который создается через Admin API.

curl -s -X POST https://connect.example.com/api/runtime-tokens \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"name":"research-agent"}'

Ответ содержит токен, начинающийся с oct_. Выпускайте по одному токену на каждого агента и называйте их в соответствии с именем агента. Если вы не сможете идентифицировать токен при отзыве, вам придется отозвать их все. После этого агент выполняет действия по обычному протоколу HTTP.

curl -s -X POST https://connect.example.com/v1/actions/github.get_current_user \
  -H "authorization: Bearer oct_..." \
  -H 'content-type: application/json' \
  -d '{"input":{}}'

Корректный ответ представляет собой конверт, в котором поле success имеет значение true, а полезная нагрузка провайдера находится в data. Токен GitHub в этом ответе отсутствует. Для клиента MCP укажите адрес https://connect.example.com/mcp с тем же заголовком bearer. Шлюз предоставит инструменты обнаружения, такие как search_actions и execute_action, вместо того чтобы предлагать по одному инструменту на каждый API. Это позволяет сократить список инструментов агента. В разделе Запуск MCP-серверов на VPS описана клиентская часть этой настройки.

Перед завершением выполните еще одну проверку. Повторите вызов действия, удалив заголовок authorization. В руководстве по быстрому старту проекта вызывается /v1 без какого-либо bearer-токена. Это означает, что при установке без настроенной аутентификации времени выполнения действия будут выполняться для любого, кто имеет доступ к порту. Если ваш неаутентифицированный запрос прошел успешно, у вас есть два пути: настроить токены времени выполнения и убедиться, что анонимный запрос теперь отклоняется, либо ограничить доступ к /api, /v1 и /mcp на обратном прокси-сервере, разрешив его только для IP-адресов ваших агентов. Только /oauth/callback должен оставаться открытым для всего мира, так как это единственный путь, необходимый для редиректа браузера провайдера.

Сокращение списка действий до необходимых агенту

Шлюз, за которым скрываются тысячи провайдеров, представляет собой широкую поверхность атаки для языковой модели. Она становится еще шире, когда модель начинает читать текст, написанный не ею, поскольку страница, возвращенная вашим собственным экземпляром SearXNG, отвечающим на веб-поиски агента, может содержать инструкции, нацеленные на любые действия, доступные агенту. Тот же принцип сдержанности, который заставляет агента для написания кода вносить минимально необходимые изменения, должен применяться и к его правам доступа: предоставляйте только те действия, которые действительно нужны для выполнения задачи, и ничего сверх этого. Два параметра позволяют ограничить эти возможности.

OOMOL_CONNECT_ALLOWED_ACTIONS принимает список разрешенных действий через запятую и поддерживает service.* и *. OOMOL_CONNECT_BLOCKED_ACTIONS — это список запрещенных действий, и он имеет приоритет. Установка списка разрешенных действий в github.get_current_user,github.list_issues означает, что любое другое действие будет отклонено, независимо от запроса агента, что является гранью между ошибкой и инцидентом. Токены времени выполнения (runtime tokens) имеют собственные правила действий поверх глобальных, и их список allowedProxies по умолчанию пуст, поэтому POST /v1/proxy/:service будет отклонено, пока вы его не разрешите. Эта конечная точка прокси пересылает необработанный запрос провайдеру с прикрепленными учетными данными, поэтому оставляйте ее пустой, если только она не требуется конкретному агенту.

OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK по умолчанию имеет значение false, что предотвращает подключение к self-hosted провайдеру, указывающему на частный адрес, такой как служба метаданных облака на 169.254.169.254 или ваша база данных в той же сети. Оставляйте этот параметр выключенным. Включайте его только для провайдера, которого вы размещаете самостоятельно.

Резервное копирование сервера, содержащего все токены

Важны две вещи, и каждая из них бесполезна без другой. База данных по пути /app/data/connect.sqlite внутри тома connector-data хранит зашифрованные учетные данные. Ключ шифрования в .env позволяет их расшифровать. Резервная копия тома без ключа ничего не восстановит, как и ключ без тома, поэтому ключ следует хранить в менеджере паролей, а том — включить в стандартный цикл резервного копирования.

Остановите контейнер перед копированием файла SQLite, так как копия, сделанная во время записи, может восстановиться как поврежденная база данных.

docker volume ls | grep connector-data
docker compose stop connector
docker run --rm -v open-connector_connector-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/connector-data.tgz -C /data .
docker compose start connector

Имя тома состоит из названия вашего каталога проекта и _connector-data, поэтому первая команда приведена здесь: вставьте реальное имя в третью команду. Отправляйте архив с VPS, используя резервное копирование restic с VPS, что обеспечит шифрование данных до их передачи, так как этот архив является хранилищем учетных данных.

Среда выполнения сохраняет недавние запуски действий в виде записей аудита (по умолчанию 5,000 записей), чтобы консоль могла показать, какой агент, что и когда выполнил. Этот журнал — первое, что нужно изучить при странном поведении агента. Также настройте страницу статуса Uptime Kuma для https://connect.example.com/health. Когда шлюз перестает отвечать, агенты начинают выдавать ошибки, вводящие в заблуждение, и понимание того, что шлюз не работает, сэкономит час на чтении вывода агентов.

Что может сломаться и какие сообщения вы увидите

redirect_uri_mismatch у провайдера. Исходный адрес и зарегистрированный URL обратного вызова (callback URL) не совпадают. Сравните точную строку из /api/oauth/configs с настройками приложения у провайдера, включая https в сравнении с http и наличие завершающего слеша.

Каждый вызов /api возвращает 401. Заголовок с токеном администратора отсутствует или написан с ошибкой. Заголовок имеет вид Authorization: Bearer <token>, и веб-консоль запрашивает тот же самый токен.

Контейнер работает, а учетные данные хранятся в открытом виде. Это происходит, когда OOMOL_CONNECT_ENCRYPTION_KEY не доходит до контейнера, так как среда выполнения сохраняет записи с учетными данными в незашифрованном виде вместо того, чтобы отказаться от запуска. Проверьте это на своей установке: подключите провайдера с API-ключом, который вы сможете распознать, а затем выполните поиск в базе данных.

docker compose cp connector:/app/data/connect.sqlite /tmp/connect.sqlite
grep -c 'github_pat_' /tmp/connect.sqlite
shred -u /tmp/connect.sqlite

Значение больше 0 означает, что ключ не действует. Убедитесь, что .env находится в том же каталоге, что и compose.yaml, и что docker compose config отображает нужное значение. Если ключ установлен, тот же поиск вернет 0, так как запись зашифрована с помощью AES-256-GCM (Advanced Encryption Standard, 256-битный ключ, режим Galois/Counter Mode).

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

Агент выдает ошибку при обращении к действию, которое есть в каталоге. Обнаружение и выполнение — это разные процессы. Действие может отображаться в search_actions, но при этом блокироваться OOMOL_CONNECT_ALLOWED_ACTIONS, черным списком или правилами самого токена среды выполнения.

Обновления. Создайте резервную копию тома, измените тег образа на новый релиз, затем выполните docker compose pull && docker compose up -d. Следите за docker compose logs -n 50 connector в ожидании строки о миграции, а затем повторно запустите проверку работоспособности и выполните одно реальное действие, прежде чем снова доверять системе. Откат означает возврат к старому тегу, что работает только в том случае, если вы зафиксировали его версию.

FAQ

Нужен ли публичный домен для самостоятельного хостинга Open Connector?

Для провайдеров, использующих API key, нет: достаточно шлюза на 127.0.0.1. Для OAuth на практике — да. Провайдер перенаправляет браузер на ваш callback URL, поэтому этот URL должен разрешаться из публичного интернета, а провайдеры отклоняют обычные http:// за пределами localhost. Установите OOMOL_CONNECT_ORIGIN в качестве вашего https:// hostname перед первым запуском и зарегистрируйте <origin>/oauth/callback в OAuth-приложении провайдера.

Что произойдет, если я потеряю ключ шифрования Open Connector?

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

Может ли мой AI-агент увидеть токен доступа провайдера?

Нет, если он обращается через шлюз. Агент проходит аутентификацию с помощью runtime-токена, начинающегося с oct_, а шлюз внедряет учетные данные провайдера в исходящий запрос на сервере, возвращая только ответ. Это свойство нарушают две вещи: эндпоинт /v1/proxy/:service, который пересылает необработанные запросы с прикрепленными учетными данными (поэтому права доступа для него по умолчанию пусты), и самостоятельная вставка API key в агента, что полностью исключает шлюз.

Должен ли шлюз быть доступен из публичного интернета?

Только /oauth/callback должен быть доступен. Опубликуйте порт контейнера на 127.0.0.1, чтобы правила NAT в Docker не могли выставить его за пределы вашего межсетевого экрана, и установите перед ним reverse proxy. Затем протестируйте один вызов действия без заголовка authorization. Если он пройдет успешно, ограничьте /api, /v1 и /mcp на прокси адресами, которые используют ваши агенты, пока не останутся только аутентифицированные вызовы.

Готов ли Open Connector к промышленной эксплуатации?

Он распространяется по лицензии Apache 2.0 и быстро развивается: репозиторий появился 29 июня 2026 года, а версия v1.3.3 была выпущена 30 июля 2026 года, поэтому рассматривайте каждый номер версии в этом руководстве как снимок на 1 августа 2026 года. Запускайте его, зафиксировав версию тегом, никогда не используйте latest или tip, читайте примечания к выпуску перед каждым обновлением и храните резервную копию тома, которую вы хотя бы раз восстанавливали. Архитектура надежна для сервера, который вы контролируете, а риск заключается в частоте смены версий, а не в самой архитектуре.