Introducción #
En muchos entornos de trabajo es común disponer de varias redes y de conexión remota mediante VPN. En ciertos contextos, tenemos que decidir si queremos conectar por VPN o enrutar directamente por la red local.
Por ejemplo, si estamos en las instalaciones físicas de la empresa, conviene conectarse directamente a la red interna, optimizando el rendimiento. En cambio, si queremos conectarnos desde fuera de las instalaciones, debemos usar una VPN para mayor seguridad de las conexiones.
El problema, resulta tedioso hacer estos cambios a mano y no todos los usuarios van a saber la topologia exacta de la red, ni enrutar con órdenes como
ip route [IP] via [GATEWAY] dev [INTERFACE]
Automatizar este proceso no solo ahorra tiempo, sino que evita errores de configuración y garantiza que la seguridad y el rendimiento se gestionen correctamente según la ubicación.
NetworkManager y dispatcher.d #
Podríamos pensar que con triggers de systemd podríamos configurar estos eventos, y de hecho es posible, pero existe un servicio presente en la mayoría de distribuciones Linux modernas que facilita enormemente la administración de redes: NetworkManager.
NetworkManager se ocupa de levantar y bajar interfaces, recordar contraseñas WiFi, gestionar perfiles de red y facilitar la integración de servicios de red. También permite ejecutar acciones personalizadas cuando ocurre un evento de red, como conectarse a una Wi-Fi o desconectarse de ella. Estos hooks se implementan mediante scripts en el directorio:
/etc/NetworkManager/dispatcher.d/
Cada vez que una interfaz cambia de estado (por ejemplo, up o down), NetworkManager ejecuta los scripts allí presentes y les pasa dos parámetros de entrada: el nombre de la interfaz y su estado.
INTERFACE="$1"
STATUS="$2"
De este modo, colocando un fichero en dispatcher.d se ejecutará automáticamente cuando cambie el estado de la red. Esto permite añadir reglas como:
-
Montar automáticamente un recurso de red al conectarse a la Wi-Fi de la empresa o de casa.
-
Cambiar dinámicamente rutas y gateways según el SSID al que estemos conectados.
-
Activar o desactivar la VPN de manera automática según la red.
Script de ejemplo #
En nuestro ejemplo, el gateway 192.168.1.50 es un router que conecta nuestra red local (192.168.1.0/24) con otra subred interna de la empresa (172.16.1.0/24).
Cuando estamos físicamente conectados a la oficina o a nuestra Wi-Fi de confianza, queremos que el tráfico destinado a esa subred interna no pase por la VPN, sino directamente a través de este gateway.
En cambio, cuando no reconoce la red wifi, activa el VPN y enruta el tráfico hacia 172.16.1.0/24mediante el mismo.
ADVERTENCIA: Este script es un ejemplo de cómo usar dispatcher.d para enrutar tráfico a través de una VPN en un caso específico. Está pensado solo como referencia para usuarios avanzados. No se garantiza que funcione en tu red y no debe ejecutarse tal cual.
#!/bin/bash
# Script for use with NetworkManager's dispatcher.d
#
# When connected to a trusted local Wi-Fi (defined in LOCAL_SSIDS), this script
# disables the Tailscale VPN and adds a route to the target network via the local gateway.
#
# When connected to any other Wi-Fi, it enables the Tailscale VPN and removes
# the local route to ensure traffic goes through the VPN.
#
# Deployment:
# Place this script in /etc/NetworkManager/dispatcher.d/
# Ensure it is owned by root and has executable permissions.
INTERFACE="$1" # Passed by NetworkManager (interface name)
STATUS="$2" # Passed by NetworkManager (connection status)
## CONFIGURATION ##
TARGET_IP="172.16.1.0/24" # IP with or without CIDR
LOCAL_GATEWAY="192.168.1.50"
LOCAL_INTERFACE="wlp2s0" # Wireless interface name
# Trusted Wifi networks (Tailscale will be disabled for these)
LOCAL_SSIDS=("Work_SSID_1" "Work_SSID_2" "Work_SSID_3" "Work_SSID_4")
## FUNCTIONS ##
check_root() {
# Verify script is running as root
if [ $(id -u) = 0 ]; then
return 0 # OK
else
echo "This script needs to be executed as root" >&2
logger -t net-dispatcher "This script needs to be executed as root"
exit 1 # ERROR
fi
}
check_package() {
# Check if required package is installed (Debian/Ubuntu)
local pkg="$1"
if dpkg -s "$pkg" &> /dev/null; then
return 0
else
echo "Package $1 not found, aborting..." >&2
exit 1
fi
}
send_notification() {
# Detectar el usuario gráfico en seat0
local user=$(loginctl list-sessions | awk '$4 == "seat0" {print $3}')
[[ -z "$user" ]] && { logger -t net-dispatcher "No se detectó usuario gráfico"; return 1; }
local uid=$(id -u "$user")
local bus="unix:path=/run/user/$uid/bus"
# Ejecutar notify-send como usuario gráfico con el bus correcto
sudo -H -u "$user" env DBUS_SESSION_BUS_ADDRESS="$bus" \
notify-send "$@"
}
## MAIN SCRIPT ##
# If the interface is down or not $LOCAL_INTERFACE
if [[ "$STATUS" != "up" || "$INTERFACE" != "$LOCAL_INTERFACE" ]]; then
exit 0
fi
# Verify requirements
check_root
check_package wireless-tools # iwgetid
check_package libnotify-bin # notify-send
check_package tailscale
check_package util-linux # logger
check_package iproute2 # ip route
# Detect current WiFi network
CURRENT_SSID=$(iwgetid -r)
logger -t net-dispatcher "Interface $LOCAL_INTERFACE is $STATUS"
# Check if connected to trusted network
ssid_found=false
for ssid in "${LOCAL_SSIDS[@]}"; do
if [[ "$ssid" == "$CURRENT_SSID" ]]; then
ssid_found=true
logger -t net-dispatcher "Local SSID found: $CURRENT_SSID"
break
fi
done
# Remove the route for safety before adding it
ip route del "$TARGET_IP" 2>/dev/null || true
if [[ "$ssid_found" == true ]]; then
# Trusted network - use local routing
send_notification --app-name="NetworkManager" -t 5000 -i network-wireless "Welcome Home" "Routing over LAN"
logger -t net-dispatcher "Routing via LAN"
tailscale down
ip route add "$TARGET_IP" via "$LOCAL_GATEWAY" dev "$LOCAL_INTERFACE"
else
send_notification --app-name="NetworkManager" -t 5000 -i network-vpn "Untrusted network" "Routing over VPN"
logger -t net-dispatcher "Local SSID not found: $CURRENT_SSID"
logger -t net-dispatcher "Routing via Tailscale"
tailscale up --accept-routes
fi