Die Annahme ist, dass ein Here-Dokument mit vier Zeilen eine vollständige nicht-interaktive Anmeldung ist, und sie hält genau so lange, bis das Gateway zwei Gruppen anbietet, ein Banner ausgibt oder nach dem Passwort ein Token verlangt. Dann tippt dasselbe Skript das Passwort in ein Menü, das eine Zahl erwartet hat. Der Kommandozeilenclient unter Linux ist skriptbar, aber nur, wenn man ihm gibt, worauf er tatsächlich wartet, und nicht, was das Tutorial angenommen hat.

Das feste Here-Dokument ist ein Mythos

Ciscos Linux-Client installiert ein kleines Programm unter /opt/cisco/anyconnect/bin/vpn, und Secure Client 5 liefert es weiterhin aus. Läuft der Dienst vpnagentd, liest das Flag -s Antworten von der Standardeingabe in der Reihenfolge, in der der Client sie abfragt. Diese Reihenfolge ist über Server hinweg nicht fest.

Ein Gruppenmenü fügt eine Zeile hinzu. Ein Anmeldebanner fügt eine weitere hinzu. Eine Token-Abfrage nach dem Passwort zerlegt ein Skript mit vier Zeilen vollständig, weil die fünfte Zeile nie das Token sein sollte. Der Fehler ist leise: Der Client erhält die falsche Zeichenkette, der Verbindungsversuch endet, und das Skript meldet nichts Brauchbares.

Führen Sie den Client einmal von Hand gegen dasselbe Gateway und dieselbe Version aus, und notieren Sie die Abfragen in der Reihenfolge, in der sie erscheinen. Dieses Protokoll ist die Spezifikation, die Ihr Skript erfüllen muss.

Worauf der Client wartet

Im üblichen Fall einer einzigen Gruppe lautet die Reihenfolge Benutzername, Passwort und manchmal eine Bannerbestätigung. Die erste Zeile eines Here-Dokuments wird nur dann zu einem Menüindex, wenn der Server tatsächlich ein Menü zeigt, und deshalb funktioniert dasselbe Skript an einem Standort und scheitert am nächsten.

/opt/cisco/anyconnect/bin/vpn -s connect vpn.example.net <<EOF
0
alice
$VPN_PASSWORD
y
EOF

Lesen Sie das als vier Antworten auf vier mögliche Fragen, nicht als festes Protokoll. Die Null ist eine Menüauswahl, kein Passwort, und das abschließende y bestätigt ein Banner, das viele Gateways nie senden.

Verwenden Sie expect, wenn die Reihenfolge nicht vorhersagbar ist

Schwankt die Reihenfolge der Abfragen, zählen Sie keine Zeilen mehr, sondern warten Sie auf die Abfrage. Ein expect-Skript startet den Befehl connect, wartet auf die Benutzernamenabfrage, sendet, wartet auf die Passwortabfrage, sendet und wartet dann auf den verbundenen Zustand. Jedes Warten hat ein eigenes Zeitlimit.

Behandeln Sie eine Zeitüberschreitung als Fehler und brechen Sie ab. Ein Skript, das nach einer unerwarteten Abfrage weiter Eingaben sendet, tippt das Passwort bereitwillig in eine Shell, ein Menü oder was sonst gerade liest. So wird aus einem Anmeldeskript ein Vorfall.

expect liefert Ihnen außerdem ein Protokoll, das Sie mit entferntem Geheimnis mitschreiben können, und das ist mehr wert als der Exit-Code, wenn ein Gateway nach einem Upgrade seine Abfragen ändert.

Das Geheimnis muss irgendwo liegen

Jede Variante dieses Problems hat eine Schwäche: das Passwort muss in einer Form existieren, die der Client lesen kann. Als Argument übergeben steht es in der Prozesstabelle. Durch eine Pipe geleitet landet es oft in der Shell-History. Beides ist für andere lokale Benutzer auf einem gemeinsam genutzten Sprunghost lesbar.

install -m 600 /dev/null ~/.vpn-secret
umask 077; VPN_PASSWORD=$(cat ~/.vpn-secret) /opt/cisco/anyconnect/bin/vpn -s connect vpn.example.net

Eine Datei mit Modus 600 im Besitz des Kontos ist das Minimum, und sie bleibt ein Klartextgeheimnis auf der Platte. secret-tool lookup liest aus dem Linux-Schlüsselring, und unter macOS erledigt security find-generic-password -w dieselbe Aufgabe. Wo die Organisation Zertifikats- oder SAML-Authentifizierung unterstützt, ziehen Sie diese vor, denn ein gespeichertes Passwort plus MFA kann ohne einen Menschen noch keine Anmeldung abschließen.

Bestätigen Sie den Tunnel, dann übernehmen Sie den Abbau

Verlassen Sie sich nicht auf das Fehlen von Fehlertext. Fragen Sie den Client nach seinem Zustand und prüfen Sie die Schnittstelle unabhängig:

/opt/cisco/anyconnect/bin/vpn state
ip -brief address

Der Zustand sollte Connected lauten, und eine Tunnelschnittstelle sollte eine Adresse haben. Fordern Sie dann eine Ressource an, die der Tunnel erreichen soll, denn ein verbundener Zustand ohne Route ist ein reales Ergebnis und leicht zu übersehen.

Trennen Sie mit vpn disconnect, und lassen Sie das Skript, das den Tunnel gestartet hat, den Abbau übernehmen. Ein fehlgeschlagener Job, der eine Sitzung offen lässt, verbraucht einen Lizenzplatz und sieht für den Nächsten, der es versucht, wie ein Authentifizierungsproblem aus.

Reihenfolge der Arbeit: vpnagentd bestätigen, die tatsächliche Reihenfolge der Abfragen erfassen, Here-Dokument oder expect wählen, das Geheimnis aus argv und History heraushalten, Zustand und eine Ressource prüfen, dann im selben Skript trennen.

Ähnliche Artikel