Ein Skript, das OpenVPN startet und nie eine Verbindung aufbaut, ist meist kein Netzwerkproblem: Der Client sitzt an einer Eingabeaufforderung, ohne dass ein Terminal sie zeichnen könnte. OpenVPN kennt genau zwei unterstützte Wege, diese Aufforderung ohne Menschen zu beantworten – eine Anmeldedatei hinter auth-user-pass und den Management-Socket – und sie sind nicht austauschbar. Die Datei kostet eine Direktive und beantwortet eine Aufforderung; der Socket beantwortet jede Aufforderung und gibt allem, was einen Loopback-Port öffnen kann, einen nicht authentifizierten Steuerkanal. Welcher in Ihre Bereitstellung gehört, hängt davon ab, welche Aufforderung tatsächlich blockiert, und davon, welches Geheimnis Sie im Ruhezustand liegen lassen können.

Benennen Sie die Aufforderung, bevor Sie einen Mechanismus wählen

Vier Aufforderungen sehen auf einem Terminal gleich aus. Das Kontopaar stammt aus der PAM- oder auth-user-pass-verify-Konfiguration des Servers und wird von auth-user-pass beantwortet. Eine Passphrase für einen privaten Schlüssel gehört zur Schlüsseldatei und wird über --askpass gelesen oder von Hand getippt. Ein PKCS#12-Bündel verlangt ein eigenes Passwort. Proxy-Authentifizierung nutzt die auth-Datei, die --http-proxy entgegennimmt, nicht die Kontozugangsdaten.

Wenn OpenVPN als Kind von systemd, PHP oder Node läuft, gibt es kein Terminal, also blockiert jede dieser Aufforderungen stumm. Der Prozess sitzt da, das Protokoll endet nach der Cipher-Zeile, und die Unit läuft schließlich in einen Timeout; diese Stille ist der Grund, warum Leute eine Wiederholungsschleife oder ein sleep ergänzen, statt nachzusehen, welche Aufforderung aussteht. Nur der Management-Socket kann alle vier beantworten. Die Anmeldedatei beantwortet die erste und sonst nichts.

auth-user-pass: eine Datei, eine Aufforderung

Die Datei enthält den Benutzernamen in Zeile eins und das Passwort in Zeile zwei, sonst nichts: keine Anführungszeichen, keine Schlüsselnamen, keinen Kommentar. Setzen Sie Modus 600 und machen Sie den Eigentümer zum Konto, das openvpn ausführt, was bei einem Dienst oft ein eigener Benutzer und nicht Ihr Login ist. Eine root-eigene Datei unter /root ist der Unterschied zwischen einem Befehl, der in Ihrer Shell funktioniert, und einer Unit, die beim Start scheitert.

Zwei Details machen aus einer korrekten Datei eine Aufforderung. Ein Editor, der CRLF speichert, lässt einen Wagenrücklauf am Passwort kleben, sodass der Server ein Passwort ablehnt, das in der Datei korrekt aussieht; prüfen Sie mit cat -A oder od -c statt mit dem Auge. Und ein argumentloses auth-user-pass innerhalb des Profils bedeutet konstruktionsgemäß «frag den Benutzer», was eine zweite Direktive auf der Kommandozeile möglicherweise nicht überstimmt, weil das Profil dort geparst wird, wo es liegt.

Die Reihenfolge der Optionen ist die dritte Falle, und sie ist versionsabhängig. Manche OpenVPN-2.4-Builds ignorieren --auth-user-pass, wenn es vor --config steht, sodass dieselben zwei Optionen in der anderen Reihenfolge verbinden. Setzen Sie die Direktive ins Profil oder auf der Kommandozeile hinter --config, und grepen Sie das Profil nach jedem Vorkommen, bevor Sie sonst etwas ändern.

auth-nocache gehört zu diesem Mechanismus, liefert aber nichts. Es begrenzt, wie lange der Client die Zugangsdaten im Speicher hält, was nach einem Absturz hilft und überhaupt nicht, wenn die Datei fehlt. Eine Wiederverbindung nach einem serverseitigen Sitzungs-Timeout liest die Datei erneut, sodass Zugangsdaten, die beim Start funktionierten, mitten am Tag ablaufen können.

Der Management-Socket: jede Aufforderung, mehr Angriffsfläche

Starten Sie den Client mit einem Listener und erklären Sie ausdrücklich, dass Aufforderungen darüber gemeldet werden, mit den Direktiven, die im OpenVPN-2.6-Handbuch dokumentiert sind:

management 127.0.0.1 3030
management-query-passwords

Halten Sie unter bash die Verbindung auf einem freien Dateideskriptor und antworten Sie nach Aufforderungsnamen statt nach Position, weil eine Wiederverbindung in anderer Reihenfolge fragen kann:

