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

Mostrando entradas con la etiqueta thinclients. Mostrar todas las entradas
Mostrando entradas con la etiqueta thinclients. Mostrar todas las entradas

jueves, 14 de marzo de 2019

Problemas en el inicio de sesión en thinclients LTSP.

Como ya hemos visto otras veces, en muchos centros seguimos usando el modelo de thinclients con Ubuntu LTSP para seguir sacando partido a máquinas que ya van para 15 años de antigüedad.

Por desgracia, da la impresión que el soporte para thinclients está cada vez mas dejado y nos cuesta trabajito tenerlo operativo.

Durante el todo el curso me han venido advirtiendo de un problema recurrente: cuando un thinclient en el que estaba trabajando un alumno quedaba colgado o se apagaba (el hardware sobre el que corren tiene sus achaques), el alumno ya no podía iniciar sesión en ese u otro thinclient con sus credenciales hasta pasado un tiempo, ya que era expulsado de la sesión a cada intento de entrada. Tenía que usar las de otro usuario para entrar. En los últimos tiempos este problema se había convertido en una manera de interrumpir el desarrollo de la clase para un grupito de alumnos graciosetes.

Mirando lo que pasaba advertimos que se habían quedado un montón de procesos a nombre del usuario abiertos y alguno de ellos bloqueaba el nuevo inicio de sesión. Esto se mantenía así hasta que esos procesos morían (pasado un tiempo o con el reinicio del LTSP).

Una manera rápida de liberar esos procesos es matarlos con uno de los varios comandos que hay para ello, por ejemplo:
# killall -9 -u usuario
Pero esto obliga a que lo haga alguien con permisos de root y acceso a la consola, lo cual no es nada cómodo.

Vamos a repasar como se inicia sesión en un thinclient y dilucidar que está fallando.

1. Proceso de inicio de sesión en thinclients.

Los thinclients usan como display manager ldm, que a diferencia de lightdm y similares, establece una conexión ssh con el servidor de aula para iniciar la sesión en él.

Toda la imagen de disco con el que funcionan los thinclients cuelga de la ruta /opt/ltsp/i386 de los servidores de aula. En esa ruta está el fichero /usr/share/ldm/ldm-script, que se ejecuta desde ldm y que lanza los scripts que hay en /usr/share/ldm/rc.d/.

ldm-script recibe un parámetro en $1 que puede tener los valores init/pressh/start/xsession/stop, tal como se ve en este "case", donde se indica en los comentarios a que evento del inicio de sesión está ligado cada valor:
case "${ACTION}" in
    init)
        # From ldm.c: LDM has just started, X is running, the greeter isn't.
        SCRIPTS="I*"
        ;;
    pressh)
        # From ssh.c: a username/password was provided, no connection yet.
        SCRIPTS="P*"
        ;;
    start)
        # From ssh.c: an ssh connection was established.
        SCRIPTS="S*"
        ;;
    xsession)
        # From plugin.c: an xsession was started (maybe even rdp).
        SCRIPTS="X*"
        ;;
    stop)
        # From screen.d/ldm.c: the xsession has ended.
        SCRIPTS="K*"
        ;;
esac
Lo que hace ldm-script es ejecutar en orden los scripts de /usr/share/ldm/rc.d/ que comienzan por la letra de la variable $SCRIPTS del paso anterior. Esto nos permite tener una secuencia de scripts asociados a cada evento de ldm. Los ficheros contenidos en mi rc.d son:
I00-localapps-cleanup
I01-halt-check
I01-nbd-checkupdate
K99-ltsp-cluster
K99_seguimiento_usuario
P00-ltsp-cluster
S01-setup-xauth
S15-userLoginCheck
S20-restrictUser
S99-debug-terminal
S99-ltsp-cluster
S99_seguimiento_usuario
X01-localapps
X01-remoteapps
X02-genmenu
X10-delayed-mounter
X50-dmrc-processing
X50-generate-env
X50-printers
X51-localapps
X51-opengl
X51-remoteapps
X52-xcompmgr
X95-run-x-session
X98-delayed-mounter
X99-ltsp-logout-action
Como se ve, hay un puñado de ficheros ligado a cada tipo de evento.

2. Inicio de sesión de usuario.

Los scripts comenzados por "S" son los que se ejecutan una vez el usuario ha tecleado usuario y contraseña, se ha establecido conexión ssh y se va a iniciar sesión gráfica.

