"Después del juego es antes del juego"
Sepp Herberger

viernes, 23 de febrero de 2024

Cada alumno con su portátil

En mi centro muchos portátiles tienen uno o dos alumnos asignados, de tal manera que solo deberían ser usados por ellos. Esto tiene como consecuencia que no suelan sufrir desperfectos ya que al ser nominativos se incentiva su cuidado.

Pero este curso, no sé por que motivo, nos hemos encontrado en que los alumnos estaban usando los portátiles de forma libre, cogiendo el de sus compañeros al azar. Me han pedido que controlemos eso, evitando que nadie pueda usar un portátil que no sea el que tiene asignado. Está es la solución que he implementado.

Por un lado un script que se ejecuta al inicio de la sesión de usuario con:
# cat /etc/xdg/autostart/CheckUser.desktop                                                                
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Icon[es_ES]=
Exec=/usr/local/bin/check_user
Name[es_ES]=Check authorized user
Name=Check authorized user
El script sería:
# cat /usr/local/bin/check_user
#!/bin/bash

usuario=$USER
equipo=$HOSTNAME
fecha=$(date)
file=/var/cache/.asignaciones.txt

#Si no hay fichero de asignaciones, salimos
test -e "$file" || exit 0

#Buscamos usuarios permitidos
permitidos=$(grep "^${equipo};" $file| cut -d";" -f3)

#Si no hay usuarios asignados, permitimos que lo use cualquiera
test -z "$permitidos"  &&  exit 0
test "$permitidos" == "-"  && exit 0

#Buscamos si usuario actual está permitido    
autorizado=$(echo $permitidos | grep -w $usuario)
test -z "$autorizado" || exit 0

sleep 5

#Cerramos los navegadores para que el usuario vea el mensaje
killall -9 firefox
killall -9 google-chrome
#Si llegamos aqui estamos intentando hacer login un un usuario no autorizado en el portátil
zenity --error --text="Usuario no autorizado en este portátil. Avise al profesor." --timeout 10 --width=600 --height=100
xfce4-session-logout

exit 0
Lo que hace es obtener el nombre de usuario y máquina y busca en el fichero /var/cache/.asignaciones.txt si el usuario está permitido en dicha máquina. Si no lo está, le manda un aviso y cierra la sesión.

El fichero /var/cache/.asignaciones.txt lo distribuyo por puppet en todos los portátiles y tiene este formato:
...
porthp-o19;b0:5a:da:a2:2d:0c;agomeza02
porthp-o20;30:8d:99:1d:f6:30;irenarr01
porthp-o21;30:8d:99:1c:ce:f4;lblancoa06
porthp-o21;30:8d:99:1c:ce:f4;mgarcias02
porthp-o22;30:8d:99:1a:dd:09;acarrerag27
porthp-o23;30:bd:23:22:8d:01;-
...
En este ejemplo anterior el portátil porthp-o20 sólo sería usable por el alumno irenarr01, el porthp-o21 estaría permitido a lblancoa06 y a mgarcias02, mientras que porthp-o23 podría ser usado por cualquier persona.

Lo mejor de todo: la cara de algunos alumnos cuando han venido a decirme que el portátil no les autorizaba el acceso, autoinculpándose de coger un portátil ajeno X-DDD...¿se puede ser más pardillo?

Montaje de recurso SAMBA falla en el arranque de los clientes Linux

Volvemos tras este prolongado silencio. Tengo varios recursos compartidos en un servidor samba que montan muchos equipos del centro en el arranque desde /etc/fstab con una línea como esta:
//192.168.0.99/media /media/media cifs _netdev,auto,user=user,password=password,rw,iocharset=iso8859-1,dir_mode=0777,file_mode=0777,vers=2.0
Durante años esto ha funcionado, pero tras unos cambios de ubicación de diversos recursos me he encontrado con que en algunas máquinas ya no se montaba la carpeta de forma automática en el inicio. Si una vez arrancada la máquina hacía:
# mount /media/media
Si se montaba sin problema. En el syslog veía que se producía un error de montaje en el inicio que daba a entender que se estaba intentando montar antes de estuviera disponible la red. Eso es absurdo ya que el parámetro _netdev de la línea de montaje le dice al sistema "hasta que no haya red, no montes". Pero no funciona, algo estaba fallando.

La solución adoptada es crear un fichero ejecutable en /etc/network/if-up.d/mountsmb con:
#!/bin/sh
mount -a
Esto ejecuta el script una vez que la tarjeta de red recibe IP y hay red disponible. En el script la línea "mount -a" fuerza el montaje de todo lo que hay en fstab marcado con el parámetro "auto". Y con eso se arregla el problema, aunque sigamos sin saber que lo originó.

Como dice el proverbio chino: siempre hay más soluciones que problemas.

jueves, 23 de marzo de 2023

Script para chequear el árbol LDAP

Los servicios LDAP que tenemos en los centros se basan en OpenLDAP, que guarda información en una base de datos jerárquica. Las bases de datos jerárquicas son esas que estudias en la carrera antes de las bases relacionales y luego olvidas para siempre, porque tienen un uso mucho más limitado. Si quieres saber más de ellas pincha aquí.

