MCP-Tunnel mit Docker Compose bereitstellen
Installiere den MCP-Tunnel-Stack auf einer VM mit Docker Compose.
Diese Anleitung stellt den Tunnel-Stack als gehärtete Container auf einem einzelnen Host bereit. Dieselbe Konfiguration kann für Verfügbarkeit auf mehrere Hosts repliziert werden.
Bevor du beginnst
Du benötigst:
- Einen Tunnel. Mit programmatischem Zugriff erstellt die Setup-Komponente einen für dich, wenn du keine Tunnel-ID angibst; um stattdessen einen bestehenden Tunnel zu verwenden, erstelle ihn in der Claude Console und notiere die Tunnel-ID (
tnl_...). Die manuelle Bereitstellung beginnt immer mit einem in der Console erstellten Tunnel. - Eine Möglichkeit für den Host, sich bei der Tunnels API zu authentifizieren.
- Programmatischer Zugriff (empfohlen). Aktiviere Set up programmatic access beim Erstellen des Tunnels, damit sich die Setup-Komponente über Workload Identity Federation authentifizieren kann. Wenn du die Setup-Komponente den Tunnel erstellen lässt, erstelle die Federation-Regel stattdessen direkt auf der Seite Workload identity (
platform.claude.com/settings/workload-identity-federation) in der Claude Console. Notiere die ID der Federation-Regel (fdrl_...) und deine Organisations-ID. - Manuell. Überspringe den programmatischen Zugriff. Du holst das Tunnel-Token aus der Console, generierst selbst eine CA und ein Serverzertifikat und registrierst die CA in der Console.
- Programmatischer Zugriff (empfohlen). Aktiviere Set up programmatic access beim Erstellen des Tunnels, damit sich die Setup-Komponente über Workload Identity Federation authentifizieren kann. Wenn du die Setup-Komponente den Tunnel erstellen lässt, erstelle die Federation-Regel stattdessen direkt auf der Seite Workload identity (
- Einen Host mit installiertem Docker und Docker Compose. Der manuelle Ablauf erfordert außerdem
openssl(1.1.1 oder neuer). - Ausgehende Netzwerkkonnektivität vom Host zu
api.anthropic.com(443 TCP) und zum Tunnel-Edge (7844 TCP und UDP). Siehe die vollständigen Netzwerkanforderungen. - Einen oder mehrere MCP-Server, die laufen und vom Host unter den Adressen erreichbar sind, die du unter
routeskonfigurierst. Wenn du noch keinen hast, verwende den Beispielserver.
Optional: Einen Beispiel-MCP-Server verwenden
Wenn dir kein MCP-Server zum Testen zur Verfügung steht, verwende diesen minimalen:
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")
EOFDie folgenden Installationsschritte wechseln mit cd in mcp-tunnel/ und geben an, wo der entsprechende Service und die Route hinzuzufügen sind.
Installation
Diese Anleitung bietet einen Referenzansatz mit Docker Compose. Du bist dafür verantwortlich, ihn an die Sicherheitsanforderungen deiner Organisation anzupassen.
Dieser Weg erfordert, dass der Host über einen OIDC-Identitätsanbieter verfügt (etwa einen Metadatenserver einer Cloud-VM oder SPIFFE). Falls nicht, verwende stattdessen den Tab Ohne programmatischen Zugriff.
Die Setup-Komponente verwendet Workload Identity Federation, um das Tunnel-Token abzurufen, eine CA und ein Serverzertifikat zu generieren und die CA bei Anthropic zu registrieren.
Bereitstellungsverzeichnis vorbereiten
mkdir -p mcp-tunnel/{config,data} cd mcp-tunnel sudo chown 65532:65532 dataDie Container laufen als Nicht-Root-UID
65532und benötigen Schreibzugriff aufdata/.docker-compose.yaml schreiben
Die Compose-Datei pinnt Images per SHA-256-Digest, führt jeden Container als Nicht-Root mit schreibgeschütztem Dateisystem aus, entzieht alle Linux-Capabilities und deaktiviert die Rechteausweitung.
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 # Teile den netns des Proxys, damit localhost:8080 ihn erreicht. 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" EOFWenn du den Beispiel-MCP-Server verwendest, füge ihn als Service hinzu:
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 EOFTunnel bereitstellen
Setze die Bezeichner. Lass
TUNNEL_IDungesetzt, damit die Setup-Komponente einen Tunnel erstellt; setze sie, um einen bestehenden Tunnel aus der Console anzubinden:# export TUNNEL_ID=tnl_... # setzen, um an einen bestehenden Tunnel anzuhängen export ANTHROPIC_FEDERATION_RULE_ID=fdrl_... export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000Wenn deine Federation-Regel auf einen anderen Workspace als den Standard-Workspace deiner Organisation beschränkt ist, setze zusätzlich
ANTHROPIC_WORKSPACE_ID=wrkspc_...; andernfalls verwendet die Setup-Komponente den Standard-Workspace. Ein automatisch erstellter Tunnel wird in diesem Workspace erstellt.Setze
ANTHROPIC_IDENTITY_TOKENauf ein OIDC-JWT vom Identitätsanbieter dieses Hosts. Folge der WIF-Anleitung für deinen Anbieter, um den Issuer zu registrieren, das Subject der Regel festzulegen und das Token zu erzeugen; die Audience der Regel muss mit der Audience übereinstimmen, die du beim Erzeugen anforderst.Führe die Setup-Komponente aus:
docker compose run --rm setupsetup initist bezüglichdata/idempotent: Ein erneutes Ausführen verwendet die dort bereits gespeicherte Tunnel-ID und CA wieder und erstellt nie einen zweiten Tunnel. Eine neue CA wird nur generiert und registriert, wenndata/leer ist oder sichTUNNEL_IDgeändert hat; in diesem Fall gilt die Obergrenze von zwei aktiven Zertifikaten, widerrufe also zuerst eines in der Console, wenn beide Plätze belegt sind.Siehe Authentifizierungsfehler der Setup-Komponente, falls ein Fehler auftritt.
Rufe deine Tunnel-Domain ab und exportiere sie für spätere Schritte:
export TUNNEL_DOMAIN=$(sudo cat data/tunnel-domain) echo "$TUNNEL_DOMAIN"Proxy-Konfiguration schreiben
tunnel_domainist erforderlich: Der Proxy verwendet es, um das Domain-Suffix von eingehenden Hostnamen zu entfernen, bevor er die Subdomain inroutesnachschlägt.routesist eine flache Zuordnung von Subdomain zu Upstream-URL, keine Liste.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 EOFDie Route
echo:zielt auf den Beispiel-MCP-Server; ersetze sie durch deine eigenen Routen (oder füge diese hinzu). Siehe die Referenz zur Proxy-Konfiguration für alle verfügbaren Felder.Bereitstellung starten
export TUNNEL_TOKEN=$(sudo cat data/tunnel-token) docker compose up -d
Verwende diesen Ablauf, wenn du Set up programmatic access nicht aktiviert hast, oder für lokale Entwicklung und Tests. Es gibt keinen setup-Service.
Tunnel-Token und Domain aus der Console abrufen
Kopiere auf der Detailseite des Tunnels die Domain (sie hat die Form
abcd1234.tunnel.anthropic.com), klicke dann auf das Augensymbol neben Token, um das Tunnel-Token abzurufen, und verwende das Kopiersymbol, um es zu kopieren.Setze beide als Shell-Variablen für den Rest der Anleitung:
export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE export TUNNEL_TOKEN='eyJ...'Gerüst erstellen und Zertifikate generieren
mkdir -p mcp-tunnel/{data,config} cd mcp-tunnelDer Proxy lauscht auf
:8080über einfaches WebSocket; der innere TLS-Handshake findet innerhalb dieses WebSocket-Streams mit diesen Zertifikaten statt. Anthropic verifiziert den inneren Handshake gegen die CA, die du in der Console registrierst. Der Subject Alternative Name (SAN) des Serverzertifikats muss*.<tunnel-domain>enthalten, wie in den Zertifikatsanforderungen beschrieben.# Selbstsignierte CA. Explizite Extensions, damit sie die Zertifikats- # anforderungen unabhängig von den openssl.cnf-Defaults der Distro erfüllt. 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" # Extension-Datei für das Server-Zertifikat. Die Verwendung von -extfile (statt # -copy_extensions, das nur ab OpenSSL 3.0 verfügbar ist) sorgt dafür, dass dies auch # mit OpenSSL 1.1.x funktioniert. cat > data/tls.ext <<EOF subjectAltName = DNS:${TUNNEL_DOMAIN},DNS:*.${TUNNEL_DOMAIN} authorityKeyIdentifier = keyid,issuer extendedKeyUsage = serverAuth EOF # Von der CA signiertes Server-Zertifikat 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 # Erlaube dem Nicht-Root-Proxy-Container (UID 65532), den Schlüssel aus # dem Bind-Mount zu lesen. Ohne das World-Read-Bit kann der Container # keine Datei öffnen, die dem Host gehört. chmod 644 data/tls.keyCA-Zertifikat in der Console registrieren
Scrolle auf der Detailseite des Tunnels zum Abschnitt Certificates und klicke auf Add certificate. Lade
data/ca.crtdirekt über Choose file hoch (das modale Fenster akzeptiert.pem,.crtund.cer) oder füge den Inhalt ein:cat data/ca.crtDas Badge Needs certificate neben dem Namen des Tunnels verschwindet, sobald ein Zertifikat registriert ist. Siehe CA-Zertifikat hinzufügen.
Proxy-Konfiguration schreiben
tunnel_domainist erforderlich: Der Proxy verwendet es, um das Domain-Suffix von eingehenden Hostnamen zu entfernen, bevor er die Subdomain inroutesnachschlägt.routesist eine flache Zuordnung von Subdomain zu Upstream-URL, keine Liste.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 EOFDie Route
echo:zielt auf den Beispiel-MCP-Server; ersetze sie durch deine eigenen Routen (oder füge diese hinzu). Siehe die Referenz zur Proxy-Konfiguration für alle verfügbaren Felder.docker-compose.yaml schreiben
Die Einstellung
network_mode: "service:mcp-proxy"platziert cloudflared im Netzwerk-Namespace des Proxys, sodasslocalhost:8080innerhalb des cloudflared-Containers den Proxy erreicht. Das Flag--url http://localhost:8080gibt cloudflared sein Weiterleitungsziel; ohne dieses Flag hat cloudflared keine Route für eingehende Anfragen und gibt einen 503 zurück.cat > docker-compose.yaml <<'EOF' services: cloudflared: image: cloudflare/cloudflared@sha256:0e56c13c7058fcef9c4f0e7d83372f82330548dbf8f8b366f3d8b506526d93b2 # --url ist erforderlich: Im manuellen Ablauf werden keine Ingress-Regeln gepusht, # daher beantwortet cloudflared ohne es jede Anfrage mit 503. command: tunnel --no-autoupdate run --url http://localhost:8080 environment: - TUNNEL_TOKEN # Teile den netns des Proxys, damit localhost:8080 ihn erreicht. 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" EOFWenn du den Beispiel-MCP-Server verwendest, füge ihn als Service hinzu:
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 EOFBereitstellung starten
docker compose up -d
Die Compose-Datei liest TUNNEL_TOKEN ohne Standardwert aus der Host-Umgebung, daher musst du den Export in jeder neuen Shell und nach einem Neustart wiederholen.
Für eine Bereitstellung auf mehreren VMs kopierst du das Verzeichnis mcp-tunnel/ auf jeden Host, setzt TUNNEL_TOKEN und führst docker compose up -d aus. Im programmatischen Ablauf ist TUNNEL_TOKEN gleich $(sudo cat data/tunnel-token); im manuellen Ablauf ist es der Wert, den du aus der Console kopiert hast. Dasselbe Tunnel-Token und dieselben Zertifikate funktionieren über alle Replikate hinweg.
Bereitstellung verifizieren
Verifiziere Ende-zu-Ende, indem du einen Upstream-MCP-Server von Anthropics Seite aus aufrufst: siehe Die getunnelten MCP-Server verwenden. Mit dem Beispiel-MCP-Server lautet die geroutete URL https://echo.<your-tunnel-domain>/mcp. Wenn die Verifizierung fehlschlägt, siehe Fehlerbehebung.
Upgrades
Führe die Befehle in diesem Abschnitt innerhalb des Bereitstellungsverzeichnisses mcp-tunnel/ aus.
Tunnel-Token rotieren
Mit programmatischem Zugriff erhöhst du --token-version im Befehl des setup-Service, setzt die Workload-Identity-Federation-Bezeichner, erzeugst ein neues OIDC-JWT und führst die Setup-Komponente erneut aus:
# Bearbeite docker-compose.yaml: Erhöhe die Ganzzahl im Argument des setup-Service
# --token-version (zum Beispiel --token-version=1 auf
# --token-version=2). Das setup-Binary verweigert die Rotation, wenn sich der Wert
# nicht geändert hat.
# export TUNNEL_ID=tnl_... # nur setzen, wenn du es bei der Installation gesetzt hast
export ANTHROPIC_FEDERATION_RULE_ID=fdrl_...
export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000
# export ANTHROPIC_WORKSPACE_ID=wrkspc_... # falls deine Regel auf einen Workspace beschränkt ist
# Erzeuge ANTHROPIC_IDENTITY_TOKEN neu gemäß dem WIF-Provider-Leitfaden für deine
# Umgebung (es ist seit der Installation abgelaufen).
export ANTHROPIC_IDENTITY_TOKEN=...
docker compose run --rm setup
export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
docker compose up -d cloudflaredDas Argument --token-version wird in docker-compose.yaml bearbeitet, statt auf der Kommandozeile übergeben zu werden, damit der neue Wert für zukünftige Ausführungen der Setup-Komponente erhalten bleibt. Die Setup-Komponente authentifiziert sich mit Workload Identity Federation; es gibt kein API-Token zu widerrufen.
Ohne programmatischen Zugriff klickst du auf der Detailseite des Tunnels in der Console auf Rotate token, aktualisierst dann die Umgebungsvariable TUNNEL_TOKEN auf jedem Host und startest cloudflared neu (docker compose up -d cloudflared).
Zertifikatserneuerung
Du bist dafür verantwortlich, den Ablauf zu überwachen und das Serverzertifikat vor seinem Ablauf zu erneuern.
Mit programmatischem Zugriff:
docker compose run --rm setup renew-cert --output=dir:/dataDie CLI-Argumente ersetzen den command des setup-Service (die init-Argumente), behalten aber dessen entrypoint bei, sodass dies /setup renew-cert --output=dir:/data ausführt.
Ohne programmatischen Zugriff signierst du ein neues Serverzertifikat mit deiner bestehenden CA (die in der Console registrierte CA ändert sich nicht) und ersetzt data/tls.crt. Setze zuerst TUNNEL_DOMAIN, wenn du dies in einer neuen Shell ausführst.
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.extIn beiden Abläufen fragt der Proxy tls.cert_file regelmäßig ab und lädt es automatisch neu, sodass kein Neustart erforderlich ist.
Nächste Schritte
Binde einen Upstream-MCP-Server an einen Managed Agent oder die Messages API an.
Hinweise zur Härtung, Rotation von Anmeldedaten und Reaktion auf Sicherheitsvorfälle.
Diagnostiziere Probleme mit Konnektivität, TLS und Routing.
Was this page helpful?