Interruptor de seguridad pf de macOS para OpenVPN: reglas que cierran
Un conjunto de reglas pf bloquea el tráfico directo mientras el túnel está caído. Mantén la ruta del cliente hacia su servidor y luego demuestra el bloqueo con el túnel apagado.
En macOS, un interruptor de seguridad vale tanto como la política del filtro de paquetes que tiene detrás, y pf es la pieza que aplica esa política. La política debe hacer dos cosas opuestas a la vez: dejar que el cliente llegue a su servidor para que pueda existir un túnel, y detener todo lo demás para que no salga directamente. La mayoría de los conjuntos de reglas publicados resuelven lo segundo y olvidan lo primero, así que la máquina pierde conectividad en lugar de ganar protección. La versión que funciona nombra el punto final, el resolutor y el túnel, y nada más.
Qué puede decidir un filtro de paquetes y qué no
pf mira interfaces, direcciones, números de protocolo y puertos, y ahí se detiene. Nunca sabe qué proceso abrió un socket, así que un permiso escrito para un programa se convierte en permiso para cada programa que pueda alcanzar el mismo puerto. Así es como una excepción UDP generosa devuelve un interruptor de seguridad a una red corriente. El bloqueo consciente de la aplicación pertenece al cliente o a una extensión del sistema, no a este archivo.
El filtro tampoco crea rutas. Cuando el cliente cae, la ruta por defecto puede seguir apuntando a un túnel muerto, y pf lo convierte en paquetes bloqueados en lugar de una vuelta silenciosa al Wi-Fi. El bloqueo es el resultado buscado, no un fallo que haya que rodear.
El orden decide el resultado más a menudo que el texto de la regla. Sin quick, pf sigue evaluando tras una coincidencia y gana la última regla que coincide, así que un paso amplio cerca del final anula las excepciones cuidadosas de arriba. Marcar los pasos con quick hace que la primera coincidencia sea definitiva, que es lo que suele querer decir un autor de políticas.
Lee primero los datos de la interfaz y del punto final
Las interfaces inalámbricas no se numeran igual en cada Mac, y el dispositivo de túnel recibe un número que puede cambiar entre arranques. Una regla que nombra un dispositivo inexistente falla al analizarse, lo cual es ruidoso y fácil de detectar, mientras que una regla que nombra el dispositivo existente equivocado se analiza limpiamente y permite tráfico que querías detener.
Recoge los datos mientras el cliente está conectado, porque el dispositivo de túnel solo existe entonces. Necesitas la interfaz que tiene la ruta por defecto, el dispositivo que creó el cliente, y la dirección y el puerto del servidor al que marca. Ese último valor es el que más conjuntos de reglas omiten, y omitirlo es por lo que un conjunto copiado bloquea su propio túnel.
route -n get default
ifconfig -l
scutil --nc list
El primer comando nombra la interfaz que gana ahora, el segundo enumera cada dispositivo que conoce el núcleo, y el tercero muestra las configuraciones VPN que el sistema puede iniciar. Apunta esas respuestas y luego lee la dirección y el puerto del servidor desde la propia configuración del cliente.
Escribe el conjunto de reglas alrededor de la ruta del túnel
El archivo de abajo deniega por defecto y vuelve a admitir cinco cosas: loopback, el dispositivo de túnel, la ruta del cliente hacia su servidor, el resolutor usado antes de que exista el túnel, y los dos intercambios que mantienen utilizable un enlace inalámbrico. Los nombres de interfaz y la dirección del servidor vienen de tu máquina, así que sustituye los valores que confirmaste. El número de túnel tampoco coincidirá con el tuyo.
set skip on lo0
block all
pass out quick on utun0
# maintain this line when the server address changes
pass out quick on en1 inet proto udp to 203.0.113.10 port 1194
# only the resolver the client uses before the tunnel exists
pass out quick on en1 inet proto udp to 192.0.2.53 port 53
pass out quick on en1 inet proto udp from any port 68 to any port 67
pass out quick on en1 inet proto udp to 224.0.0.251 port 5353
La línea DHCP existe porque un Mac pide una dirección antes de tenerla, así que la petición sale del puerto 68 hacia el puerto 67 sin una dirección de origen utilizable. La línea de multicast permite al sistema juzgar si vale la pena usar el enlace inalámbrico, y quitarla produce una máquina que informa de que no hay conexión en una red que funciona. La línea del punto final es lo que permite a OpenVPN llegar a su servidor; sin ella, block all descarta el saludo exterior y el túnel nunca sube. La línea del resolutor es más estrecha que un permiso DNS general, porque nombra una dirección y un puerto en lugar de cada consulta que hace la máquina.
Esas excepciones del punto final y del resolutor son las dos que deben existir; todo lo demás sigue bloqueado. Si el cliente marca una dirección literal, quita la línea del resolutor y mantén la del punto final. Si la dirección del servidor cambia, actualiza esa línea antes de la siguiente conexión, o llena una tabla de pf desde el propio gancho del cliente y haz coincidir la tabla en su lugar.
Baja el túnel y luego reinicia
Analiza antes de cargar, porque un error de sintaxis es barato en una prueba en seco y caro en una sesión remota. Luego carga el archivo, activa el filtro si estaba apagado, y relee qué está realmente en vigor. La relectura no es ceremonia: otro anclaje puede añadir reglas que no escribiste, así que compara el conjunto impreso con tu archivo.
sudo pfctl -n -v -f /etc/pf.conf
sudo pfctl -f /etc/pf.conf
sudo pfctl -e
sudo pfctl -s rules
sudo pfctl -s Anchors
sudo pfctl -a <anchor> -s rules
sudo pfctl -s info
Con las reglas cargadas, baja el túnel e intenta cargar una página. El tráfico corriente debería detenerse, y debería seguir detenido después de que la máquina duerma y despierte, porque es en esa transición donde vuelve el estado obsoleto. Reconecta y confirma que el tráfico se reanuda sin tocar el conjunto de reglas. Si un navegador sigue funcionando con el túnel bajado, alguna regla es más amplia de lo que pensabas, y la relectura suele nombrarla.
El reinicio es la parte que la gente se salta. macOS lee /etc/pf.conf al arrancar, pero leer el archivo no es lo mismo que activar el filtro, así que un conjunto de reglas que funciona ahora puede quedar inactivo tras un reinicio. Ejecuta después sudo pfctl -s info y confirma que la línea de estado informa el filtro como activado.
Mantén honestos la salida y la ruta de reinicio
Un conjunto de reglas que bloquea todo también puede dejarte fuera de una máquina que administras en remoto. Edita desde una sesión local cuando puedas, ten el archivo anterior junto al nuevo, y conoce la reversión antes de necesitarla. Los comandos de abajo desactivan el filtro y recargan la copia que guardaste, y es la secuencia para ejecutar desde una consola, no por el túnel que acabas de cortar. Una reversión de cinco segundos gana a una regla ingeniosa cada vez.
sudo pfctl -d
sudo pfctl -f /etc/pf.conf.previous
sudo pfctl -s rules
La persistencia exige un demonio de arranque que ejecute pfctl -e y cargue tu archivo al inicio. Trátalo como cualquier otro trabajo privilegiado: rutas absolutas, un registro que puedas leer después, y una prueba en una máquina que puedas tocar físicamente. Un demonio que falla en silencio deja la máquina desprotegida y con aspecto normal.
Documenta los huecos, empezando por un segundo cliente VPN que crea su propio dispositivo de túnel. Tus reglas nombran una interfaz de túnel, así que la segunda queda bloqueada o la captura una regla amplia añadida más tarde. Las interfaces de tethering y punto de acceso solo aparecen a veces y merecen la misma nota. Ninguno de esos huecos es razón para saltarse la política; son las condiciones bajo las que aún se sostiene.