Wake on Lan desde el USG

Es un hecho, el bastión es una de las computadoras que más uso y es la puerta de acceso a mi homelab. Pasa encendida 24/7 y por ello es vulnerable a cortes eléctricos que superen la duración de la batería a la cual se encuentra conectada.

Por esta razón se me ocurrió poner un script bash simple en el USG que por medio de bash verifica que el bastión este en línea y si no lo está intenta enviar un paquete mágico wake on lan para iniciarlo y notifica a través de slack el estado. Para crear un bot de Slack, solo hay que seguir la guía oficial.

Haciendo pruebas he observado que el tiempo que le toma al sistema levantar vía wake on lan hasta ser funcional es de entre 50 a 80 segundos. Por lo que me parece razonable que cada 3 minutos la tarea cron ejecute la comprobación del estado del bastión.

La idea general es la siguiente, instalar etherwake en el USG, poner el script en el USG en /config/scripts/, modificar el config.gateway.json del Cloud Key para indicarle la tarea cron y la periodicidad y reaprovisionar el USG para que quede activa. Así que manos a la línea de comando y veamos de que va esto.

Verificando el soporte Wake On Lan

Definitivamente este es el primer paso, porque de nada serviría todo esto si el bastión del homelab no tiene soporte para wake on lan. Así que conectado vía SSH al bastión, se puede hacer la verificación. Primero necesitamos saber como se llama la interfaz.

1
2
$ ip link show | grep default
default via 192.168.1.1 dev eno1 onlink

Este comando nos mostrará la conexión por defecto activa y en ella el nombre de interfaz luego de dev. En mi caso sería eno1. A continuación verificamos esta interfaz con ethtool.

1
2
3
$ sudo ethtool eno1 | grep Wake-on
    Supports Wake-on: pumbg
    Wake-on: g

Esta respuesta en la línea Supports Wake-on cada letra tiene un significado, según esta lista:

  • d (disabled): soporte wake on lan desactivado en la NIC.
  • p (PHY activity): actividad física en la capa 7 del modelo OSI en la NIC.
  • u (unicast activity): actividad unicast en la NIC.
  • m (multicast activity): actividad multicast en la NIC.
  • b (broadcast activity): soporte broadcast en la NIC.
  • a (ARP activity): soporte ARP en la NIC.
  • g (magic packet activity): soporte de paquetes mágicos en la NIC.

Si en la línea Wake on, esta presente la letra g estamos listos para continuar. Sino podemos intentar buscar en el BIOS del bastión si existe soporte wake on lan y activarlo. O bien si obtuvimos la letra d indicando que esta desactivado, podemos intentar habilitarlo en una sola línea.

1
$ sudo ethtool -s eno1 wol g

De este modo estaría habilitada la NIC para recibir paquetes mágicos. Sin embargo, podría ocurrir el caso que esto no sería persistente luego de un reinicio, por lo que habría que hacer algunas otras cosas para hacerlo persistente en el tiempo. En la wiki de Arch Linux, hay un buen tutorial para hacerlo.

Instalando etherwake

Nuestro script va a depender de dos programas específicos: curl y etherwake. curl ya viene preinstalado en el USG. Así que tendremos que instalar etherwake.

Lo primero será conectarnos al USG vía SSH

1
$ ssh usuario@ip-del-usg

Una vez dentro, nos volvemos usuarios root

1
$ sudo su

Podemos comprobar que se trata de Debian Wheezy, así

1
2
# cat /etc/os-release | grep PRETTY_NAME
PRETTY_NAME="Debian GNU/Linux 7 (wheezy)"

Por defecto, el USG no posee ningún repositorio, por lo que el archivo /etc/apt/sources.list está vacío y si intentamos hacer apt-get update tendremos un error. Añadimos los repositorios:

1
2
# echo 'deb http://archive.debian.org/debian/ wheezy main contrib non-free' > /etc/apt/sources.list;
# echo echo 'deb http://archive.debian.org/debian-security/ wheezy/updates main' > /etc/apt/sources.list.d/security.list;

Ahora hacemos el update de los repositorios:

1
# apt-get update -o Acquire::Check-Valid-Until=false

Al hacer el update obtendremos algunos errores, sobre todo falta de índices y el procesamiento ocurrirá un tanto lento a lo que uno suele estar acostumbrado.

Una acotación sobre esto, es que Debian 7 definitivamente no tiene ningún soporte oficial (fue LTS hasta 2018) y el USG utiliza un procesador MIPS, por lo que usa específicamente los repositorios de esa arquitectura. Por otro lado, es mala idea hacer un upgrade del sistema, pues no podemos predecir que ocurrirá con el USG, así que mejor solo dejar las cosas hasta donde están.

Ahora instalaremos el comando etherwake.

1
# apt-get install etherwake

Después que termine de ejecutarse, podremos usar el comando etherwake desde el USG y por supuesto integrarlo en el script que usaremos para automatizar el proceso. De paso, ya que tenemos acceso a los repositorios, también podemos instalar nano, el editor de texto de los psicópatas, en lugar de usar vi/vim.

El original por acá

El script

Todavía conectados al USG vía SSH y como usuarios root lo siguiente será crear script. Vamos a la carpeta /config/scripts. Para hacer las cosas simples, usaremos el editor de texto nano.

1
# nano /config/scripts/wakeifoffline.sh

Y ahora añadimos el script propiamente dicho…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env bash

REMOTEHOSTIP=192.168.1.99
REMOTEHOSTNAME=hobbiton
REMOTEHOSTMAC=6A:41:35:D5:94:26
SLACKURL=https://hooks.slack.com/services/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