Entre estos script hay uno llamado /usr/share/ldm/rc.d/S15-userLoginCheck cuya función es comprobar si LDM_LIMIT_ONE_SESSION es true y en caso afirmativo, matar todos los procesos previos del usuario. LDM_LIMIT_ONE_SESSION es una variable definida en /etc/lts.conf y tiene como función "Only allow a given user to log into one thin-client at a time", es decir, solo se permite que un usuario haga login en único thinclient al mismo tiempo. Para ello el script S15-userLoginCheck mata todos los procesos y sesiones previas del usuario.

Bien, pues esto que ha venido funcionando durante años, ahora con Ubuntu 18 ha dejado de funcionar. Y como consecuencia de ello tenemos el problema que estamos comentando en esta entrada.

Si miramos en todos los scripts de rc.d vemos que en muchos de ellos se usan las variables LDM_SOCKET, LDM_SERVER y LDM_USERNAME, que deberían contener respectivamente el socket de la conexión ssh entre thinclient y LTSP, la IP del LTSP y el nombre del usuario que está iniciando sesión. Bien, pues aunque antaño estas variables tenían valores correctos, hace ya tiempo que vienen vacías. Esto hace que muchos de los scripts de rc.d no hagan nada.

Podemos solucionar esto dando los valores adecuados a esas variables en cada script, que tras un proceso de investigación averiguamos que consiste en añadir las líneas:
LDM_SOCKET=$(ls -1 /var/run/ldm_socket*  | head -1)
LDM_SERVER=192.168.0.254
LDM_USERNAME=$(ssh -S $LDM_SOCKET $LDM_SERVER "id -un")
pero el hecho de que eso no venga de serie y nadie se ocupe de añadirlo nos da una idea del estado actual de semiabandono del proyecto, pese a lo cual sigue funcionando bastante bien.

Pero es que incluso después de poner las líneas arriba indicadas dentro de S15-userLoginCheck con la esperanza de que volviese a funcionar la matanza de procesos previos del usuario pude comprobar que seguía sin funcionar, y que por desgracia no se podía iniciar sesión por segunda vez. Podría haberme puesto a depurar el código de S15-userLoginCheck, pero antes se me han cruzado un par de asuntos que han solucionado el problema por otra vía.

3. Solución.

Recordé que en /etc/lts.conf había una variable llamada LDM_DIRECTX, que tradicionalmente habíamos tenido con valor False, que es el valor que trae por defecto. Cuando vale False toda la sesión entre thinclient y LTSP se hace cifrada por el túnel ssh, lo cual aporta de seguridad a costa de hacer mas lento el escritorio por sobrecarga de trabajo del thinclient. Un compañero sugirió probar con LDM_DIRECTX = True, que evita el uso de este tunel ssh y lo cierto es que el escritorio de los thinclients iba sensiblemente mas rápido, así que la dejamos con True.

Haciendo caso a un pálpito cambié LDM_DIRECTX = False y regeneré la imagen de thinclients...y ¡voilá!, volvía a funcionar S15-userLoginCheck y un alumno podía iniciar sesión en cualquier lado sin problema, provocando el cierre de la sesión previa. Como contrapartida tenemos que el escritorio de los thinclients quedaba un poco mas lento. ¿Causa de que se arreglase el desaguisado? Je ne sais pas.

No todo queda aquí, en una sincronicidad al poco tiempo otro compañero nos reveló un método para que se liquidasen los procesos de los usuarios que han cerrado sesión, consistente en añadir a /etc/systemd/logind.conf (en nuestro caso, del servidor LTSP) la línea:
KillUserProcesses=yes
con la cual systemd se ocupa de matar procesos de usuarios que han salido del sistema.

No tenía mucha confianza de que funcionase con los thinclients, pero probé a realizar estos pasos:

  1. Añadir KillUserProcesses=yes a logind.conf.
  2. Reiniciar el servicio systemd-logind.
  3. Poner LDM_DIRECTX = True en lts.conf.
  4. Regenerar imagen de thinclients con ltsp-update-image

He podido verificar con alegría que ahora si funcionaba todo bien como antiguamente. Al final el truco para arreglar todo no estaba en nada relacionado con LTSP, sino con logind.conf.

En conclusión: larga vida a los thinclients.


