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.
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.
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
.
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
.
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.
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.
@linkmoises
Aún no hay comentarios...