Pese a sus ventajas, como la rapidez para algunas consultas, tienen ciertas desventajas. Una de ellas es que, en el caso de OpenLDAP, los datos quedan inconsistentes con cierta frecuencia, de tal manera que se va acumulando "información basura" dentro de las diversas ramas. Por ejemplo: ramas vacías, grupos con miembros no existentes, atributos duplicados...

Para un trabajo que estoy perpetrando, necesitaba tener el ldap de mi red limpito y consistente. Por ello he unificado diversos scripts que tenía en uno solo que recorre el árbol ldap y muestra todo lo que resulta sospechoso. Avisos:
  • OJO: este script no arregla nada, solo muestra las cosas rarunas que encuentra. El arreglo nos corresponde a nosotros a mano usando la herramienta web phpldapadmin.
  • IMPORTANTE: antes de tocar nada se aconseja hacer un backup de ldap por si la liamos parda, aquí un script de Esteban para hacer el backup y otro para restaurarlo.
El script de chequeo se puede encontrar en este enlace. Allí hay además una relación de las comprobaciones que se hacen.

Out!
Ya tenemos al Miura 1 en la rampa de lanzamiento, listo para su primer vuelo desde las instalaciones de El Arenosillo, en Huelva:
Esperemos que en las próximas semanas el inexistente programa espacial español dé un paso exitoso con el lanzamiento de esta maravilla.

viernes, 27 de enero de 2023

Restaurar imagen de clonezilla en disco mas pequeño que el original

A veces sucede que tenemos una imagen de clonezilla y nos encontramos que cuando queremos restaurarla sobre un disco vemos que no cabe, ya que el disco original desde donde hicimos la imagen tiene un tamaño mayor que el disco destino. Vamos a ver distintas soluciones para esto.

1. Hacer otra imagen mas pequeña.

Para ello restauramos la imagen en un tercer disco alternativo mas grande donde la imagen no tenga problemas de tamaño para entrar. Luego, con ayuda de la herramienta gparted (la podemos ejecutar desde cualquier disco live de Linux), reducimos todas las particiones de dicha imagen a un tamaño suficiente para que entre en el disco destino que nos daba problemas de tamaño y tras este paso podemos hacer una nueva imagen con clonezilla. Esta imagen reducida será la que restauremos. Se tarda tiempo, pero es un proceso sin complicaciones .

2. Intentar restaurar la imagen con las opciones de Experto de clonezilla.

Cuando restauramos una imagen con clonezilla, llega un punto del proceso en el que nos preguntan si queremos seguir en modo Basic o Expert. Normalmente elegimos el modo Basic, pero para esta vía deberemos coger el modo Expert. Para intentar encoger y meter la imagen en nuestro disco elegiremos en una de las pantallas posteriores la opcion "-icds" (no comprobar tamaño de disco) y en la siguiente pantalla la opción -k1 (construir las particiones de forma proporcional al tamaño del disco), tal como nos cuentan aquí.

