Развёртывание туннелей MCP с помощью Docker Compose
Установите стек туннелей MCP на виртуальную машину с помощью Docker Compose.
В этом руководстве стек туннеля развёртывается в виде защищённых контейнеров на одном хосте. Ту же конфигурацию можно воспроизвести на нескольких хостах для обеспечения доступности.
Перед началом работы
Вам понадобится:
- Туннель. При программном доступе компонент настройки создаёт туннель за вас, если вы не указываете ID туннеля; чтобы вместо этого подключиться к существующему туннелю, создайте его в Claude Console и запишите ID туннеля (
tnl_...). Ручная подготовка всегда начинается с туннеля, созданного в Console. - Способ аутентификации хоста в Tunnels API.
- Программный доступ (рекомендуется). Включите Set up programmatic access при создании туннеля, чтобы компонент настройки мог проходить аутентификацию через Workload Identity Federation. Если вы позволяете компоненту настройки создать туннель, вместо этого создайте правило федерации непосредственно на странице Workload identity (
platform.claude.com/settings/workload-identity-federation) в Claude Console. Запишите ID правила федерации (fdrl_...) и ID вашей организации. - Вручную. Пропустите программный доступ. Вы получите токен туннеля из Console, самостоятельно сгенерируете CA и сертификат сервера и зарегистрируете CA в Console.
- Программный доступ (рекомендуется). Включите Set up programmatic access при создании туннеля, чтобы компонент настройки мог проходить аутентификацию через Workload Identity Federation. Если вы позволяете компоненту настройки создать туннель, вместо этого создайте правило федерации непосредственно на странице Workload identity (
- Хост с установленными Docker и Docker Compose. Для ручного процесса также требуется
openssl(1.1.1 или новее). - Исходящее сетевое подключение от хоста к
api.anthropic.com(443 TCP) и к периферии туннеля (7844 TCP и UDP). См. полные сетевые требования. - Один или несколько серверов MCP, запущенных и доступных с хоста по адресам, которые вы настроите в
routes. Если у вас его ещё нет, используйте пример сервера.
Необязательно: использование примера сервера MCP
Если у вас нет сервера MCP для тестирования, используйте этот минимальный:
mkdir -p mcp-tunnel
cat > mcp-tunnel/hello_server.py <<'EOF'
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-server", host="0.0.0.0", port=9000)
@mcp.tool()
def hello(name: str = "world") -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="streamable-http")
EOFСледующие шаги установки выполняют cd в mcp-tunnel/ и указывают, куда добавить соответствующий сервис и маршрут.
Установка
В этом руководстве представлен один эталонный подход с использованием Docker Compose. Вы несёте ответственность за его адаптацию к требованиям безопасности вашей организации.
Для этого пути на хосте должен быть поставщик удостоверений OIDC (например, сервер метаданных облачной виртуальной машины или SPIFFE). Если его нет, используйте вкладку Без программного доступа.
Компонент настройки использует Workload Identity Federation, чтобы получить токен туннеля, сгенерировать CA и сертификат сервера и зарегистрировать CA в Anthropic.
Подготовьте каталог развёртывания
mkdir -p mcp-tunnel/{config,data} cd mcp-tunnel sudo chown 65532:65532 dataКонтейнеры работают от имени непривилегированного UID
65532и нуждаются в доступе на запись кdata/.Напишите docker-compose.yaml
Файл compose закрепляет образы по дайджесту SHA-256, запускает каждый контейнер от непривилегированного пользователя с файловой системой только для чтения, отбрасывает все возможности (capabilities) Linux и отключает повышение привилегий.
cat > docker-compose.yaml <<'EOF' services: setup: image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:d243d30642d39857a0579e465cb3b9bf3ae7c1141e83143bb6856777a31467a5 entrypoint: ["/setup"] command: - init - --api-url=https://api.anthropic.com - --output=dir:/data - --token-version=1 environment: - TUNNEL_ID - ANTHROPIC_FEDERATION_RULE_ID - ANTHROPIC_ORGANIZATION_ID - ANTHROPIC_WORKSPACE_ID - ANTHROPIC_IDENTITY_TOKEN volumes: - ./data:/data user: "65532:65532" read_only: true security_opt: - no-new-privileges:true cap_drop: - ALL profiles: ["setup"] cloudflared: image: cloudflare/cloudflared@sha256:0e56c13c7058fcef9c4f0e7d83372f82330548dbf8f8b366f3d8b506526d93b2 command: tunnel --no-autoupdate run --url http://localhost:8080 environment: - TUNNEL_TOKEN # Используем общее netns с прокси, чтобы localhost:8080 был доступен. network_mode: "service:mcp-proxy" restart: unless-stopped user: "65532:65532" read_only: true security_opt: - no-new-privileges:true cap_drop: - ALL stop_grace_period: 30s logging: options: max-size: "10m" max-file: "3" mcp-proxy: image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:d243d30642d39857a0579e465cb3b9bf3ae7c1141e83143bb6856777a31467a5 volumes: - ./config/mcp-proxy.yaml:/etc/mcp-gateway/config.yaml:ro - ./data:/data:ro restart: unless-stopped user: "65532:65532" read_only: true security_opt: - no-new-privileges:true cap_drop: - ALL stop_grace_period: 30s logging: options: max-size: "10m" max-file: "3" EOFЕсли вы используете пример сервера MCP, добавьте его как сервис:
cat >> docker-compose.yaml <<'EOF' hello-mcp: image: python:3.13-slim working_dir: /app volumes: - ./hello_server.py:/app/hello_server.py:ro command: sh -c "pip install --quiet mcp && python hello_server.py" restart: unless-stopped EOFПодготовьте туннель
Задайте идентификаторы. Оставьте
TUNNEL_IDнезаданным, чтобы компонент настройки создал туннель; задайте его, чтобы подключиться к существующему туннелю из Console:# export TUNNEL_ID=tnl_... # задайте, чтобы подключиться к существующему туннелю export ANTHROPIC_FEDERATION_RULE_ID=fdrl_... export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000Если ваше правило федерации ограничено рабочим пространством, отличным от рабочего пространства вашей организации по умолчанию, также задайте
ANTHROPIC_WORKSPACE_ID=wrkspc_...; в противном случае компонент настройки использует рабочее пространство по умолчанию. Автоматически созданный туннель создаётся в этом рабочем пространстве.Задайте в
ANTHROPIC_IDENTITY_TOKENOIDC JWT от поставщика удостоверений этого хоста. Следуйте руководству по WIF для вашего поставщика, чтобы зарегистрировать издателя, задать субъект правила и выпустить токен; аудитория правила должна совпадать с аудиторией, которую вы запрашиваете при выпуске.Запустите компонент настройки:
docker compose run --rm setupsetup initидемпотентен относительноdata/: повторный запуск повторно использует уже сохранённые там ID туннеля и CA и никогда не создаёт второй туннель. Новый CA генерируется и регистрируется только тогда, когдаdata/пуст илиTUNNEL_IDизменился; в этом случае действует ограничение в два активных сертификата, поэтому, если оба слота заняты, сначала отзовите один из них в Console.Если возникает ошибка, см. Сбои аутентификации компонента настройки.
Получите домен вашего туннеля и экспортируйте его для последующих шагов:
export TUNNEL_DOMAIN=$(sudo cat data/tunnel-domain) echo "$TUNNEL_DOMAIN"Напишите конфигурацию прокси
tunnel_domainобязателен: прокси использует его, чтобы удалить суффикс домена из входящих имён хостов перед поиском поддомена вroutes.routes— это плоское сопоставление поддомена с URL вышестоящего сервера, а не список.cat > config/mcp-proxy.yaml <<EOF listen_addr: ":8080" log_level: info shutdown_timeout: 30s tunnel_domain: ${TUNNEL_DOMAIN} tls: cert_file: /data/tls.crt key_file: /data/tls.key routes: echo: http://hello-mcp:9000 EOFМаршрут
echo:указывает на пример сервера MCP; замените его своими маршрутами (или добавьте их). Все доступные поля см. в справочнике по конфигурации прокси.Запустите развёртывание
export TUNNEL_TOKEN=$(sudo cat data/tunnel-token) docker compose up -d
Используйте этот процесс, если вы не включили Set up programmatic access, или для локальной разработки и тестирования. Сервиса setup здесь нет.
Получите токен и домен туннеля из Console
На странице сведений о туннеле скопируйте Domain (он имеет вид
abcd1234.tunnel.anthropic.com), затем нажмите значок глаза рядом с Token, чтобы получить токен туннеля, и используйте значок копирования, чтобы скопировать его.Задайте оба значения как переменные оболочки для остальной части руководства:
export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE export TUNNEL_TOKEN='eyJ...'Создайте структуру и сгенерируйте сертификаты
mkdir -p mcp-tunnel/{data,config} cd mcp-tunnelПрокси прослушивает
:8080по обычному WebSocket; рукопожатие внутреннего TLS происходит внутри этого потока WebSocket с использованием этих сертификатов. Anthropic проверяет внутреннее рукопожатие по CA, который вы регистрируете в Console. Альтернативное имя субъекта (Subject Alternative Name, SAN) серверного сертификата должно включать*.<tunnel-domain>, как описано в требованиях к сертификатам.# Самоподписанный CA. Расширения заданы явно, чтобы он удовлетворял требованиям к сертификату # независимо от значений по умолчанию в openssl.cnf дистрибутива. openssl req -x509 -newkey rsa:2048 -nodes \ -keyout data/ca.key -out data/ca.crt \ -days 3650 -subj "/CN=mcp-tunnel-ca" \ -addext "basicConstraints=critical,CA:TRUE" \ -addext "keyUsage=critical,keyCertSign,cRLSign" \ -addext "subjectKeyIdentifier=hash" # Файл расширений для сертификата сервера. Использование -extfile (вместо # -copy_extensions, доступного только в OpenSSL 3.0+) сохраняет работоспособность на # OpenSSL 1.1.x. cat > data/tls.ext <<EOF subjectAltName = DNS:${TUNNEL_DOMAIN},DNS:*.${TUNNEL_DOMAIN} authorityKeyIdentifier = keyid,issuer extendedKeyUsage = serverAuth EOF # Сертификат сервера, подписанный CA openssl req -newkey rsa:2048 -nodes \ -keyout data/tls.key -out /tmp/server.csr \ -subj "/CN=${TUNNEL_DOMAIN}" openssl x509 -req -in /tmp/server.csr \ -CA data/ca.crt -CAkey data/ca.key -CAcreateserial \ -out data/tls.crt -days 90 \ -extfile data/tls.ext # Разрешить контейнеру прокси без root-прав (UID 65532) читать ключ из # bind mount. Без бита чтения для всех контейнер не сможет открыть # файл, принадлежащий хосту. chmod 644 data/tls.keyЗарегистрируйте сертификат CA в Console
На странице сведений о туннеле прокрутите до раздела Certificates и нажмите Add certificate. Загрузите
data/ca.crtнапрямую с помощью Choose file (модальное окно принимает.pem,.crtи.cer) или вставьте его содержимое:cat data/ca.crtЗначок Needs certificate рядом с именем туннеля исчезает после регистрации сертификата. См. Добавление сертификата CA.
Напишите конфигурацию прокси
tunnel_domainобязателен: прокси использует его, чтобы удалить суффикс домена из входящих имён хостов перед поиском поддомена вroutes.routes— это плоское сопоставление поддомена с URL вышестоящего сервера, а не список.cat > config/mcp-proxy.yaml <<EOF listen_addr: ":8080" log_level: info tunnel_domain: ${TUNNEL_DOMAIN} tls: cert_file: /data/tls.crt key_file: /data/tls.key routes: echo: http://hello-mcp:9000 EOFМаршрут
echo:указывает на пример сервера MCP; замените его своими маршрутами (или добавьте их). Все доступные поля см. в справочнике по конфигурации прокси.Напишите docker-compose.yaml
Параметр
network_mode: "service:mcp-proxy"помещает cloudflared в сетевое пространство имён прокси, чтобыlocalhost:8080внутри контейнера cloudflared указывал на прокси. Флаг--url http://localhost:8080задаёт для cloudflared цель перенаправления; без этого флага у cloudflared нет маршрута для входящих запросов, и он возвращает 503.cat > docker-compose.yaml <<'EOF' services: cloudflared: image: cloudflare/cloudflared@sha256:0e56c13c7058fcef9c4f0e7d83372f82330548dbf8f8b366f3d8b506526d93b2 # --url обязателен: в ручном режиме правила ingress не передаются, # поэтому без него cloudflared отвечает 503 на каждый запрос. command: tunnel --no-autoupdate run --url http://localhost:8080 environment: - TUNNEL_TOKEN # Используем общий netns с прокси, чтобы localhost:8080 до него доходил. network_mode: "service:mcp-proxy" restart: unless-stopped user: "65532:65532" read_only: true security_opt: - no-new-privileges:true cap_drop: - ALL stop_grace_period: 30s logging: options: max-size: "10m" max-file: "3" mcp-proxy: image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:d243d30642d39857a0579e465cb3b9bf3ae7c1141e83143bb6856777a31467a5 volumes: - ./config/mcp-proxy.yaml:/etc/mcp-gateway/config.yaml:ro - ./data:/data:ro restart: unless-stopped user: "65532:65532" read_only: true security_opt: - no-new-privileges:true cap_drop: - ALL stop_grace_period: 30s logging: options: max-size: "10m" max-file: "3" EOFЕсли вы используете пример сервера MCP, добавьте его как сервис:
cat >> docker-compose.yaml <<'EOF' hello-mcp: image: python:3.13-slim working_dir: /app volumes: - ./hello_server.py:/app/hello_server.py:ro command: sh -c "pip install --quiet mcp && python hello_server.py" restart: unless-stopped EOFЗапустите развёртывание
docker compose up -d
Файл compose считывает TUNNEL_TOKEN из окружения хоста без значения по умолчанию, поэтому необходимо повторять экспорт в каждой новой оболочке и после перезагрузки.
Для развёртывания на нескольких виртуальных машинах скопируйте каталог mcp-tunnel/ на каждый хост, задайте TUNNEL_TOKEN и выполните docker compose up -d. В программном процессе TUNNEL_TOKEN — это $(sudo cat data/tunnel-token); в ручном процессе — значение, которое вы скопировали из Console. Один и тот же токен туннеля и сертификаты работают на всех репликах.
Проверка развёртывания
Выполните сквозную проверку, вызвав вышестоящий сервер MCP со стороны Anthropic: см. Использование серверов MCP через туннель. С примером сервера MCP маршрутизируемый URL — https://echo.<your-tunnel-domain>/mcp. Если проверка не проходит, см. Устранение неполадок.
Обновления
Выполняйте команды из этого раздела внутри каталога развёртывания mcp-tunnel/.
Ротация токена туннеля
При программном доступе увеличьте --token-version в команде сервиса setup, задайте идентификаторы Workload Identity Federation, выпустите новый OIDC JWT и повторно запустите компонент настройки:
# Отредактируйте docker-compose.yaml: увеличьте целое число в аргументе
# --token-version сервиса setup (например, с --token-version=1 на
# --token-version=2). Бинарный файл setup отказывается выполнять ротацию, если значение
# не изменилось.
# export TUNNEL_ID=tnl_... # задайте, только если вы задавали его при установке
export ANTHROPIC_FEDERATION_RULE_ID=fdrl_...
export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000
# export ANTHROPIC_WORKSPACE_ID=wrkspc_... # если ваше правило ограничено рабочим пространством
# Заново выпустите ANTHROPIC_IDENTITY_TOKEN согласно руководству по провайдеру WIF для вашей
# среды (срок его действия истёк с момента установки).
export ANTHROPIC_IDENTITY_TOKEN=...
docker compose run --rm setup
export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
docker compose up -d cloudflaredАргумент --token-version редактируется в docker-compose.yaml, а не передаётся в командной строке, чтобы новое значение сохранялось для будущих запусков компонента настройки. Компонент настройки проходит аутентификацию через Workload Identity Federation; токена API для отзыва нет.
Без программного доступа нажмите Rotate token на странице сведений о туннеле в Console, затем обновите переменную окружения TUNNEL_TOKEN на каждом хосте и перезапустите cloudflared (docker compose up -d cloudflared).
Продление сертификата
Вы несёте ответственность за отслеживание срока действия и продление сертификата сервера до его истечения.
При программном доступе:
docker compose run --rm setup renew-cert --output=dir:/dataАргументы CLI заменяют command сервиса setup (аргументы init), но сохраняют его entrypoint, поэтому выполняется /setup renew-cert --output=dir:/data.
Без программного доступа подпишите новый сертификат сервера своим существующим CA (CA, зарегистрированный в Console, не меняется) и замените data/tls.crt. Если вы выполняете это из новой оболочки, сначала задайте TUNNEL_DOMAIN.
export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE
openssl req -new -key data/tls.key -out /tmp/server.csr \
-subj "/CN=${TUNNEL_DOMAIN}"
openssl x509 -req -in /tmp/server.csr \
-CA data/ca.crt -CAkey data/ca.key -CAcreateserial \
-out data/tls.crt -days 90 \
-extfile data/tls.extВ обоих процессах прокси периодически проверяет tls.cert_file и автоматически перезагружает его, поэтому перезапуск не требуется.
Следующие шаги
Подключите вышестоящий сервер MCP к Managed Agent или к Messages API.
Рекомендации по усилению защиты, ротация учётных данных и реагирование на взломы.
Диагностика проблем с подключением, TLS и маршрутизацией.
Was this page helpful?