Detectar una VPN activa dentro de una app de Mac con sandbox
No hay una API pública para esta pregunta, y el truco del nombre de interfaz es solo una heurística. Seis comprobaciones para decidir de qué debe depender realmente tu app.
Necesitas que una pantalla se comporte de forma distinta cuando el túnel está activo, y no hay ninguna llamada que te lo diga. Esa es la restricción de entrega: en macOS ningún framework público informa si una VPN arbitraria está conectada, y en iOS el estado pertenece a la app que instaló la configuración. Las comprobaciones siguientes van de ese límite a algo que puedes entregar, y cualquiera de ellas puede terminar la búsqueda antes de tiempo.
Comprobación 1: ¿la pregunta tiene respuesta siquiera?
El sandbox resuelve la pregunta antes que cualquier API. Un proceso de terceros no puede enumerar la extensión de otro fabricante ni puede leer la configuración de esa extensión, así que no hay ningún objeto compatible al que preguntar.
Todos los rodeos chocan con el mismo muro. Los listados de interfaces, scutil --nc list y las lecturas directas de la configuración del sistema o bien requieren derechos que el sandbox retiene o bien dependen de un comportamiento que Apple nunca prometió. Si el plan necesita una de esas cosas, el problema es el plan. Una función construida sobre un detalle no documentado es un ticket de soporte con fecha de entrega.
Comprobación 2: ¿tu app gestiona el túnel ella misma?
Si lo hace, tienes una respuesta compatible para tu propia configuración. Carga las preferencias, gestiona el error y después lee el estado y suscríbete a los cambios en lugar de sondear:
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
}Sé preciso sobre lo que devuelve esto: el estado del túnel que instalaste. Un usuario que ejecute otro cliente aparecerá como desconectado, y eso no es un error en tu código. Decide cuál de esas dos preguntas plantea realmente tu función antes de construir sobre la respuesta.
Comprobación 3: ¿qué te diría realmente una heurística?
El enfoque ampliamente copiado pide al diccionario de proxy del sistema sus entradas con ámbito y lee las claves, que resultan ser nombres de interfaz:
let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as NSDictionary?
let scoped = settings?["__SCOPED__"] as? [String: Any]
let names = scoped?.keys ?? []Una clave que empiece por tap, tun, ppp, ipsec o utun implica que hay presente una interfaz con ámbito de túnel. Comparar el nombre completo falla de inmediato, porque iOS 12 llegó con utun1 y utun2 y algunos perfiles aparecen como ipsec4, ppp0 o utun0. Compara prefijos en su lugar.
Aun así, un nombre no identifica a ningún propietario. Las funciones del sistema y las extensiones de otros fabricantes también crean interfaces utun, y una puede existir sin llevar nada de tu tráfico. Léelo como una pista de que hay algo con forma de túnel cerca, nada más fuerte. Un falso positivo aquí es barato si solo condicionas una función, y caro si condicionas la pantalla de inicio de sesión.
Comprobación 4: ¿puedes probar el destino en su lugar?
Si la app solo necesita saber si un servicio interno es alcanzable, prueba ese servicio. Una sonda corta con tiempo de espera responde a la pregunta de la que realmente depende la función, y sigue funcionando cuando el usuario cambia de cliente o el túnel cambia de forma.
La alcanzabilidad es una observación sobre tu ruta. Un nombre de interfaz es una inferencia sobre algo adyacente a tu ruta, y por eso la heurística produce discusiones en los informes de errores y la sonda produce pruebas. Si la sonda falla mientras el usuario insiste en que la VPN está activa, ahora tienes una ruta concreta que depurar: resolución de nombres, ruta o permiso del servicio.
Comprobación 5: ¿puede la plataforma imponer lo que necesitas?
Cuando el requisito es la garantía de que nada se escapa del túnel, el perfil es su sitio, no el detector. Activar includeAllNetworks en una configuración de túnel completo en iOS 14 o posterior cambia el comportamiento de la plataforma en lugar de pedir a tu app que lo infiera. El indicador está documentado en la referencia de NEVPNProtocol.
Esa es otra clase de respuesta: imposición en lugar de observación. Además solo se aplica a perfiles que controlas, así que no ayuda a una app de consumo que vigila el túnel de otra persona.
Comprobación 6: haz que el fallo sea seguro y observable
Apaga solo la capacidad que necesita el túnel y deja el resto de la app utilizable. Un detector que esconde toda la interfaz cada vez que no está seguro fabrica una caída para cualquiera cuyo túnel use un nombre que nunca anticipaste. Vuelve más bien al comportamiento conservador: las funciones locales siguen funcionando y solo se desactiva la llamada remota que de verdad necesita el túnel. Esa elección es una decisión de producto, y merece escribirse junto a la heurística.
Registra lo que observaste: nombres de interfaz, versión de la plataforma y el resultado de una sonda. Eso convierte el siguiente informe de una discusión en una comprobación. Después prefiere una declaración compatible cuando la tengas, porque una configuración gestionada puede llevar la intención de forma explícita y eliminar la mayor parte de la razón para inspeccionar nombres.
Si las comprobaciones 1 a 5 te han dejado sin una respuesta compatible, entrega la sonda y etiqueta el resultado con honestidad. Una señal documentada de mejor esfuerzo es una mejor decisión de producto que una API privada que se rompe en la próxima versión.