Esto hará que la restauración de las particiones individuales mediante la herramienta partclone se ejecute con el parámetro -C (Don't check device size and free space). En resumen, lo que hacemos con esto es reducir todas las particiones originales proporcionalmente al tamaño del disco destino y meter la imagen sin comprobar nada a ver si hay suerte y cabe.

Si la imagen original tiene una sola partición puede que funcione. Mi experiencia es que rara vez me ha acabado con éxito, especialmente si hay varias particiones.

Hay al menos dos motivos para esto:

  • No siempre se pueden reducir todas las particiones proporcionalmente. La reducción se hace a costa del espacio libre que hay dentro de cada partición, pero puede que una de ellas esté demasiado llena y no deje espacio suficiente para reducirla en la medida en que queremos hacerlo. Por desgracia, no hay opción sencilla para reducir solo unas particiones y dejar las otras igual. La única vía con alguna posibilidad de éxito sería usar la opción k2 en lugar de k1 y crear las particiones a mano nosotros, lo cual es un proceso que habria que hacer con mucho cuidado.
  • Hay particiones ocultas o especiales (EFI, de sistema, reservadas, de la Agenda 2030, etc) que deben tener un tamaño y ubicación concreta en el disco y cualquier alteración de estos parámetros provoca que la clonación falle o que el sistema no arranque posteriormente. Esas aplicaciones deben restaurarse tal cual están en el disco original, sin quitar ni poner ni un byte.


3. Proceso manual partición a partición.

Esta vía es la que me ha llevado a documentar esta entrada del blog. Tengo una tarjeta SD de 32Gb con un sistema Rasbpian ya configurado que quiero clonar. Su estructura es:
# fdisk -l /dev/sdc
/dev/sdc1              8192   532479   524288   256M  c W95 FAT32 (LBA)
/dev/sdc2            532480 60579839 60047360  28,6G 83 Linux
Quiero restaurar la imagen en una tarjeta de 16Gb. Mis intentos de restaurarla usando el modo Experto de clonezilla (descrito en el paso anterior) han fracasado miserablemente.

Los ficheros que componen la imagen del disco son:
-rw-r--r-- 1 root root        449 ene 13 09:43 blkdev.list
-rw-r--r-- 1 root root        361 ene 13 09:44 blkid.list
-rw-r--r-- 1 root root       5878 ene 13 09:44 clonezilla-img
-rw-r--r-- 1 root root        159 ene 13 09:44 dev-fs.list
-rw-r--r-- 1 root root          4 ene 13 09:44 disk
-rw-r--r-- 1 root root      22062 ene 13 09:44 Info-dmi.txt
-rw-r--r-- 1 root root        187 ene 13 09:44 Info-img-id.txt
-rw-r--r-- 1 root root      27119 ene 13 09:44 Info-lshw.txt
-rw-r--r-- 1 root root       2986 ene 13 09:43 Info-lspci.txt
-rw-r--r-- 1 root root        161 ene 13 09:44 Info-packages.txt
-rw-r--r-- 1 root root         80 ene 13 09:44 Info-saved-by-cmd.txt
-rw-r--r-- 1 root root         10 ene 13 09:44 parts
-rw------- 1 root root   40735156 ene 13 09:44 sdd1.vfat-ptcl-img.gz.aa
-rw------- 1 root root 4096000000 ene 13 09:44 sdd2.ext4-ptcl-img.gz.aa
-rw------- 1 root root  953241799 ene 13 09:44 sdd2.ext4-ptcl-img.gz.ab
-rw-r--r-- 1 root root         36 ene 13 09:43 sdd-chs.sf
-rw-r--r-- 1 root root    4193792 ene 13 09:44 sdd-hidden-data-after-mbr
-rw-r--r-- 1 root root        512 ene 13 09:43 sdd-mbr
-rw-r--r-- 1 root root        327 ene 13 09:43 sdd-pt.parted
-rw-r--r-- 1 root root        303 ene 13 09:43 sdd-pt.parted.compact
-rw-r--r-- 1 root root        181 ene 13 09:43 sdd-pt.sf
En negrita he remarcado los ficheros que me importan: sdd-mbr (con la tabla de particiones y sector de arranque del disco), sdd1.vfat-* (con la imagen de la primera particion) y sdd2.ext4-* (con la imagen de la segunda partición).

Lo primero que haremos es restaurar la tabla de particiones y el MBR del disco original en el nuevo disco con:
# dd if=sdd-mbr of=/dev/sdc bs=512 count=1
Con esto creamos una tabla de particiones idéntica a la del disco original, lo cual no parece buena idea, ya que este disco es mucho mas pequeño. Lo que hago a continuación para arreglar esto es editar dicha tabla de particiones con:
# cfdisk /dev/sdc
Con cfdisk borramos a mano sdc2 (que es la partición grande que me obstaculiza la restauración de la imagen) y luego la creamos de nuevo ya ajustada al disco actual. La partición sdc1 es la partición de arranque, ocupa poco y tiene pinta de ser delicada, asi que para no sufrir represalias futuras del karma, mejor la dejamos como está. Tras editar la tabla de particiones como hemos descrito esta nos quedará:
/dev/sdc1              8192   532479   524288   256M  c W95 FAT32 (LBA)
/dev/sdc2            532480 30873599 30341120  14,5G 83 Linux
Ya creadas, podemos restaurar la partición scd1 usando clonezilla, como se ha hecho de toda la vida, y una vez restaurada podemos ponernos manos a la obra para meter la otra partición a martillazos en sdc2, que ha quedado mucho más pequeña. Arrancamos clonezilla, montamos la particion con las imágenes en /home/partimag y salimos a terminal para teclear allí:
# cd /home/partimag/nuestra-imagen-querida
# cat sdd2.ext4-ptcl-img.gz.a* | gunzip -d | partclone.restore -d -C -s - -o /dev/sdc2 
Lo que hemos hecho aquí es concatenar los ficheros que componen la imagen de la segunda partición (cat sdd2.ext4-ptcl-img.gz.a*), descomprimir el fichero resultante (gunzip -d) y restaurarlo sobre sdc2 usando partclone.restore con el parámetro -C (que omite comprobar si la imagen cabe o no en la partición). Esto parece muy aparatoso, pero es exactamente lo que hace clonezilla por debajo cuando restauramos una partición.

Tras acabar la restauración, vamos a ver si ha tenido éxito. Montamos la partición restaurada:
# mount /dev/sdc2 /mnt
mount: wrong fs type, bad option, bad superblock
...
Carajo, no se que ha liado pero no ha funcionado. No surrender, si lo anterior no nos funciona vamos a ver como hacerlo de otra manera:
# cat sdd2.ext4-ptcl-img.gz.a* | gunzip -d | partclone.restore -d -s - -o sdd2.ext4.img
En vez de descomprimir en una partición física llevamos todo al fichero sdd2.ext4.img. Ahora hacemos:
# resize2fs -M sdd2.ext4.img
# fsck.ext4 sdd2.ext4.img
Con los dos comandos anteriores encogemos el tamaño de la imagen para que ocupe lo mínimo posible ( -M: Shrink the filesystem to the minimum size) y hacemos un chequeo de disco para reajustar todo al nuevo tamaño. Esta partición encogida a mano si que debe entrar en el espacio que hemos quedado en /dev/sdc2.
# dd if=sdd2.ext4.img of=/dev/sdc2 bs=512 status=progress
Con el comando sobre estas líneas restauramos la imagen en la partición con un rústico "dd", la forma mas artesanal de clonado y restauración. Tras esto:
# mount /dev/sdc2 /mnt
# ls /mnt
# umount /mnt
Debería dejarnos montar y ver el contenido de la partición ya restaurada. Queda un último paso: expandir el sistema de ficheros restaurado para que ocupe toda la partición /dev/sdc2, ya que seguramente haya quedado bastante apretadito y es peligroso dejarlo asi. Vamos allá:
# e2fsck -f -y -v -C 0 /dev/sdc2
# resize2fs -p /dev/sdc2
Otra forma de hacer esto último es usando Gparted, con la opción Partición->Comprobar/Verificar. Que cada cual elija si quiere hacerlo con comandos o con ratón.

Después de esto ya tenemos lo que queríamos: una partición clonada con su tamaño natural y la otra reducida para entrar en el nuevo disco destino. Como el último método me ha funcionado no he seguido probando cosas más divertidas.

Taluec!


viernes, 20 de enero de 2023

Lanzar un test de impresión desde un script

De vez en cuando aparece alguien quejándose de que una impresora "no imprime nada" y te ves obligado a ir hasta un puesto que tenga configurada la impresora y hacer una prueba por ti mismo, imprimiendo una página de prueba desde CUPS o una aplicación de escritorio (para descubrir muchas veces que la impresora funciona OK y era realmente un problema del usuario). Para librarme de ese engorro tengo un script que permite imprimir un test desde una conexión ssh. El script en cuestión es:
# cat test-printer.sh

#!/bin/bash
ruta=$(dirname "$0")
if [ "$#" -ne 2 ]
then
    echo "Uso: $(basename $0) <impresora> <bn|color>"
    echo "Impresoras disponibles el sistema:"
    lpstat -p | awk '{ print $3}'
else
    impresora="$1"
    case "$2" in
        "color" )fichero="${ruta}/TestPrinterColor.pdf"  
                 ;;
        "bn" )fichero="${ruta}/TestPrinterBN.pdf"
                 ;;
        *) echo "Error en segundo parámetro"
           exit 1
           ;;
    esac
    lp -d "$impresora" "$fichero" || echo "Error: revise la cola de impresión"
