Engineering-Notiz

Eine lokale API per SSH-Reverse-Tunnel mit dem Cloud-Mac verbinden

Remote Mac ·ca. 5 Min. Lesezeit

Eine lokale API per SSH-Reverse-Tunnel mit dem Cloud-Mac verbinden

Auf dem lokalen Entwicklungsrechner läuft eine noch nicht bereitgestellte API, während sich das Xcode-Projekt und die Build-Skripte auf einem Cloud-Mac befinden. Eine Portfreigabe am Router einzurichten ist umständlich und vergrößert die Angriffsfläche. Auch die Adresse im Repository vorübergehend durch eine öffentliche Domain zu ersetzen, kann leicht unerwünschte Konfigurationsänderungen hinterlassen. Zuverlässiger ist es, die SSH-Verbindung vom Entwicklungsrechner aus aufzubauen und den lokalen Port über einen Reverse-Tunnel an die Loopback-Adresse des Cloud-Mac weiterzuleiten.

Zuerst die Richtung des Datenverkehrs klären

Ein Reverse-Tunnel ermöglicht den Zugriff aus der Cloud auf einen lokalen Dienst. Angenommen, die API läuft auf dem Entwicklungsrechner unter 127.0.0.1:8080 und soll auf dem Cloud-Mac über 127.0.0.1:18080 erreichbar sein. Die SSH-Verbindung wird vom Entwicklungsrechner zum Cloud-Mac aufgebaut und leitet dort eingehenden Datenverkehr zurück an den lokalen Port.

Bei einer lokalen Portweiterleitung ist die Richtung umgekehrt: Der Entwicklungsrechner greift auf einen Debug-Dienst zu, der auf dem Cloud-Mac ausschließlich an die Loopback-Adresse gebunden ist. Läuft der Cloud-Dienst beispielsweise unter 127.0.0.1:9000, kann er auf dem Entwicklungsrechner als 127.0.0.1:19000 bereitgestellt werden.

Ziel SSH-Option Ausgangspunkt der Verbindung Zugriffspunkt
Cloud-Mac greift auf lokale API zu -R Entwicklungsrechner Cloud-Mac unter 127.0.0.1:18080
Lokaler Zugriff auf Debug-Dienst des Cloud-Mac -L Entwicklungsrechner Lokal unter 127.0.0.1:19000

Prüfe zunächst mit einem echten Client, ob der Quelldienst erreichbar ist. Beginne nicht sofort mit der Fehlersuche am Tunnel:

curl -i http://127.0.0.1:8080/health
lsof -nP -iTCP:8080 -sTCP:LISTEN

Falls die Anwendung keinen Health-Check-Endpunkt anbietet, kannst du vorübergehend einen Testdienst starten, der ausschließlich lokal lauscht:

python3 -m http.server 8080 --bind 127.0.0.1

Einen Reverse-Tunnel mit minimaler Exposition einrichten

Lege auf dem Entwicklungsrechner die Adresse des Cloud-Mac fest und führe anschließend den folgenden Befehl aus:

export CLOUD_MAC_IP="你的节点地址"
ssh -N \
  -o ExitOnForwardFailure=yes \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -R 127.0.0.1:18080:127.0.0.1:8080 \
  dev@"$CLOUD_MAC_IP"

-N verhindert die Ausführung eines Befehls auf dem entfernten System; die Verbindung dient ausschließlich der Weiterleitung. ExitOnForwardFailure sorgt dafür, dass bei einem fehlgeschlagenen Port-Binding keine scheinbar funktionierende SSH-Sitzung bestehen bleibt. Die beiden Keepalive-Optionen helfen dabei, ausgefallene Verbindungen zu erkennen, stellen den Tunnel nach einer Netzwerkunterbrechung jedoch nicht automatisch wieder her.

Öffne zur Überprüfung ein weiteres Terminal auf dem Cloud-Mac:

lsof -nP -iTCP:18080 -sTCP:LISTEN
curl -i http://127.0.0.1:18080/health