exec 3<>/dev/tcp/127.0.0.1/3030
while read -r -u 3 line; do
  case "$line" in
    *"Need 'Auth' username/password"*)
      echo 'username "Auth" alice' >&3
      echo 'password "Auth" secret' >&3 ;;
    *"Need 'Private Key' password"*)
      echo 'password "Private Key" secret' >&3 ;;
  esac
done

Die Grenze ist der Transport. Das Management-Protokoll ist Klartext über TCP auf Loopback, also darf der Listener nicht auf einer LAN- oder Tunneladresse veröffentlicht werden, damit ein entfernter Controller ihn erreicht; ein entfernter Controller bedeutet eine SSH-Weiterleitung, keine breitere Bindung. Der Community-Daemon akzeptiert außerdem nur einen Management-Client gleichzeitig, sodass eine GUI oder ein Monitor den Socket nicht mit Ihrem Skript teilen kann. Die zweite Verbindung wird abgelehnt, nicht eingereiht, und der Fehler sieht aus wie ein Skript, das taub geworden ist.

Dieser Socket ist auch ein Steuerkanal. Ohne Management-Passwort kann alles, was sich mit 127.0.0.1:3030 verbinden kann, den Status lesen, den Tunnel beenden oder selbst eine Aufforderung beantworten. Das Management-Passwort und die Anmeldedatei sind dieselbe Klasse von Geheimnis, und beide verdienen dieselbe Sorgfalt.

Welcher zur Bereitstellung passt

Wählen Sie die Anmeldedatei, wenn die einzige Aufforderung das Kontopaar ist, der Client als Dienst mit stabilem Eigentümer läuft und Sie eine 600-Datei aus Sicherungen heraushalten können, die Sie nicht kontrollieren. Wählen Sie den Management-Socket, wenn eine Schlüssel-Passphrase oder ein PKCS#12-Passwort im Spiel ist, wenn der Wrapper den Prozess bereits überwacht oder wenn Sie Status und Aufforderungen beobachten müssen, während Sie eine Wiederverbindung debuggen.

Zwei Bedingungen verschieben die Entscheidung. Ist der Wrapper /bin/sh, eine systemd-Unit, die die Binärdatei direkt aufruft, oder ein minimaler Container, dann ist der nur-bash-Socket-Kleber nicht verfügbar und die Datei die billigere Antwort. Rotiert das Geheimnis häufig, ist keine der Dateien ein gutes Zuhause dafür: Fragen Sie einen Schlüsselbund aus einem kleinen Wrapper ab und lassen Sie die Unit den Wrapper aufrufen, was die Rotation an einer Stelle hält und das Passwort aus der Shell-Historie.

Prozesssubstitution steht zwischen beiden und gehört zu keinem. Der Einzeiler unten funktioniert, weil bash ihn erweitert, bevor openvpn startet, nicht weil der Client die Syntax versteht:

openvpn --config client.ovpn --auth-user-pass <(printf "%s
%s
" "$USER" "$PASS")

Er bricht unter /bin/sh, in einer Unit, die die Shell umgeht, und in Images ohne bash oder /dev/fd. Behalten Sie ihn für interaktive Tests, niemals als den Vertrag, von dem ein Dienst abhängt.

Schützen Sie das Geheimnis, dann prüfen Sie im Protokoll

Beide Mechanismen hinterlassen ein Geheimnis, das der OpenVPN-Prozess lesen kann. Setzen Sie das Passwort niemals auf die Kommandozeile oder in die Unit-Datei, wo ps und systemctl es jedem lokalen Benutzer zeigen; übergeben Sie den Pfad zur Datei, nicht den Wert darin. Wo Zertifikate das Kontopaar ersetzen können, entfernt das die Kategorie der Aufforderung, statt sie zu verstecken.

Ein korrekter Lauf zeigt die Aufforderungssequenz als abgeschlossen und die Sitzung bis «Initialization Sequence Completed». Bleibt der Client an derselben Aufforderung stehen, ist der Abgleich fehlgeschlagen: Geben Sie die genauen Zeilen aus, die der Socket gesendet hat, prüfen Sie Quoting und Aufforderungsnamen und korrigieren Sie das Muster, statt ein sleep hinzuzufügen. Ein Tunnel, der Erfolg meldet und nichts überträgt, ist ein Routing- oder Push-Problem, kein Zugangsdatenproblem.

Testen Sie dann die Wiederverbindung, nicht nur den ersten Start. Trennen Sie die serverseitige Sitzung oder warten Sie ihren Timeout ab; der Client fragt erneut, und dort zeigt eine Datei, die beim Start korrekt aussah, ob sie für das Konto, das sich neu verbindet, noch lesbar ist.

Ähnliche Artikel