fi
exit 0
El script es muy básico: recibe como primer parámetro un nombre de impresora y como segundo una cadena "bn" o "color", para lanzar una prueba con un documento en blanco y negro o un documento en color. Si lo ejecutamos sin parámetros nos listará los nombres de las impresoras configuradas en el PC donde ejecutamos el script, que son aquellas con las que podremos lanzar el test.

Lo único que necesitamos es crear 2 ficheros pdf a nuestro gusto para los tests en blanco/negro y color, dejándolos en el mismo directorio en que está el script. Estos 3 ficheros los podemos poner en una ruta de red accesible desde cualquier máquina o bien los copiaremos a los PC desde donde queramos hacer las pruebas. Luego, ante un problema bastará entrar por ssh y hacer:
# ./test-printer.sh IMPRESORA_DPTO_12 color
Out!

jueves, 1 de diciembre de 2022

Interruptor casero USB.

Vamos a ver como construir un interruptor casero que se conecta por USB y que al pulsarse permite lanzar un evento en el ordenador, ejecutando un script.

Lo primero es construir el interruptor: ¿que usar si no tenemos ni idea de electrónica?. Pues no hace falta nada especial ya que con un ratón de ordenador es suficiente. Todos tenemos ratones USB rotos o antiguos que podemos reciclar. La idea es desmontar el ratón, sacar la placa, arrancar la luz led y verificar que el botón central (el asociado al click de la rueda) funciona y manda señal por el cable USB. Da igual el estado del resto de mecanismos.

Cogemos el ratón, lo metemos dentro de una cajita del tamaño adecuado y lo pegamos usando la pistola térmica de silicona que tenemos todos para las manualidades. En mi caso tenía una cajita que venía perfecta:
Nótese como buscamos que el interruptor del botón quede justo en el centro (marcado con el círculo amarillo). Luego he puesto una tapa de plástico agujereada para permitir llegar al interruptor y forrada con goma eva. Por último hago un interruptor externo con un tapón y un poco de espuma de embalaje. La idea es que el interruptor externo presione al interruptor interno del ratón.
Una vez pegado y montado todo:
Creo que es resultón para lo que yo quiero, pero si queremos hacerlo mas formal siempre podemos usar la impresora 3D para generar una cajita y botón con aspecto mas profesional.

