Aktives VPN in einer sandboxed Mac-App erkennen
Für diese Frage gibt es keine öffentliche API, und der Trick mit dem Interface-Namen ist nur eine Heuristik. Sechs Prüfungen, um zu entscheiden, worauf Ihre App wirklich angewiesen sein sollte.
Sie brauchen einen Bildschirm, der sich anders verhält, wenn der Tunnel aktiv ist, und es gibt keinen Aufruf, der Ihnen das sagt. Das ist die Einschränkung beim Ausliefern: Unter macOS meldet kein öffentliches Framework, ob ein beliebiges VPN verbunden ist, und unter iOS gehört der Status der App, die die Konfiguration installiert hat. Die folgenden Prüfungen führen von dieser Grenze zu etwas, das Sie ausliefern können, und jede von ihnen kann die Suche vorzeitig beenden.
Prüfung 1: Ist die Frage überhaupt beantwortbar?
Sandboxing entscheidet die Frage, bevor es irgendeine API tut. Ein Prozess eines Drittanbieters kann die Erweiterung eines anderen Anbieters nicht aufzählen und auch deren Konfiguration nicht lesen, also gibt es kein unterstütztes Objekt, das man fragen könnte.
Jeder Umweg stößt an dieselbe Wand. Interface-Listen, scutil --nc list und direkte Lesezugriffe auf die Systemkonfiguration erfordern entweder Berechtigungen, die die Sandbox vorenthält, oder hängen von Verhalten ab, das Apple nie versprochen hat. Wenn der Plan eines davon braucht, ist der Plan das Problem. Ein Feature, das auf einem undokumentierten Detail aufbaut, ist ein Support-Ticket mit Lieferdatum.
Prüfung 2: Verwaltet Ihre App den Tunnel selbst?
Wenn ja, haben Sie eine unterstützte Antwort für Ihre eigene Konfiguration. Laden Sie die Einstellungen, behandeln Sie den Fehler, lesen Sie dann den Status und abonnieren Sie Änderungen, statt zu pollen:
vpnManager.loadFromPreferences { error in
// handle a load failure before reading status
}
NotificationCenter.default.addObserver(
forName: .NEVPNStatusDidChange,
object: nil,
queue: nil
) { note in
let status = (note.object as? NEVPNConnection)?.status
}Seien Sie präzise dabei, was dies zurückgibt: den Zustand des Tunnels, den Sie installiert haben. Ein Nutzer, der einen anderen Client ausführt, erscheint als getrennt, und das ist kein Fehler in Ihrem Code. Entscheiden Sie, welche der beiden Fragen Ihr Feature wirklich stellt, bevor Sie auf der Antwort aufbauen.
Prüfung 3: Was würde Ihnen eine Heuristik tatsächlich sagen?
Der viel kopierte Ansatz fragt das System-Proxy-Wörterbuch nach seinen bereichsbezogenen Einträgen und liest die Schlüssel, die zufällig Interface-Namen sind:
let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as NSDictionary?
let scoped = settings?["__SCOPED__"] as? [String: Any]
let names = scoped?.keys ?? []Ein Schlüssel, der mit tap, tun, ppp, ipsec oder utun beginnt, deutet darauf hin, dass eine tunnelbezogene Schnittstelle vorhanden ist. Den ganzen Namen abzugleichen scheitert sofort, denn iOS 12 brachte utun1 und utun2 mit, und manche Profile erscheinen als ipsec4, ppp0 oder utun0. Vergleichen Sie stattdessen Präfixe.
Dennoch benennt ein Name keinen Eigentümer. Systemfunktionen und Erweiterungen anderer Anbieter erzeugen ebenfalls utun-Schnittstellen, und eine kann existieren, ohne dass sie einen Teil Ihres Verkehrs trägt. Lesen Sie es als Hinweis, dass etwas Tunnelartiges in der Nähe ist, nicht mehr. Ein Fehlalarm ist hier billig, wenn Sie nur ein Feature absichern, und teuer, wenn Sie den Anmeldebildschirm absichern.
Prüfung 4: Können Sie stattdessen das Ziel testen?
Wenn die App nur wissen muss, ob ein interner Dienst erreichbar ist, testen Sie diesen Dienst. Eine kurze Probe mit Timeout beantwortet die Frage, von der das Feature tatsächlich abhängt, und sie funktioniert weiter, wenn der Nutzer den Client wechselt oder der Tunnel seine Form ändert.
Erreichbarkeit ist eine Beobachtung über Ihren Pfad. Ein Interface-Name ist eine Schlussfolgerung über etwas, das an Ihren Pfad angrenzt, weshalb die Heuristik Diskussionen in Fehlerberichten erzeugt und die Probe Beweise. Wenn die Probe fehlschlägt, während der Nutzer darauf besteht, das VPN sei an, haben Sie jetzt einen konkreten Pfad zum Debuggen: Namensauflösung, Route oder Dienstberechtigung.
Prüfung 5: Kann die Plattform durchsetzen, was Sie brauchen?
Wenn die Anforderung eine Garantie ist, dass nichts aus dem Tunnel entweicht, gehört das Profil dorthin, nicht der Detektor. includeAllNetworks in einer Volltunnel-Konfiguration unter iOS 14 oder neuer einzuschalten, ändert das Plattformverhalten, statt Ihre App zu bitten, es abzuleiten. Das Flag ist dokumentiert in der NEVPNProtocol-Referenz.
Das ist eine andere Art von Antwort: Durchsetzung statt Beobachtung. Sie gilt außerdem nur für Profile, die Sie kontrollieren, hilft also einer Verbraucher-App nicht, die den Tunnel eines anderen beobachtet.
Prüfung 6: Machen Sie den Fehlerfall sicher und beobachtbar
Schalten Sie nur die Fähigkeit ab, die den Tunnel braucht, und lassen Sie den Rest der App nutzbar. Ein Detektor, der die ganze Oberfläche verbirgt, wann immer er unsicher ist, erzeugt einen Ausfall für jeden, dessen Tunnel einen Namen verwendet, den Sie nie vorhergesehen haben. Fallen Sie stattdessen auf das konservative Verhalten zurück: lokale Funktionen funktionieren weiter, und nur der Fernaufruf, der den Tunnel wirklich braucht, wird deaktiviert. Diese Wahl ist eine Produktentscheidung, und sie verdient es, neben der Heuristik aufgeschrieben zu werden.
Protokollieren Sie, was Sie beobachtet haben: Interface-Namen, Plattformversion und das Ergebnis einer Probe. Das macht den nächsten Bericht aus einer Diskussion zu einer Prüfung. Bevorzugen Sie dann eine unterstützte Deklaration, wo Sie eine haben, denn eine verwaltete Konfiguration kann die Absicht ausdrücklich tragen und den meisten Grund beseitigen, Namen überhaupt zu prüfen.
Wenn Prüfungen 1 bis 5 Sie ohne unterstützte Antwort gelassen haben, liefern Sie die Probe aus und benennen Sie das Ergebnis ehrlich. Ein dokumentiertes Best-Effort-Signal ist eine bessere Produktentscheidung als eine private API, die beim nächsten Release bricht.