In der Ausgabe von lsof sollte als Listen-Adresse 127.0.0.1:18080 und nicht *:18080 erscheinen. Hinterlege die Adresse für die gemeinsame Entwicklung außerdem in einer Umgebungsvariable der Entwicklungsumgebung, statt sie fest in den Quellcode zu schreiben:

export DEV_API_BASE_URL="http://127.0.0.1:18080"
xcodebuild -scheme DemoApp -configuration Debug test

Der Tunnel verschlüsselt lediglich die Übertragung und beschränkt den Zugriffspunkt. Er ersetzt nicht die Authentifizierung der API. Auch wenn der Port ausschließlich an die Loopback-Adresse gebunden ist, sollten Test-Token, Berechtigungsprüfungen und bereinigte Protokolle weiterhin verwendet werden.

Bedienfehler mit einer SSH-Konfiguration vermeiden

Bei der wiederholten Eingabe langer Befehle werden lokaler Port, entfernter Port und Zieladresse leicht vertauscht. Lege deshalb auf dem Entwicklungsrechner einen eigenen Eintrag in ~/.ssh/config an:

Host minid-api-tunnel
    HostName CLOUD_MAC_IP
    User dev
    RemoteForward 127.0.0.1:18080 127.0.0.1:8080
    ExitOnForwardFailure yes
    ServerAliveInterval 30
    ServerAliveCountMax 3

Ersetze CLOUD_MAC_IP durch die tatsächliche Adresse des Knotens und stelle sicher, dass die Dateiberechtigungen korrekt gesetzt sind:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/config
ssh -N minid-api-tunnel

Bündele in einem Eintrag nicht zahlreiche voneinander unabhängige Weiterleitungen. Verwende für jedes Projekt einen eigenen Alias und teile die Ports nach Verwendungszweck auf, beispielsweise 18080 für die API und 19000 für ein Debug-Dashboard. So lässt sich anhand eines offenen Ports leichter erkennen, zu welchem Projekt er gehört. Gleichzeitig sinkt auf gemeinsam genutzten Knoten das Risiko von Portkonflikten.

Wenn du von deinem Entwicklungsrechner auf einen Dienst des Cloud-Mac zugreifen musst, richte dafür eine separate lokale Portweiterleitung ein:

ssh -N \
  -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:19000:127.0.0.1:9000 \
  dev@"$CLOUD_MAC_IP"

Rufe anschließend nur auf dem Entwicklungsrechner http://127.0.0.1:19000 auf. Bei keiner der beiden Weiterleitungsarten muss der jeweilige Anwendungsdienst an eine öffentliche Netzwerkschnittstelle gebunden werden.

Verbindungsfehler schichtweise untersuchen

Probleme mit einem Tunnel sollten in drei Schichten geprüft werden: Quelldienst, SSH-Weiterleitung und Ziel-Client. So vermeidest du, Firewall, Anwendungskonfiguration und Projektcode gleichzeitig zu verändern.

Quelldienst

Führe auf dem Entwicklungsrechner curl und lsof aus. Lauscht die API ausschließlich über IPv6 auf ::1, während als Weiterleitungsziel 127.0.0.1 eingetragen ist, wird die Verbindung abgewiesen. Vereinheitliche in diesem Fall das verwendete Protokoll oder ändere das Weiterleitungsziel auf die tatsächlich verwendete Adresse. Prüfe außerdem, ob ein lokaler Proxy den Loopback-Datenverkehr übernimmt. Zum Gegencheck kannst du vorübergehend folgenden Befehl verwenden:

curl --noproxy '*' -i http://127.0.0.1:8080/health

SSH-Weiterleitung

Aktiviere eine ausführliche Ausgabe, um das Ergebnis der Portanforderung zu prüfen:

ssh -vv -N \
  -o ExitOnForwardFailure=yes \
  -R 127.0.0.1:18080:127.0.0.1:8080 \
  dev@"$CLOUD_MAC_IP"

Wird gemeldet, dass die entfernte Portweiterleitung fehlgeschlagen ist, probiere zunächst einen freien Port aus. Schlägt auch das fehl, prüfe, ob der SSH-Dienst auf dem Cloud-Mac TCP-Weiterleitungen erlaubt. Binde den Port nicht aus Bequemlichkeit an eine öffentliche Adresse und erlaube auch nicht pauschal den Zugriff aus allen Quellen.