Ya tenemos el hardware, vamos al software. Pinchamos el cable en un puerto USB y el Linux lo detecta como un ratón, pero como esta casi todo inutilizado tan solo recibirá eventos del botón central. Lo primero es encontrar el generador de eventos asociado al nuevo ratón:
# ls -l /dev/input/by-id
total 0
lrwxrwxrwx 1 root root  9 oct 27 09:34 usb-0461_USB_Optical_Mouse-event-mouse -> ../event2
lrwxrwxrwx 1 root root  9 oct 27 09:34 usb-0461_USB_Optical_Mouse-mouse -> ../mouse0
lrwxrwxrwx 1 root root  9 oct 27 09:34 usb-CHICONY_HP_Basic_USB_Keyboard-event-kbd -> ../event3
lrwxrwxrwx 1 root root 10 nov 29 13:32 usb-Logitech_USB-PS_2_Optical_Mouse-event-mouse -> ../event14
lrwxrwxrwx 1 root root  9 nov 29 13:32 usb-Logitech_USB-PS_2_Optical_Mouse-mouse -> ../mouse1
En nuestro caso sería /dev/input/event14. El paso siguiente es verificar que se detectan los eventos de pulsación. Para ello usamos triggerhappy:
# apt-get install triggerhappy
# thd --dump /dev/input/event14
Y pulsamos varias veces el interruptor para ver si se detecta. En consola debería mostrarse:
EV_KEY	BTN_MIDDLE	1	/dev/input/event14
# BTN_MIDDLE	1	command
EV_KEY	BTN_MIDDLE	0	/dev/input/event14
# BTN_MIDDLE	0	command
EV_KEY	BTN_MIDDLE	1	/dev/input/event14
# BTN_MIDDLE	1	command
thd --dump /dev/input/event14
EV_KEY	BTN_MIDDLE	0	/dev/input/event14
# BTN_MIDDLE	0	command
Perfecto: el evento generado por la pulsación del botón es "BTN_MIDDLE 1". Conviene recalcar en este momento que la pulsación del botón central del ratón puede ser también interceptado por nuestro entorno de escritorio Linux y provocar algún efecto asociado (por ejemplo, pegar el texto que hay en el portapapeles). Para evitar efectos colaterales recomendamos desactivar ese ratón en la configuración del entorno de escritorio:

Una vez tenemos el evento localizado, creamos la configuración que asocia dicho evento al lanzamiento de un script:
# cat /etc/triggerhappy/triggers.d/redbutton.conf
BTN_MIDDLE    1       /root/scripts/redbutton.sh
Después de este cambio de configuración se debe reiniciar el servicio triggerhappy, pero antes debemos configurarlo para que se ejecute como root (normalmente lo hace como nobody) y escuche solo los eventos que vengan de /dev/input/event14.

Inocente de mi, probé a modificar tanto /etc/default/triggerhappy como /etc/init.d/triggerhappy sin éxito. Seguia ejecutándose con el usario nobody y escuchando en /dev/input/event*. Esto lo podemos verificar haciendo:
# ps aux | grep thd
nobody   12523  0.1  0.0  43844  2852 ?        Ss   21:32   0:00 /usr/sbin/thd --triggers /etc/triggerhappy/triggers.d/ --socket /run/thd.socket --user nobody --deviceglob /dev/input/event*
La causa de esto es que en Ubuntu 18 el demonio triggerhappy se lanza realmente desde /lib/systemd/system/triggerhappy.service. Los otros dos ficheros están de adorno. No entiendo esta manía de tener servicios que están init.d y en systemd a la vez, es como si hubiesen quedado el trabajo a medias y se hubiesen ido de cañas.

Editamos /lib/systemd/system/triggerhappy.service y lo quedamos:
[Unit]
Description=triggerhappy global hotkey daemon
After=local-fs.target

[Service]
Type=notify
ExecStart=/usr/sbin/thd --triggers /etc/triggerhappy/triggers.d/ --socket /run/thd.socket --user root --deviceglob /dev/input/event14

[Install]
WantedBy=multi-user.target
Luego reiniciamos el servicio:
# service triggerhappy restart
Y bueno ahora nos falta escribir el script. Aqui podemos poner cualquier cosa, yo simplemente voy a hacer que se oiga un audio (descargado de Internet o generado con pico2wave) en el escritorio:
# cat /root/scripts/redbutton.sh 
#!/bin/bash

#Subimos el volumen al máximo en el alsamixer
amixer set Master unmute
amixer set Master 100%

#Si hay un usuario logado, conectamos con su pulseaudio para subir el volumen al máximo
user=$(who | grep "(:0)" | head -1 | cut -f1 -d" "); 
if  [ -n "$user" ]
then
  su $user -c "DISPLAY=:0 pactl set-sink-volume 0 150%"
  su $user -c "DISPLAY=:0 pactl set-sink-mute 0 0"
  cp -f /root/scripts/no.wav /tmp/no.wav
  su $user -c  "DISPLAY=:0 aplay /tmp/no.wav"
else
  cp -f /root/scripts/no.wav /tmp/no.wav
  DISPLAY=:0 aplay /tmp/no.wav
fi