El 22 de febrero pasado la sonda japonesa Hayabusa 2 le dió un piquito al asteoride Ryugu para recoger muestras antes de traerlas a casa:



No es Bruce Willis y su equipo de perforadores de Armageddon, pero la hazaña de llegar hasta un pedrolo de 435 metros, orbitarlo, aterrizar con éxito 2 rovers del tamaño de una Roomba y acercarse a la superficie para chocar suavemente con ella y recoger muestras no es moco de pavo.

En la primera temporada de True Detective el detective Rust observa un grupo de fanáticos religiosos baptistas y sentencia "míralos: estos nunca fisionarán el átomo". Ni aterrizarán en un asteroide.

jueves, 8 de noviembre de 2018

Lentitud de páginas web multimedia en thinclients de LTSP Ubuntu 18.

Me ha llegado una incidencia sobre la lentitud de un aula de thinclients sobre LTSP en Ubuntu 18 al abrir páginas web. Tras hacer pruebas in situ pude comprobar que al abrir varias páginas con 10 o mas thincliens encendidos todo se hacía enormemente lento, incluido el servidor LTSP, de tal manera que era imposible trabajar.

Descartado un problema de red me centro en ver que páginas usan: son unas páginas de idiomas que tienen mucho Flash (Odín maldiga al Flash) y que además abren muchas pestañas de Firefox. Haciendo un "ps aux | grep firefox" veo que hay una burrada de instancias de Firefox abiertas (mas de 40 procesos) y que eso puede provocar la fastidiosa lentitud de alguna manera que se me escapa.

En las últimas versiones de Firefox se ha implementado una feature llamada Electrolysis que, al igual que tiene hace tiempo Chrome, implica que Firefox abra procesos a tutiplén (uno o más por cada pestaña) para mejorar el rendimiento. Eso está muy bien para un entorno monousuario, pero en un entorno donde hay 10 o 12 usuarios en la misma máquina abriendo pestañas puede llevar las cosas al límite, especialmente en nuestros servidores LTSP con CPU Intel Core2 Quad de casi 10 años de antigüedad.

¿Se puede controlar este desmadre procesil? Pues en parte si, tocando varias configuraciones. Para hacerlo automático y que se aplique de forma obligatoria a todos los usuarios del PC (evitando tener que tocarlo perfil a perfil) lo mejor es usar el fichero syspref.js global de /etc/firefox:
# cat /etc/firefox/syspref.js
...
...
...
//Limitar el nmero de procesos firefox. Thinclients.
pref("browser.tabs.remote.autostart", false, locked);
pref("extensions.e10sMultiBlockedByAddons", false, locked); 
pref("dom.ipc.processCount", 1, locked); 
y distribuir dicho fichero mediante puppet a las máquinas que lo necesiten.

Con esto el número de instancias de Firefox se queda en 1-2 para cada usuario y la cosa se hace sensiblemente mas manejable: antes iba como el culo y ahora va casi medio bien.

Otra opción podría ser ejecutar Firefox de forma local en los thinclients, con sus propios recursos. Eso se hace lanzándolo así:
# ltsp-localapps firefox
El problema de esto es que:
  • Se ejecuta el Firefox y el Flash de 32bits contenido en la imagen de los thinclients, sensiblemente mas antiguo que el del servidor LTSP. Las imágenes de los thinclients están basadas en Ubuntu 14.
  • Al ejecutarse con el procesador y memoria de los thinclients tarda en arrancar y puede ir bastante ralentizado (suelen ser Pentiums IV con 512Mb de RAM). La ventaja es que no satura el servidor ni los thinclients ajenos.
Si aún así queremos hacerlo funcionar la forma mas adecuada es poner un script en los servidores LTSP que detecte si estamos en un thinclient o el servidor (examinando el contenido de la variable $LTSP_CLIENT) para lanzar el Firefox de una manera u otra:
# cat /usr/local/bin/firefox
#!/bin/bash
if [ ! -z "$LTSP_CLIENT" ]; then
       ltsp-localapps firefox
else
       /usr/lib/firefox/firefox "$@"
fi
exit 0
# chmod +x /usr/local/bin/firefox
Al estar /usr/local/bin por delante de cualquier otro directorio en $PATH siempre se ejecutará primero el script al llamar a "firefox".

Con esto conseguimos que la cosa tire mejor en tanto en cuanto Flash no sea purgado de la faz de la Tierra.