if (echo > /dev/tcp/${REMOTEHOSTIP}/22) >/dev/null 2>&1
then
        :
else
        curl -X POST -H 'Content-type: application/json' --data '{"text":"['${REMOTEHOSTNAME}'] offline. Enviando paquete mágico..."}' ${SLACKURL}
        etherwake -i eth1 ${REMOTEHOSTMAC}
        sleep 90
        if (echo > /dev/tcp/${REMOTEHOSTIP}/22) >/dev/null 2>&1
        then
                curl -X POST -H 'Content-type: application/json' --data '{"text":"['${REMOTEHOSTNAME}'] online nuevamente..."}' ${SLACKURL}
        else
                curl -X POST -H 'Content-type: application/json' --data '{"text":"['${REMOTEHOSTNAME}'] continua offline..."}' ${SLACKURL}
        fi
fi

En este script veremos varias cosas. Las variables las reemplazaremos con nuestros datos, REMOTEHOSTIP hace referencia a la ip fija del bastión, REMOTEHOSTNAME técnicamente no es necesario pero me gusta que me notifique vía slack, así que por eso lo uso. REMOTEHOSTMAC sería la mac address de la interfaz que usaremos. SLACKURL sería la URL de Slack donde haremos la notificación por medio de la API.

Esta línea /dev/tcp/${REMOTEHOSTIP}/22 es donde ocurre la magia. Si nuestro bastión está en línea, habrá respuesta, si no está en línea, no habrá respuesta y enviará un paquete mágico y esperara 90 segundos para realizar una segunda comprobación. Puede que sea un poco cutre, porque podría darse el caso que muera el servidor SSH y el script comience a enviar paquetes mágicos una y otra vez.

Ahora toca darle permisos de ejecución al script que acabamos de crear.

1
# chmod +x /config/scripts/wakeifoffline.sh

Antes de crear la tarea cron sería bueno verificar que funciona. Solo tendremos que apagar el bastión y ejecutar manualmente nuestro script.

1
# sh /config/scripts/wakeifoffline.sh

Si funciona correctamente, verás los mensajes en Slack a los pocos segundos de iniciado el script y al poco tiempo el bastión estará encendido. Ahora bien, esto será un engorro mandando mensajes cada 3 minutos al canal de Slack, así que después solo dejaremos que notifique si esta offline y luego enciende vía wake on lan, solo hay que comentar la línea entre then y else.

Tarea cron en el config.gateway.json

Ya hemos verificado que funciona el script. Es hora de mandarlo a una tarea cron por medio del Unifi Cloud Key, lo primero, conectarse por SSH al UCCK y vamos al archivo config.gateway.json.

Solo necesitamos añadir la línea siguiente al bloque task-scheduler indicándole que la tarea se ejecutará cada 3 minutos.

1
"wakeifoffline":{"executable":{"path":"sh /config/scripts/wakeifoffline.sh"},"crontab-spec":"*/3 * * * *"}

Nos quedará el script así en caso que tengamos otras tareas cron activas

1
2
3
4
5
6
7
8
9
        "system":{
                "task-scheduler":{
                        "task":{
                                "hostblacklist":{"executable":{"path":"/config/scripts/getBlacklistHosts.sh"},"crontab-spec":"30 3 * * *"}
                                "dynamicdomainupdater":{"executable":{"path":"sh /config/scripts/dynamic-domain-updater/update-dns.sh"},"crontab-spec":"* * * * *"}
                                "wakeifoffline":{"executable":{"path":"sh /config/scripts/wakeifoffline.sh"},"crontab-spec":"*/3 * * * *"}
                        }
                }
        },

O bien, si es la primera vez que añadimos algo al config.gateway.json, nos quedará así:

1
2
3
4
5
6
7
8
9
{
        "system":{
                "task-scheduler":{
                        "task":{
                                "wakeifoffline":{"executable":{"path":"sh /config/scripts/wakeifoffline.sh"},"crontab-spec":"*/3 * * * *"}
                        }
                }
        },
}

Una vez añadido, activamos el task-scheduler:

1
# set system task-scheduler task wakeifoffline executable path /config/scripts/wakeifoffline.sh

Ahora podemos reaprovisionar el USG y disfrutar de nuestro script que mantiene en línea el bastión que a la vez mantiene una serie de servicios para mí red del homelab tratando de preservar el mínimo tiempo de caída una vez se restablezca el fluido eléctrico.

Reaprovisionar el USG

Tarea cron en el crontab

Si hacemos correctamente el método anterior, encontraremos que el config.gateway.json añade la tarea al crontab.

Sin embargo, podemos hacer la edición directa manual sobre el archivo crontab, solo hay que ejecutar crontab -e y añadimos la siguiente línea.

1
*/3 * * * * /config/scripts/wakeifoffline.sh

Al guardar, inmediatamente se comienza a ejecutar la tarea. Pero tenemos que tener en cuenta que este último método puede dejar de funcionar en caso de una actualización del firmware del USG.


Moisés Serrano Samudio Médico de atención primaria, fotógrafo aficionado, apasionado de las tecnologías relacionadas con el EdTech y el eHealth y diseñador/desarrollador de sitios web de salud. Médico, apasionado del EdTech/eHealth y diseñador/desarrollador de sitios web de salud.
Moisés Serrano Samudio

@linkmoises

Médico de atención primaria, fotógrafo aficionado, apasionado de las tecnologías relacionadas con el EdTech y el eHealth.

Entradas relacionadas

  1. Aún no hay comentarios...

Deja una respuesta

Su email no será publicado. Required fields are marked *