exit 0
Con esto ya tenemos todo funcionando. Bien, y ahora la pregunta: ¿Para qué carajos quiero esto?. Bueno, pues aparte de para enredar, para conectarlo a una Raspberry Pi o un OpenWRT sin teclado/ratón y poder lanzar eventos sobre ellos: apagar/encender la wifi, hacer una foto con la webcam, reiniciar un servicio o una aplicación, etc.

En mi caso tengo una Rasbperry Pi mostrando una página web en modo kiosko con información en tiempo real. A veces el navegador se bloquea y hay que cerrarlo y abrirlo de nuevo, teniendo que conectarme por VNC y hacer el proceso a mano. Con un botón de este tipo puedo lanzar un script que permite a cualquiera reiniciar el navegador en todo momento.

Out!

Directorio home de usuarios + nfs4: lentitud en el escritorio.

Una de las cosas que mas me gustan de Linux en un entorno de red es la capacidad de que los homes de los usuarios estén centralizados en un servidor de red común, de tal forma que siempre tenemos el mismo escritorio independientemente de la máquina usada. Esto facilita al usuario trabajar sin tener que andar subiendo ficheros a una nube o carpeta concreta de red, y a nosotros hacer copias de seguridad de sus datos y no preocuparnos de recuperar datos del usuario cuando falle físicamente el disco del equipo donde ha estado trabajando.

En nuestro caso siempre hemos usado NFS para montar estos homes, aunque hay mas alternativas. Nunca había habido problema pero cuando cambiamos hace unos años a usar Ubuntu 18 empezaron a aparecer problemas aleatorios de lentitud extrema en los escritorios/navegadores web. Una explicación común es que el aumento de complejidad de los navegadores en los últimos años (y eso es algo de lo que habrá que hablar algún día martillo en mano) los ha convertido en unos artefactos que con sus operaciones de entrada/salida acaban saturando el servidor NFS. Una pista en contra de esta hipótesis es que la lentitud va y viene sin correlación aparente con el número de usuarios instantáneos en la red del centro y que cuando una máquina va lenta, la de al lado no tiene por qué hacer lo mismo. Tenía que haber alguna explicación adicional.

Después de muchas pruebas hemos establecido que cambiar el sistema de montaje de los homes de los clientes de nfs4 a nfs3 mejora enormemente el problema, haciéndolo desaparecer en muchos casos. Vamos a ver las pruebas a realizar cuando tenemos un escritorio/navegador ralentizado para determinar si este cambio en el nfs solucionaría el problema.

Las pruebas que podemos realizar son las siguientes:
  • Entrar por ssh como root a un puesto donde el escritorio vaya lento y hacer el comando "time su <usuario>", siendo "usuario" un usuario que monta su home por NFS, claro está. Hacer lo mismo en un equipo no afectado por el problema. Si en el primero tarda 5 o mas veces que el segundo es un síntoma de que está afectado.
  • Entrar por ssh como root a un puesto donde el escritorio vaya lento y hacer "wget https://speed.hetzner.de/1GB.bin". Hacer lo mismo en un equipo no afectado por el problema. Es un fichero de prueba de 1Gb para hacer tests de velocidad desde consola. Si la velocidad es similar en ambos puestos podemos descartar que sea un problema de red (bucles de red, problemas de cableado, de switchs, de salida a Internet) y centrarnos en que es un problema de NFS.
  • Entrar por ssh como root a un puesto donde el escritorio vaya lento y mirar en el syslog buscando errores como:
    NFS: nfs4_reclaim_open_state: Lock reclaim failed!
    NFS: nfs4_reclaim_open_state: unhandled error -10026
    nfs4_reclaim_open_state: 3 callbacks suppressed
        
    Estos errores son síntomas de bloqueos y otros problemas en el montaje NFS.
  • Entrar en el servidor NFS y testear la carga con los comandos:
    # atop
    # iotop
    # nfsstat
      
    Buscando sobrecargas en el disco, red o en los procesos nfs.
  • Entrar por ssh como root a un puesto donde el escritorio vaya lento y hacer
    # nfsiostat
      
    Comparar el resultado con un equipo no afectado por el problema.
Después de todas estas pruebas, si sospechamos que las causas de la lentitud están relacionadas con el servidor NFS, el siguiente paso es verificar que tipo de montaje usamos en nuestros puestos. Para ello entramos por ssh como root en un equipo de usario y hacemos:
# automount -m

global options: none configured

  Mount point: /home

  source(s):

   type: ldap
   map: ldap:ou=auto.home,ou=Automount,dc=instituto,dc=extremadura,dc=es
   ....
   ....
   * | -fstype=nfs4,rw,hard,intr,nodev,nosuid,nolock,rsize=16384,wsize=16384 servidor:/&
La última línea nos dice que el montaje del home esta siendo realizado mediante nfs4. No tiene porque ser igual a la indicada, pero el "nfs4" es revelador. La idea es cambiarla por:
   * | -fstype=nfs,rw,fg,hard,intr,nodev,nosuid,async,ac,vers=3,fsc servidor:/home/&
que cambia el montaje del home para que se haga usando nfs3. Esta línea si que debemos ponerla de forma literal, tal cual aparece aquí.