lunes, 16 de abril de 2018

Captura de la pantalla de los alumnos (I)

Lamento este tiempo de silencio, he estado muy ocupado haciendo un Máster en Estupefacción.

Siempre es muy útil para el profesor controlar que uso están haciendo los alumnos con sus ordenadores en el aula. Para esto hemos usado hasta ahora Aulalinex o epoptes. El problema es que ambos son sencillos de usar para observar que hace un alumno concreto, pero no lo son para un grupo de ellos o todos a la vez. Además, desde hace un tiempo nuestro Aulalinex no acaba de funcionar al 100% en Ubuntu.

Un profesor me planteó la posibilidad de hacer una "foto" colectiva de todas las pantallas, que dejase las imágenes con las capturas de los escritorios en un directorio concreto para su consulta posterior. El problema es interesante, pero nos encontramos que aunque desde la óptica del profesor todo son aulas con ordenadores de alumnos, desde un punto de vista interno tenemos al menos 3 escenarios:

  • Aulas con thinclients, donde los ordenadores de los alumnos ejecutan su escritorio y aplicaciones dentro del ordenador del profesor. Sus pc son terminales tontos que apenas hacen nada que no sea conectarse remotamente y abrir sus sesiones en el pc del profesor a través de la red interna 192.168.0.X del cada aula.
  • Aulas con ordenadores independientes para alumnos dentro de una VLAN privada, con direcciones 192.168.0.X. Estos ordenadores pueden ser portátiles o sobremesa (aulas "infolab"), realmente son el mismo caso.
  • Aulas con ordenadores independientes para alumnos que no están aislados, sino que están en la red general del centro y tienen IPs 172.X.Y.Z.

Internamente, el manejo de esta casuística es muy diferente. Por ello iremos por partes y solucionaremos primero el caso de las aulas con thinclients, que es seguramente el más sencillo. En este escenario todas las sesiones están dentro del propio pc del profesor de tal manera que no hace falta ninguna conexión remota: todo queda en casa.

Lo primero es averiguar si hay alumnos en el aula conectados desde los thinclient. Eso se consigue lanzando el comando "who" en el pc del profesor:
cbayont04@a23-pro:~$ who
sjimenezt09 pts/5        2018-04-12 08:43 (a23-o03.local)
cbayont04    :0           2018-04-12 08:33 (:0)
sruizb13    pts/1        2018-04-12 08:43 (a23-o06.local)
........
Aquí se ve que hay un profesor en la consola (:0) y dos alumnos conectados remotamente desde los thinclients a23-o03 y a23-o06. El código para verificarlo será:
thinclients=$(who |  grep ".local)$")  #Si hay logeados usuarios con  el prefijo .local) al final estamos en un aula de thinclients y esos son los alumnos
if [ -z "$thinclients" ]
then 
   echo "No detecto thinclients. Abortando.
   exit 1
else #Son thinclients
   ...
Luego preparamos un directorio para grabar las capturas de la pantalla (en negrita):
pc=$(hostname)
fecha=$(date +%Y-%m-%d)
hora=$(date +%H:%M:%S)
destino="$HOME/capturas/$pc/$fecha/$hora"
rm -rf "$destino"
mkdir -p "$destino"
cd "$destino"
A continuación se identifican las IP de los pc encendidos mediante un nmap en el rango 192.168.0.200-253 (el usado en mi caso para los thinclients) e iteramos sobre ellas:
encendidos=$(nmap -oG - --system-dns  -sP 192.168.0.200-253 | grep -v '^#' | tr -d '()' | sed 's/.local//' | awk '{print $2}' | sort)
for ip in $encendidos
do
Seguidamente obtenemos el nombre del thinclient a partir de la IP (usando avahi-resolve-address ya que los thinclients no están dados de alta en nuestro servidor DNS y la resolución de nombres la realizan ellos mismos mediante el demonio avahi) y el nombre del alumno que ha hecho login en dicho thinclient.
pc=$(avahi-resolve-address $ip | cut -f2)  #El delimitador por defecto es  
if [ -n "$pc" ]    #Si resolvemos el nombre, esta operativo
then
  user=$(who | grep -i $pc | cut -d" " -f1)
  if [ -n "$user" ]  #Si tiene user logueado
  then