Ziel-Client

Ein erfolgreicher curl-Aufruf im Terminal des Cloud-Mac bedeutet nicht, dass alle Ausführungsziele automatisch unter denselben Netzwerkbedingungen arbeiten. Build-Skripte, grafische Anwendungen und Simulatoren können unterschiedliche Umgebungskonfigurationen verwenden. HTTP-Anfragen können zudem von den Sicherheitsrichtlinien des Projekts beeinflusst werden. Protokolliere deshalb separat den aufrufenden Prozess, die Ziel-URL, den Rückgabecode und das Zeitlimit. Erst danach solltest du entscheiden, ob ein Netzwerkfehler oder eine Ablehnung auf Anwendungsebene vorliegt.

Abschließende Prüfungen und Aufräumen

Beende nach Abschluss der gemeinsamen Tests zunächst alle Aufgaben, die vom Tunnel abhängen, und schließe danach die SSH-Sitzung. Prüfe mit lsof, ob der weitergeleitete Port verschwunden ist. Entferne außerdem alle temporären Adressen, Test-Token und Debug-Protokolle aus dem Projekt.

Kontrolliere vor dem Commit mindestens die folgenden Punkte:

  • Die API lauscht weiterhin ausschließlich an der Loopback-Adresse des Entwicklungsrechners.
  • Der weitergeleitete Port auf dem Cloud-Mac lauscht ausschließlich auf 127.0.0.1.
  • Die Berechtigungen des privaten SSH-Schlüssels stehen auf 600, die des Konfigurationsverzeichnisses auf 700.
  • Das Projekt wählt die Adresse für gemeinsame Tests über eine Umgebungsvariable; die Release-Konfiguration verweist nicht auf diesen Port.
  • Die Protokolle enthalten weder Anfrage-Token noch Schlüssel, vollständige Benutzerdaten oder Authentifizierungsheader.
  • Bei gemeinsam genutzten Projekten sind Portnummer, verantwortliche Person und Endzeit dokumentiert.

Dieses Verfahren eignet sich für temporäre APIs, die Prüfung von Callbacks und gemeinsame Tests mit entfernten Build-Systemen. Muss ein Dienst dauerhaft von mehreren Knoten aufgerufen werden, sollte stattdessen eine reguläre Bereitstellung mit eigener Authentifizierung, Zugriffskontrolle und Audit-Protokollierung eingerichtet werden. Ein temporärer Tunnel ist kein Ersatz für einen produktiven Zugangspunkt.

Häufig gestellte Fragen

Wird die lokale API durch den SSH-Reverse-Tunnel öffentlich erreichbar?

Nicht, wenn der entfernte Listener ausdrücklich an 127.0.0.1 gebunden ist. Dann ist der Port nur auf dem Cloud-Mac erreichbar. Eine Bindung an 0.0.0.0 sollte vermieden werden.

Was ist zu prüfen, wenn der weitergeleitete Port nicht antwortet?

Zuerst muss der Quelldienst auf dem Entwicklerrechner laufen. Danach zeigt lsof auf dem Cloud-Mac, ob der Zielport lauscht. Häufige Ursachen sind Portkonflikte oder deaktivierte TCP-Weiterleitung.

Kann eine aus Xcode gestartete App den Tunnel direkt verwenden?

Build-Skripte auf dem Cloud-Mac können die lokale Tunneladresse verwenden. Für Simulatoren und Apps müssen zusätzlich deren Netzwerkkontext und die HTTP-Sicherheitsregeln des Projekts geprüft werden.

MiniD Cloud Mac

Dedizierten physischen Mac mini tage-, wochen- oder monatsweise mieten

Jeder Plan läuft auf einem dedizierten physischen Mac mini mit Remote-Desktop- und SSH-Zugang; aktuelle Modelle, Regionen und Laufzeiten stehen auf der Bestellseite.

Bestelloptionen ansehen