El lugar donde se hace este cambio dependerá de donde configuremos el montaje en nuestro entorno. Para el caso de nuestros centros esto se hace en el directorio ldap, al que accedemos mediante phpldapadmin en el nodo:
cn=/,ou=auto.home,ou=Automount,dc=instituto,dc=extremadura,dc=es
Una vez allí localizamos el atributo "automountInformation", hacemos una copia de su contenido por si las moscas y lo sustitumos por:
* | -fstype=nfs,rw,fg,hard,intr,nodev,nosuid,async,ac,vers=3,fsc servidor:/home/&
Después de esto recargamos el servicio nfs en el servidor principal:
/etc/init.d/nfs-kernel-server reload
Y por último reiniciamos los clientes, dejando que trabajen durante varios días a ver si desaparecen los problemas. En bastantes casos con este sencillo cambio ha sido suficiente.

viernes, 11 de noviembre de 2022

Log histórico de portátiles encendidos cada día

Por una necesidad concreta he escrito un script que va rellenando un log de los portátiles de alumnas que se encienden cada día en el instituto. La finalidad es generar un listado tabulado como el siguiente:
...
2022/11/11-08:37:10	portinves-o11	10.192.55.97
2022/11/11-08:37:10	portinves-o12	10.192.54.74
2022/11/11-08:37:23	portinves-o04	10.192.54.91
2022/11/11-08:37:30	portlenovo-o02	10.192.55.106
2022/11/11-08:37:38	portinves-o01	10.192.54.115
2022/11/11-08:37:45	portinves-o09	10.192.54.114
2022/11/11-08:37:46	portinves-o10	10.192.54.159
2022/11/11-08:37:51	portinves-o15	10.192.54.110
2022/11/11-08:38:09	portinves-o05	10.192.54.78
2022/11/11-08:38:26	portinves-o02	10.192.54.116
...
Aunque esa información puede encontrarse en la herramienta "controlies", quería tenerla centralizada en un fichero de texto plano fácilmente procesable para hacer estadísticas. Para saber que portátiles se han encendido puedo consultar el fichero /var/log/syslog del servidor, filtrando por "DHCPACK" y por el nombre "port":
# grep DHCPACK /var/log/syslog | grep -i port
...
Nov 11 10:37:21 servidor dhcpd: DHCPACK on 10.192.54.196 to 28:e3:47:32:7e:0e (portinves-o16) via 10.192.54.1
Nov 11 10:58:53 servidor dhcpd: DHCPACK on 10.192.55.111 to 6c:62:6d:14:e4:36 (portmsi-o13) via 10.192.54.1
Nov 11 11:10:29 servidor dhcpd: DHCPACK on 10.192.55.112 to 6c:62:6d:15:14:b6 (portmsi-o02) via 10.192.54.1
...
En mi caso esto es sencillo porque todos los portátiles de alumnos se llaman "portXXX-oXX". Otro filtro que se puede usar es la MAC, ya que la mayoría de portátiles del mismo lote tiene un prefijo de MAC idéntico.

Una vez obtenido el listado lo manipulo uno poco con utilidades bash para extraer la marca de tiempo, nombre e IP. Luego se ordena y eliminan líneas duplicadas, llevando todo a /var/log/portatiles.log. El script queda:
#!/bin/bash

dhcp_hoy=$(mktemp)
log_portatiles_hoy=$(mktemp)
log_portatiles="/var/log/portatiles.log"

grep DHCPACK /var/log/syslog | grep -i port  > $dhcp_hoy

while read -r line
do
  fecha=$(echo $line | head -c 15)
  fecha=$(date -d"$fecha" +%Y/%m/%d-%H:%M:%S)
  ip=$(echo $line | cut -d" " -f8)
  mac=$(echo $line | cut -d" " -f10)
  nombre=$(echo $line | cut -d" " -f11 | tr -d "()")

  echo -e "${fecha}\t${nombre}\t${ip}" >> $log_portatiles
done < $dhcp_hoy

cat $log_portatiles_hoy >> $log_portatiles

sort -u  -o $log_portatiles $log_portatiles

exit 0
Todo esto se pone en un script del servidor que es invocado un par de veces al día usando crontab. Con eso tendremos el log preparado para consultar y disponer de esa información por si nos la requieren.


Los 同志 chinos han acabado de ensamblar en órbita su Estación Espacial:
Han tenido que montarse su propia estación debido a las reticencias de USA. Cuando no se quiere colaborar no queda otra que competir... y competir contra China es cada vez más complicado. No creo que hayamos aprendido la lección.

Zabbix (IV): creación de plantilla con items y triggers para agentes Linux.

En la primera entrada dedicada a Zabbix vimos templates para monitorizar impresoras Brother color. De todas las impresoras que tengo de esta marca he notado que la Broher DCP-9020CDW cada varios días se bloqueaba y me obligaba a reiniciarla. Si desactivaba la monitorización de Zabbix de esa impresora el problema desaparecía.