Por último ya solo queda capturar el escritorio del thinclient y copiarlo a la carpeta $destino. Para ello debemos averiguar la ruta del $HOME del alumno (con "getent passwd ..") y tomar la identidad de dicho usuario (con "su $user") para realizar la captura de su escritorio completo con:
home_user=$(getent passwd "$user" | cut -d: -f6)
su $user -c  "DISPLAY=$ip:7  XAUTHORITY=$home_user/.Xauthority import -window root /tmp/$user-$pc.jpg"
cp /tmp/$user-$pc.jpg $destino/$user-$pc.jpg
El comando anterior:
DISPLAY=$ip:7  XAUTHORITY=$home_user/.Xauthority import -window root /tmp/$user-$pc.jpg
digamos que lo he encontrado a base de prueba-error.

Para poder hacer la captura del escritorio del thinclient conectado remotamente hay que definir las variables DISPLAY y XAUTHORITY correctas para acceder a dicho escritorio con la credencial del alumno y tener los permisos adecuados. El acceso a las sesiones X desde otra sesión X es algo que está siempre bastante protegido para evitar espionaje entre usuarios y es complicado incluso para el usuario root. Finalmente la foto se guarda en /tmp/$user-$pc.jpg y de ahí la llevamos luego a $destino/$user-$pc.jpg.

Una vez recopiladas las capturas de todas las IP abrimos la carpeta $destino con thunar y así el profesor puede ver todas las imágenes cómodamente.

El script completo es:
# cat /usr/bin/captura-escritorio-todos
#!/bin/bash

thinclients=$(who |  grep ".local)$")  #Si hay logeados usuarios con  el prefijo .local) al final estamos en un aula de thinclients y esos son los alumnos

if [ -z "$thinclients" ]
then 
   echo "No detecto thinclients. Abortando."
   exit 1
else #Son thinclients
  pc=$(hostname)
  fecha=$(date +%Y-%m-%d)
  hora=$(date +%H:%M:%S)
  destino="$HOME/capturas/$pc/$fecha/$hora"
  rm -rf "$destino"
  mkdir -p "$destino"
  cd "$destino"

  #En los thinclients el DNS ip<-->nombre se publica desde cada puesto mediante avahi
  encendidos=$(nmap -oG - --system-dns  -sP 192.168.0.200-253 | grep -v '^#' | tr -d '()' | sed 's/.local//' | awk '{print $2}' | sort)
  for ip in $encendidos
  do
     pc=$(avahi-resolve-address $ip | cut -f2)  #El delimitador por defecto es  , por eso no uso -d
     if [ -n "$pc" ]    #Si resolvemos el nombre, esta operativo
     then
         user=$(who | grep -i $pc | cut -d" " -f1)
         if [ -n "$user" ]  #Si tiene user logueado
         then
             sudo /usr/bin/captura-escritorio-thinclient $user $ip $pc
             cp /tmp/$user-$pc.jpg $destino/$user-$pc.jpg
         fi
     fi
  done
  thunar "$destino"
fi

exit 0
El otro script usado es:
# cat /usr/bin/captura-escritorio-thinclient
#!/bin/bash

user=$1; ip=$2; pc=$3
home_user=$(getent passwd "$user" | cut -d: -f6)
su $user -c  "DISPLAY=$ip:7  XAUTHORITY=$home_user/.Xauthority import -window root /tmp/$user-$pc.jpg"
   
exit 0
Para que el el script llamado por el usuario normal pueda hacer el "su $user ..." necesitamos que esa parte se ejecute con permisos de root, que es el único usuario que puede adoptar la identidad de otro sin saber su contraseña. Por eso llevamos esa parte a otro script que ejecutaremos con "sudo". Evidentemente también hay que modificar /etc/sudoers para permitir su ejecución con sudo a todos los usuarios regulares del grupo teachers (profesores del centro).
# cat /etc/sudoers
....
....
%teachers ALL = (ALL) NOPASSWD: /usr/bin/captura-escritorio-thinclient
....
....
Al finalizar la captura de todos los escritorios de alumnos se abre la carpeta $destino con todas las imágenes de las capturas de escritorio para que el profesor pueda revisarlas tranquilamente con el visor de fotografías de nuestro sistema operativo:


Bueno, pues está resuelto el caso mas simple. Quedan dos pendientes para continuar la serie y esta vez no vamos a esperar tanto para continuar.