He deducido que alguno de los ítems que interrogaban a la impresora acababan finalmente provocando su cuelgue. Durante varios días he ido habilitando e inhabilitando ítems de la plantilla por grupos, mediante un divide y vencerás, hasta dar con el culpable: el "JetDirect Check".
Como es un ítem intrascendente que venía en la plantilla por defecto, lo he dejado desactivado (casilla "Enabled" desmarcada) y ya no ha vuelto a bloquearse:
¡Asunto solucionado!

viernes, 30 de septiembre de 2022

Filtrando publicidad con powerdns (III)

Nuestro filtro de publicidad basado en PowerDNS funciona bastante bien limpiando anuncios indeseados de las páginas y agilizando la navegación dentro de nuestra red.

El problema es que a veces filtra demasiado y nos quedamos sin ver cosas que pueden interesarnos (o no, como ATRESplayer). Esta semana me comunicaron que no se cargaban los contenidos de RTVE Play. Tras analizar el tráfico web con las opciones de desarrollador del navegador veo que se están cortando peticiones web a una serie de sitios con el nombre "gigya" y esto acababa repercutiendo en que no se cargaban los vídeos de RTVE.

La solución es no filtrar esos sitios, pero recordeoms que el script que construye la lista negra recopila los datos de varias ubicaciones web y no es sencillo quitar de esa lista uno a uno manualmente los sitios que si deseo permitir. Es el momento de implementar una "lista blanca" con patrones para identificar los sitios permitidos. El script blacklist-powerdns.sh quedaría:
# cat blacklist-powerdns.sh 
#!/bin/bash

#Crea fichero de bloqueos de publicidad y sitios web para powerdns.

#Descargamos listados de sitios web de publicidad a bloquear.
cp /dev/null /tmp/filter-hosts

wget -N -O /tmp/hosts https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
grep "^0.0.0.0" /tmp/hosts |  awk '{print $2}' >> /tmp/filter-hosts
echo "" >> /tmp/filter-hosts

wget -N -O /tmp/hosts http://sysctl.org/cameleon/hosts
grep "^127.0.0.1" /tmp/hosts | awk '{print $2}' >> /tmp/filter-hosts
echo "" >> /tmp/filter-hosts

wget -N -O /tmp/hosts https://mirror1.malwaredomains.com/files/justdomains
cat /tmp/hosts >> /tmp/filter-hosts
echo "" >> /tmp/filter-hosts

wget -N -O /tmp/hosts https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
cat /tmp/hosts >> /tmp/filter-hosts
echo "" >> /tmp/filter-hosts

wget -N -O /tmp/hosts https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
cat /tmp/hosts >> /tmp/filter-hosts
echo "" >> /tmp/filter-hosts

#Este enlace está muerto
#wget -N -O /tmp/hosts https://hosts-file.net/ad_servers.txt
#cat /tmp/hosts >> /tmp/filter-hosts

#Añadimos lista negra de dominios mantenida por nosotros
if [ -f /etc/powerdns/bloqueados.list ]
then
   cat /etc/powerdns/bloqueados.list >> /tmp/filter-hosts
   echo "" >> /tmp/filter-hosts
fi

#Ordenamos y quitamos dominios repetidos. Borramos comentarios.
cp /tmp/filter-hosts /tmp/filter-hosts2
sort -u -o /tmp/filter-hosts /tmp/filter-hosts
sed -i '/^localhost$/d' /tmp/filter-hosts
sed -i '/^#/d' /tmp/filter-hosts

#Quitamos de la lista los que tienen las palabras en /etc/powerdns/permitidos.list
if [ -f /etc/powerdns/permitidos.list ]
then
   for cadena in $(cat /etc/powerdns/permitidos.list)
   do
     sed -i  "/$cadena/d" /tmp/filter-hosts
   done
fi

#Generamos el fichero blocklist.lua compatible con powerdns
echo "return {" > /tmp/blocklist.lua
for i in $(cat /tmp/filter-hosts)
do
   echo \"$i\", >> /tmp/blocklist.lua
done
echo "}" >> /tmp/blocklist.lua

#Copiamos fichero de bloqueo DNS y reiniciamos servicio.
/etc/init.d/pdns-recursor stop
/etc/init.d/pdns stop
cp -f /etc/powerdns/blocklist.lua /etc/powerdns/blocklist.lua.bak
cp -f /tmp/blocklist.lua /etc/powerdns/blocklist.lua
/etc/init.d/pdns start
/etc/init.d/pdns-recursor start

exit 0

En negrita está el código nuevo: una vez hecha la lista de sitios filtrados recorremos el contenido del fichero /etc/powerdns/permitidos.list y eliminamos de la lista los sitios que tienen las palabras/subcadenas encontradas allí. En principio he puesto en permitidos.list solo una línea con la cadena "gigya", pero seguramente en los próximos meses vaya añadiendo mas contrafiltros que hagan menos estricta la lista negra con la que venimos trabajando.


Espectacular petardazo de la NASA para desviar un asteroide con el impacto de la sonda DART. Esperemos que, como las armas nucleares, todo se quede en ensayos y no sea necesario usarlo nunca.