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

martes, 14 de noviembre de 2017

Conectar por ssh/vnc al PC del profesor tras el punto de acceso DLink DIR-860L

En esta entrada anterior vimos como hacer NAT en el aula usando un punto de acceso DLink DIR-860L, de manera que los PC de alumnos y profesor quedaban en red privada dentro del rango 192.168.0.X.

El punto de acceso tenía dos direcciones: la 172.X.Y.Z (que sería la pública del centro) y la 192.168.0.254 (dentro de la red privada del aula), haciendo NAT entre ellas. Esto aísla por completo el aula de la red del centro pudiendo asegurar que el DLink realiza el NAT de una forma bastante más eficiente que los equipos HP que nos trajeron a los infolab.

El problema derivado es que no podemos conectar por ssh ni por vnc con el ordenador del profesor, que queda detrás del DLink. Esto causa bastante trastorno ya que es muy normal conectar para realizar diversas tareas de forma remota. Existe una vía indirecta para conectar por ssh, entrando primero en el DLink y luego desde alli haciendo ssh root@192.168.0.100, pero aparte de ser lento no podemos usar ssh -X para abrir aplicaciones gráficas sobre la conexión.

La solución está en configurar el port forwarding en el DLink para redigir determinadas conexiones a 172.X.Y.Z para que acaben en el 192.168.0.100 de la red privada. Es el famoso "abrir puertos" que hemos hecho todos en el router de casa para usar torrent o juegos.

El método paso a paso para abrir puertos en el entorno web de DD-WRT de nuestro punto de acceso DLink está descrito en este vídeo.

Para redirigir ssh he optado conectar el puerto 23 de la IP externa con el puerto 192.168.0.100:22, mediante la opción NAT-Qos -> Port Forwarding:


Uso el puerto 23 porque el puerto 22 está reservado para el servicio sshd del punto de acceso y no quiero perder la posibilidad de hacer ssh al DLink. Una vez aplicados los cambios, si desde cualquier punto de la red del centro hacemos:
$ ssh -X -p 23 root@172.X.Y.Z
conectaremos con el PC del profesor, saltando de forma transparente el DLink. Al usar "-X" tenemos la posibilidad de abrir aplicaciones gráficas.

Para redirigir vnc he usado la opción NAT-Qos -> Port Range Forwarding:


Pongo el rango de puertos 5900-5910 ya que vnc va usándolos de forma secuencial si hay varias sesiones vnc abiertas. En este caso el mapeo de los puertos es directo: el 5900 de fuera se conecta con el 5900 de dentro. Teniendo arrancado el servidor vnc en el PC del profesor (en mi caso lo arranco a mano cuando lo necesito, entrando antes por ssh) desde fuera haremos:
$ vinagre 172.X.Y.Z 
y entramos en el pc interno al aula.

Con esto tendremos acceso a esos PC igual al que tenemos a cualquier otro PC de profesor del centro.

martes, 7 de noviembre de 2017

Personalizando la pantalla de login de los portátiles de alumnos.

Como ya dijimos en la anterior entrada lo mas extendido es asignar cada portátil a un mismo alumno durante todo el curso.

Para poner las cosas sencillas ponemos una etiqueta adhesiva en la tapa o en el inferior con los datos del portátil y del alumno, pero esta etiqueta se puede quitar/caer y, por otro lado, no es infrecuente que algunos alumnos estén en la luna de Valencia y acaben confundiendo sus portátiles.

Una buena solución es que en la pantalla de login salga algo que identifique al alumno justo antes de poner sus credenciales: una foto, un texto con su nombre, etc. De esta manera se minimiza la posibilidad de coger otro portátil de forma accidental ya que se darán cuenta al meter las credenciales. Mi compañero Manu hizo la aplicación autolabel, que toma los datos del árbol ldap (en su árbol ldap hay campos que asocian portátil y alumno) y modifica el greeter del gdm3 (fichero /etc/gdm3/greeter.gsettings) para mostrar en él la foto y nombre del alumno.

Con xubuntu nos hemos cambiado a lightdm que desgraciadamente no tiene la versatilidad de gdm3 para poner imágenes y textos en la pantalla de login. Pero que no cunda el pánico: siempre podemos coger la imagen de fondo del greeter y manipularla con la utilidad convert para añadir la foto y nombre del alumno.

Para ello tengo un script que obtiene el login del alumno asociado al portátil, coge su foto y otros campos (tal como ya hicimos en la entrada anterior) y modifica nuestro fondo estándar de lightdm, almacenado en "/usr/share/xfce4/backdrops/fondo_1a.jpg".
# cat cambia_background_greeter.sh 

#!/bin/bash
#Curso 17-18-v1.
nombre_pc=$(/bin/hostname)
ldaphost=ldap
base=dc=instituto,dc=extremadura,dc=es
userbase=ou=People,$base
interno="cn=interno,dc=instituto,dc=extremadura,dc=es"
pwd_interno="mipwdinterno"
tmp="/tmp"
fondo_original="/usr/share/xfce4/backdrops/fondo_1a.jpg"
fondo_bak="/usr/share/xfce4/backdrops/fondo_1a_bak.jpg"
fichero="/root/scripts/asignaciones.txt"

test -e $fondo_bak || cp -f $fondo_original $fondo_bak

linea=$(/bin/grep -i -e "^${nombre_pc};" $fichero)
if [ -z $linea ] # no encontrado nombre.
then
  echo "ERROR: No encuentro portatil. Abortado"
  exit 1
fi

#Llegados aquí, en $linea tenemos nombre-pc;mac;usuario;password;grupo
usuario=$(echo $linea | /usr/bin/cut -d";" -f3)
pass=$(echo $linea | /usr/bin/cut -d";" -f4)
grupo=$(echo $linea | /usr/bin/cut -d";" -f5)
nombre=$(ldapsearch -w $pwd_interno  -D $interno -xLLL -t -h $ldaphost -b $userbase "(&(objectClass=posixAccount)(uid=$usuario))" | grep "^cn: ")
nombre=${nombre:4} # quitamos "cn: "

rm -f $tmp/ldapsearch-jpegPhoto-*
ldapsearch -w $pwd_interno  -D $interno -xLLL -T $tmp -t -h $ldaphost -b $userbase "(&(objectClass=posixAccount)(uid=$usuario))" jpegPhoto
if [ -e $tmp/ldapsearch-jpegPhoto-* ]; then
  mv -f $tmp/ldapsearch-jpegPhoto-* $tmp/.face 2> /dev/null
else
  cp -f /root/scripts/anonymous.jpg $tmp/.face 2> /dev/null # Foto genérica para alumnos sin foto.
fi
convert $fondo_bak  \( $tmp/.face -resize 200x200 \) -geometry +1700+50 -composite -matte $tmp/output.jpg
comando="text 20,300 \"Alumno: $usuario\"; text 20,340 \"${nombre}\"; text 20,380 \"Grupo: ${grupo}\""
convert $tmp/output.jpg  -pointsize 30 -fill yellow -gravity northeast -draw "$comando" $fondo_original
rm $tmp/.face
rm $tmp/output.jpg

exit 0
Recordemos que en pwd_interno debemos poner la contraseña que hayamos puesto a nuestro usuario "interno" de ldap. Todo esto se distribuye y ejecuta una vez más desde una tarea puppet:

class xubuntu_portatil {
  .....
  .....
  .....
  #Etiquetado fondo lightdm con datos alumno

  file {"/root/scripts/cambia_background_greeter.sh":
        owner=>root, group=>root, mode=>755,
        source=>"puppet:///modules/xubuntu_portatil/cambia_background_greeter.sh",
        notify => Exec["etiqueta-lightdm"],
  }

  file {"/root/scripts/anonymous.jpg":
        owner=>root, group=>root, mode=>755,
        source=>"puppet:///modules/xubuntu_portatil/anonymous.jpg",
        notify => Exec["etiqueta-lightdm"],
  }

  exec { "etiqueta-lightdm":
                        path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                        command => "/root/scripts/cambia_background_greeter.sh",
                        require => [File["/root/scripts/cambia_background_greeter.sh"],File["/root/scripts/anonymous.jpg"]],
                        refreshonly => true,
  }

  .....
  .....
  .....
}
El fichero anonymous.jpg es cualquier fichero con una foto genérica que queramos poner como imagen por defecto. Yo tengo una imagen tipo:


Tras ejecutarse el script al actualizar por puppet la pantalla de login del portátil quedaría tal que así:


El próximo año bastaría con cambiar el fichero asignaciones.txt distribuido en la tarea anterior y modificar el script un poco para que se baje y ejecute de nuevo.

Y con esto creo que acabo por ahora la serie de preparación de portátiles de alumnos.

Por último, no puedo dejar esto sin conmemorar que hoy es el centenario de la Revolución Rusa y nada mejor que una buena película sobre la gloriosa gesta del primer paseo espacial de Alexey Leonov para homenajearlo:


domingo, 5 de noviembre de 2017

Automatizando la configuración de portátiles de alumnos al inicio de cada curso.


Lo más común en nuestros centros es que los portátiles estén asignados durante todo el curso a un mismo alumno, que es responsable de su buen uso. Eso hace que cada año al comenzar el curso haya que realizar un proceso manual que comprende al menos los siguientes pasos:
  1. Cargar la imagen del sistema operativo en el portátil si fuere necesario, usando Clonezilla o DRBL.
  2. Limpiar datos del usuario anterior y actualizar el portátil.
  3. Copiar la configuración de la red wifi del aula donde va el portátil en el caso de que el portátil sólo vaya a usarse en una única estancia y con contraseña fija.
  4. Actualizar el nombre del portátil. Aquí tenemos 2 opciones: que el nombre coincida con el login del alumno o que el nombre del portátil sea independiente del mismo y permanezca invariable de un año a otro. Cada centro decide como gestionar la nomenclatura según su criterio.
  5. Cachear las credenciales del alumno que va a usar el portátil, para permitir que inicie sesión sin tener conexión a la red del centro.
El paso 1 ya hemos visto hasta la saciedad que puede hacerse de forma bastante automática y concurrente usando DRBL o Clonezilla.

El paso 2 ya contamos como realizarlo en la anterior entrada.

El paso 3 es sencillo: basta con copiar un fichero con la configuración a /etc/NetworkManager/system-connections/ de cada portátil. Ese fichero se saca conectando a la wifi con un portátil de forma manual y luego recuperando ese fichero de configuración para distribuirlo a los demás mediante puppet.

Veremos a continuación como automatizar lo más posible los pasos 4 y 5. Para ello necesitamos una manera de relacionar cada alumno con el portátil que tiene asignado y determinar el nombre del mismo, así como obtener mas información que pueda ser útil, como la contraseña que tendrá el usuario o el grupo al que pertenece.

Lo ideal sería tenerlo todo en una base de datos centralizada (ya sea una BBDD tradicional o en nuestro ldap) y consultar via un servicio web o similar, pero de momento no dispongo de dicho artefacto. Así que me he ido a lo más cutre: tengo un fichero de texto en formato .cvs con todos los datos que necesito. Dicho fichero lo copio via puppet y lo proceso en cada portátil. El formato del fichero es:
# cat asignaciones.txt 
porthp-o11;22:30:43:11:34:3A;aiglesiasz21;12433133;ESO3oA
porthp-o02;22:30:43:11:37:3C;rbarradop11;32212244;ESO3oB
......
Es un fichero con 5 columnas separadas por punto y coma: nombre del portátil, MAC de alguna tarjeta de red del portátil (normalmente ethX), nombre del alumno que lo tiene asignado, contraseña por defecto del alumno y grupo al que pertenece el mismo.

Al ser un .cvs es sencillo abrirlo en una aplicación de hoja de cálculo y rellenarlo de forma adecuada. Los datos de las MAC los podemos obtener de los facter almacenados en servidor:/var/lib/puppet/yaml/facts. Los datos de cada usuario se obtienen del fichero Alumnos.xml que descargamos desde Rayuela.

Este es el script que uso:
# cat cachea-credenciales.sh 
#!/bin/bash

fichero="/root/scripts/asignaciones.txt"  # fichero con las asignaciones de portatiles, con lineas csv en formato nombre-pc;mac;usuario;password;grupo. La mac puede ser "-"
                            # si el pc tiene nombre y se encuentra en el fichero, la mac es despreciada. En ese caso, el pc mantiene su nombre.
temporal="/tmp/macs_pc"
nombre_pc=$(/bin/hostname)
ldaphost=ldap
base=dc=instituto,dc=extremadura,dc=es
userbase=ou=People,$base
interno="cn=interno,dc=instituto,dc=extremadura,dc=es"
pwd_interno="mi_contrasena"

#Buscamos pc por nombre en fichero de asignaciones. 
#Nota: si queremos cambiar el nombre del pc en funcion de la mac habria que buscar aquí por mac primero y luego ajustar los nombres.
#Eso no lo hacemos, pero queda dicho.

linea=$(/bin/grep -i -e "^${nombre_pc};" $fichero)
if [ -z $linea ] # no encontrado nombre, buscamos por macs
then
  /sbin/ifconfig -a | /bin/grep HWa | tr -s ' ' | cut -f5 -d' ' | tr '[a-z]' '[A-Z]' | sort -u > $temporal # se cogen todas macs.
  macs_pc=$(/bin/cat $temporal)
  linea=$(/bin/grep -i -f $temporal $fichero)
  if [ -z $linea ]
  then 
     echo "ERROR: No encuentro las macs $macs_pc. Abortado"
     exit 1
  else
     #mac encontrada, hay que dar nombre al pc
     nombre_pc=$(echo $linea | /usr/bin/cut -d";" -f1)

     echo "127.0.0.1       localhost
127.0.1.1       $nombre_pc

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters" > /etc/hosts
     echo "$nombre_pc" >  /etc/hostname
     hostname -F /etc/hostname
  fi
fi

#Llegados aquí, en $linea tenemos nombre-pc;mac;usuario;password;grupo

usuario=$(echo $linea | /usr/bin/cut -d";" -f3)
pass=$(echo $linea | /usr/bin/cut -d";" -f4)
grupo=$(echo $linea | /usr/bin/cut -d";" -f5)

#Chequeamos credenciales por defecto del alumno  intentando acceder al campo privado employeeNumber
eNumber=$(ldapsearch  -w $pass -D "uid=$usuario,$userbase" -xLLL -t -h $ldaphost -b $userbase "(&(objectClass=posixAccount)(uid=$usuario))" employeeNumber | grep "^employeeNumber" | cut -d" " -f2)
if [ "$eNumber" != "" ]
then
   #Cachea credencial por defecto alumno
   /usr/sbin/cc_test -store any $usuario $pass 
   /usr/sbin/nss_updatedb ldap
else
   echo "WARNING: El alumno ha cambiado la contraseña, no se cachea."
fi

#Buscamos el homeDirectory del alumno en ldap
homeDirectory=$(ldapsearch -x -h $ldaphost -b $userbase "uid=$usuario" | grep "^homeDirectory" | cut -d" " -f2)
if [ "$homeDirectory" != "" ]
then
   #Si no existe se crea
   test -d $homeDirectory || /sbin/mkhomedir_helper $usuario
   #Y se baja la foto para guardarla en .face
   rm -f $homeDirectory/ldapsearch-jpegPhoto-*
   ldapsearch -w $pwd_interno  -D $interno -xLLL -T $homeDirectory -t -h $ldaphost -b $userbase "(&(objectClass=posixAccount)(uid=$usuario))" jpegPhoto
   if [ -e $homeDirectory/ldapsearch-jpegPhoto-* ]; then
          mv -f $homeDirectory/ldapsearch-jpegPhoto-* $homeDirectory/.face 2> /dev/null
          chown root  $homeDirectory/.face 2> /dev/null
          chmod 444 $homeDirectory/.face 2> /dev/null
   fi
fi

exit 0
Básicamente hace lo siguiente:
  • Obtenemos el nombre del portatil y lo buscamos en el fichero.
  • Si no lo encontramos, obtenemos su MAC y la buscamos. Si aparece la MAC, cogemos el nombre asociado y se lo damos al portátil.
  • Si no encontramos coincidencia en nombre ni MAC se acaba el script.
  • Cogemos el nombre de alumno, su clave y el grupo al que pertenece.
  • Verificamos contra ldap si el alumno mantiene esa clave y no la ha cambiado. Si es así la cacheamos.
  • Conseguimos en ldap su homeDirectory y lo creamos de forma local en el portátil usando mkhomedir_helper.
  • Por último, obtenemos de ldap su foto y la copiamos en el fichero .face de su home local. Es conveniente tenerla para algunas aplicaciones.
  • Para obtener la foto usamos el usuario "interno" que tiene definido nuestro arbol ldap. Este usuario tiene una clave en principio desconocida, pero la podemos cambiar nosotros mismos usando phpldapadmin y luego la ponemos a mano en el script, en la variable "pwd_interno".
A modo de sugerencia, otras cosas que no hace el script pero que sería interesante realizar:
  • Comprobar primero la MAC y ver si el nombre en el portátil coincide con el nombre en el fichero, renombrando en ese caso el portátil al del fichero.
  • Usar el grupo para otras tareas útiles, por ejemplo determinar y autoconfigurar la red wifi a la que se conectará.
Por último, distribuimos ambos ficheros desde nuestra tarea puppet favorita:
class xubuntu_portatil {
  ....
  ....
  file {"/root/scripts/asignaciones.txt":
        owner=>root, group=>root, mode=>500,
        source=>"puppet:///modules/xubuntu_portatil/asignaciones.txt",
  }


  file {"/root/scripts/cachea-credenciales.sh":
        owner=>root, group=>root, mode=>755,
        source=>"puppet:///modules/xubuntu_portatil/cachea-credenciales.sh",
  }
  ....
  ....
}
Podríamos automatizar también su ejecución, como lo tenemos hecho en el script de limpieza de credenciales, pero de momento prefiero lanzarla a mano en cada portátil mientras no adquiere mas rodaje. Ya habrá tiempo de hacerlo de forma desatendida en un futuro.

Todo esto es una mezcolanza de ideas tomadas en los últimos años de scripts que han hecho varios compañeros y han compartido conmigo. ¡Gracias Nando, Manu, Antonio Abasolo y seguramente alguno más!

martes, 31 de octubre de 2017

Limpieza de portátiles de alumnos a principio de curso.

A principio de curso siempre tenemos que preparar los portátiles asignados a los alumnos, empezando por hacer una limpieza general de los mismos. Con los años hemos ido automatizando esto lo más posible para evitar tener que repetir una serie de tareas portátil a portátil.

Aparte de ejecutar puppet y pkgsync para actualizar convenientemente los portátiles, es recomendable borrar las credenciales cacheadas de antiguos usuarios, sus homes y las conexiones wifi creadas en el pasado por los alumnos. Este sería el script a ejecutar al inicio de curso:
# cat limpia_credenciales.sh

#!/bin/bash
#Curso 17-18. Version v1.

#Limpia credenciales cacheadas
for i in $(cc_dump | awk ' {print $3} '); do  cc_test -update any $i -  ; done

#Limpia conexiones wifi antiguas
rm /etc/NetworkManager/system-connections/*

#Limpia homes antiguos
rm -rf /home/alumnos/*

exit 0
Para ejecutarlo la forma mas sencilla es usar unas reglas puppet que aplicaríamos a los portátiles. En mi caso tengo un módulo puppet "omnibús" llamado xubuntu_portatil donde tengo varias pequeñas tareas de este tipo.

El esquema es el típico de estos casos: si no existe o ha cambiado, copiamos el script al portátil y lo ejecutamos.
class xubuntu_portatil {
  
  .......
  .......
  #Limpieza de credenciales a inicio de curso. El script se ejecuta una vez. Si queremos que se ejecute de nuevo
  #hay que cambiar el contenido (p.ej. la versión)

  file {"/root/scripts":
     ensure => directory,
     before => File["/root/scripts/limpia-credenciales.sh"],
  }

  file {"/root/scripts/limpia-credenciales.sh":
        owner=>root, group=>root, mode=>755,
        source=>"puppet:///modules/xubuntu_portatil/limpia-credenciales.sh",
        notify => Exec["limpia-credenciales"],
  }

  exec { "limpia-credenciales":
                        path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                        command => "/root/scripts/limpia-credenciales.sh",
                        require => File["/root/scripts/limpia-credenciales.sh"],
                        refreshonly => true,
  }
  .......
  .......

}
Cada vez que queramos hacer limpieza basta con cambiar algo (basta, por ejemplo, con cambiar la fecha o nº de versión de v1 a v2) en el script almacenado en el directorio "files" de nuestro módulo xubuntu_portatil y se copiará de nuevo y ejecutará.

domingo, 22 de octubre de 2017

rsync con sistemas de ficheros ntfs

Estoy haciendo backups de todas mis imágenes clonezilla a una máquina con partición de destino ntfs y me encuentro con que rsync copia de nuevo muchos ficheros que no han variado cada vez que lo lanzo.

Lo que hace rsync (a no ser que le pongamos el parámetro --checksum) es comparar la fecha de modificación y tamaño de cada fichero en origen y destino y si difiere algo sincroniza de nuevo el fichero. Eso en sistemas ext2/3/4 funciona sin problemas con:
# rsync -avP root@ip-origen:/mnt/partimag/* /mnt/partimag
Pero si uno o ambos sistemas de ficheros es ntfs (y seguramente también fat) no funciona. La causa es que la precisión o el formato de las fechas en ntfs es distinto al de ext2/3/4 y rsync se muestra confundido, considerando fechas distintas las que no lo son. La solución consiste en proporcionar una ventana de error de varios segundos para permitir conciliar bien las fechas:
# rsync -avP --modify-window=5 root@ip-origen:/mnt/partimag/*  /mnt/partimag
Con el ejemplo anterior doy 5 segundos de margen de error al comparar 2 fechas de modificación y tomar la decisión de que han cambiado. Por supuesto eso se puede afinar lo que haga falta en función de cada caso, aunque a mi con 5 segundos me ha ido bien.

lunes, 16 de octubre de 2017

En busca del musthave mínimo.

Como estos días he estado enredando con pkgsync no he podido evitar darme cuenta de que el musthave que tengo en todos los pc es bastante tocho. En su mayoría han sido generados con el script /root/scripts/generar_musthave.sh y por lo que veo tienen mucha redundancia.

Por ejemplo, tener en el musthave los paquetes libreoffice y libreoffice-writer es redundante. Las dependencias del primero implican tener instalado el segundo, por que solo con tener el primero en musthave es suficiente. Sobre las librerías (paquetes lib*) mejor ni hablar: casi todas se instalan de forma automática al cumplir las dependencias de otro paquete de rango superior.

El hecho de tener en musthave solo los paquetes importantes, y no sus dependencias, lo hacen mas manejable y menos susceptible a caer en el "pkgsync installation loop" (esté término me lo acabo de inventar). Este "loop" pasa cuando tenemos en el musthave paquetes que son excluyentes entre si y pkgsync resuelve esta situación esquizofrénica instalando y quitando grupos de paquetes en cada llamada de forma alternativa. Eso no debe ser así, ya que pkgsync debe comportarse de forma idempotente: si no tocamos ningún fichero (mayhave/musthave/maynothave/...) todas las llamadas de pkgsync deben quedar el sistema de paquetes como estaba, invariante.

Por esto y porque me apetecía me puse a mirar como reducir el tamaño del musthave para que esté lo mas puro y mínimo posible. Vamos a ver poco a poco lo que he encontrado:

Primero sacamos una lista ordenada con los paquetes instalados de forma no automática (de esta forma nos libramos de los paquetes tipo librería y otros dependientes). Es lo que se llama paquetes explícitos:
# paquetes_explicitos=$(aptitude search '~i !~M' -F '%p' --disable-columns | sort -u)
De estos paquetes los habrá que se han instalado desde /etc/pkgsync/musthave, también estarán los que se hayan instalado desde /etc/pkgsync/musthave.d por tareas independientes nuestras y, por último, los que se hayan instalado a mano y puesto en /etc/pkgsyc/mayhave para no ser desinstalados. Solo nos interesan los primeros (ya que lo que queremos es aligerar el etc/pkgsync/musthave inicial).
for i in $paquetes_explicitos
do
   echo -n "Paquete $i"
   if grep $i /etc/pkgsync/musthave > /dev/null
   then
        echo " ->instalado por musthave"
        echo $i >> musthave.new        
   else
      if grep $i /etc/pkgsync/mayhave > /dev/null
      then
          echo "  ->instalado por mayhave"
      else
          echo "  ->instalado por musthave.d"
      fi
   fi
done
sort -u -o musthave.new musthave.new
A la salida tenemos en musthave.new los paquetes explícitos que están en el /etc/pkgsync/musthave original. Con esto tenemos una selección bastante buena de paquetes, pero no es suficiente. Si miramos detenidamente en el musthave.new vemos que sigue habiendo redundancias. Hay que ir mas allá y limpiar todos los paquetes de musthave.new que dependan de otros paquetes que estén en ese mismo fichero.

Con el siguiente bucle que recorre musthave.new voy metiendo en musthave.deps todas las dependencias de cada paquete. Para ello uso el comando apt-rdepends (que saca las dependencias y sugerencias de un paquete), aplico un par de filtros para quedarme sólo con las dependencias y si aparece el propio paquete entre sus dependencias lo ignoro (por extraño que parezca, a veces sucede). Por último ordenamos la lista de paquetes dependientes y quitamos los repetidos usando "sort -u":
for i in $(cat musthave.new)
do
   #echo "Dependencias de $i"
   apt-rdepends $i |grep Depends: | awk ' { print $2 } ' | sed "/^${i}$/d" >> musthave.deps
done
sort -u -o musthave.deps musthave.deps
Una vez hecho esto, quitamos de musthave.new todos los paquetes que también estén en musthave.deps mediante "comm -23", borrando los paquetes redundantes y dejando el resultado en musthave.final:
comm -23 musthave.new  musthave.deps  > musthave.final
Todo el script junto queda:
# cat musthave-minimo
#!/bin/bash

cp /dev/null musthave.new
cp /dev/null musthave.deps

paquetes_explicitos=$(aptitude search '~i !~M' -F '%p' --disable-columns | sort -u)
for i in $paquetes_explicitos
do
   echo -n "Paquete $i"
   if grep $i /etc/pkgsync/musthave > /dev/null
   then
        echo " ->instalado por musthave"
        echo $i >> musthave.new        
   else
      if grep $i /etc/pkgsync/mayhave > /dev/null
      then
          echo "  ->instalado por mayhave"
      else
          echo "  ->instalado por musthave.d"
      fi
   fi
done
sort -u -o musthave.new musthave.new

for i in $(cat musthave.new)
do
   #echo "Dependencias de $i"
   apt-rdepends $i |grep Depends: | awk ' { print $2 } ' | sed "/^${i}$/d" >> musthave.deps
done
sort -u -o musthave.deps musthave.deps

comm -23 musthave.new  musthave.deps  > musthave.final

exit 0
Una vez obtenido el musthave.final podemos compararlo con el musthave inicial y con mushave.new usando "wc -l". Veremos que el número de paquetes se ha reducido considerablemente. Esto es un ejemplo sobre un Ubuntu con la imagen LTSP:
# wc -l musthave*
 1837 musthave.deps
  174 musthave.final
 1285 musthave
  501 musthave.new
Como vemos pasamos de 1285 paquetes en el musthave generado con el script generar_musthave.sh a 174 en musthave.final, tras realizar las sucesivas purgas de paquetes dependientes.

Antes de aplicar musthave.final de forma general es conveniente hacer una copia de seguridad /etc/pkgsync/musthave, sobrescribir musthave.final sobre él y ejecutar pkgsync en modo simulación (no hace nada de verdad, simplemente te dice que ficheros quitaría y que ficheros instalaría), para calibrar si es inofensivo.
# cp /etc/pkgsync/musthave /etc/pkgsync/musthave.backup
# cp musthave.final /etc/pkgsync/musthave
# pkgsync -s
Normalmente nos dirá que quiere quitar algunos paquete secundarios raros que no parecen tener mayor importancia. Aquí entra en juego la pericia de cada cual a la hora de evaluar el riesgo de dejarle que quite esos paquetes, pero por mi experiencia y tras investigar el contenido los paquetes propuestos puedo decir que todas las veces me han salido paquetes anodinos, que ni sé que hacen ahí ni los voy a echar de menos cuando se vayan.

Una vez seguros de lo que queremos hacer cogemos aire, nos encomendamos a Odín y ejecutamos "pkgsync" sin mirar atrás. En el peor de los casos, siempre podemos volver a cargar la imagen con clonezilla ;-).

viernes, 13 de octubre de 2017

Cómo montar el pkgsync con control compartido usando puppet.

Cada vez que preparo un tipo nuevo de equipo y tengo que meterle pkgsync para mantener la coherencia de los paquetes de forma centralizada me toca re-estudiarme como está montado. Esta vez voy a apuntarlo aquí a modo de guía.

1. Cómo funciona el pkgsync en máquinas clientes.

Gracias a Esteban tenemos un pkgsync cada vez mas potente y versátil. En los repositorios tenemos la version 1.39:
# apt-cache policy pkgsync
pkgsync:
  Instalados: 1.39
  Candidato:  1.39
  Tabla de versión:
 *** 1.39 0
        500 http://linex.educarex.es/ubuntu/trusty/ trusty/linex amd64 Packages
        500 http://servidor/html/linex2/ubuntu/trusty/ trusty/linex amd64 Packages
        100 /var/lib/dpkg/status
        ....
Hace años que partimos de un pkgsync primitivo basado en solo 3 ficheros (/etc/pkgsync/mayhave, maynothave y musthave) que nos daba muchos problemas de competencia entre lo que nos imponían desde Mérida y lo que queríamos tener cada uno en nuestro centro (¿quién manda si hay un único mayhave?).

Gracias a las mejoras de Esteban hemos evolucionado a un sistema de soberanía compartida, donde hay una parte del mayhave (y compañía) controlada por Mérida y otra controlada por nosotros.

Por ello ahora tenemos tres orígenes de ficheros que se fusionan al ejecutar pkgsync en un único /etc/pkgsync/mayhave.all, el cual toma la función que tenía el antiguo y solitario /etc/pkgsync/mayhave. Estos 3 orígenes son:

  • /etc/pkgsync/mayhave: es controlado por Mérida mediante diversas tareas puestas en nuestro servidor puppet. Si lo modificamos será sobreescrito desde el Puppet Master of Our Universe de la Consejería.
  • /etc/pkgsync/mayhave.ies: es controlado por nosotros en nuestro servidor puppet. Para ello debemos tener una tarea hecha que lo controle.
  • /etc/pkgsync/mayhave.d/*: es como mayhave.ies, pero en este directorio podemos poner varios ficheros que se fusionarán con el resto. Es muy cómodo para hacer grupos de paquetes personalizados según el tipo de equipo (por ejemplo /etc/pkgsync/mayhave.d/mayhave.multimedia y poner dentro una lista de paquetes relacionados con multimedia). También debemos tener una tarea hecha por nosotros que controle estos ficheros.

Of course, esto relatado del mayhave se aplica de forma idéntica para musthave y maynothave.

2. Cómo se distribuyen los ficheros de pkgsync oficiales desde el servidor puppet.

Para evitar tener que ir equipo a equipo, los ficheros vistos en el apartado anterior tienen que controlarse desde el servidor puppet del centro. Hemos convenido en que los ficheros musthave/mayhave/maynothave son controlados desde Mérida por su puppet, mientras que los musthave.ies/mayhave.ies/maynothave.ies y los musthave.d/mayhave.d/maynothave.d están controlados por nosotros por nuestro puppet.

Los controlados por el servidor de la Consejería en Mérida se guardan en la ruta /etc/puppet/files/xubuntu del servidor principal del centro. Si vamos allí, veremos al menos estos ficheros:
mayhave.siatic.i386
musthave.hp360
musthave.infolab
musthave.siatic
musthave.siatic.i386
Para saber que se hace con ellos, busco en las tareas puppet:
root@servidor:/etc/puppet# grep -ir puppetinstituto/files/xubuntu/mayhave * 
manifests/classes/xubuntu/siatic.pp:        source => "puppet://puppetinstituto/files/xubuntu/mayhave.siatic.i386",

root@servidor:/etc/puppet# grep -ir puppetinstituto/files/xubuntu/musthave * 
manifests/classes/xubuntu/notebookHP.pp:        source => "puppet://puppetinstituto/files/xubuntu/musthave.hp360",
manifests/classes/xubuntu/siatic.pp:        source => "puppet://puppetinstituto/files/xubuntu/musthave.siatic.i386",
manifests/classes/xubuntu/infolab.pp:#        source => "puppet://puppetinstituto/files/xubuntu/musthave.infolab",
De lo visto podemos decir que:
  • musthave.hp360: es el musthave que se copia para tablets-portátiles hp360.
  • musthave.infolab: es el musthave para infolabs. Lo cierto es que a día de hoy no se usa, se comentó su inserción por un problema con las tarjetas nvidia y así permanece.
  • mayhave.siatic.i386, musthave.siatic.i386: son el mayhave/musthave que se copia a las Siatic con sistema de 64bits.
  • musthave.siatic: ya no se usa. En su día era el oficial de los siatics, pero había paquetes de xserver que daban problemas y se dejó de insertar.

Como vemos, no hay musthaves/mayhaves/maynothave oficiales para equipos ltsp, workstations, portátiles distintos a los hp360 ni para siatics de 64bits. Se entiende que todos estos equipos tienen un /etc/pkgsync/musthave que viene de serie en su respectiva imagen y de momento la Sección no contempla modificarlos por puppet ya que no hay tarea oficial que se ocupe de actualizarlos.

En el caso de las siatic 64bits ni siquiera traen un musthave inicialmente (lo cual hace que pkgsync no funcione ya que requiere de su existencia para trabajar) y si lo queremos debemos generarlo con el script que aparece en la imagen llamado /root/Primer_arranque/generar_musthave.sh (el cual también es una creación de Esteban).

Por lo general tampoco hay mayhaves y maynothave oficiales para ningún equipo, ya que de momento no ha surgido la necesidad de tenerlos de forma general en todos los centros.

3. Cómo distribuyo los ficheros de pkgsync particulares desde el servidor puppet.

Hasta aquí hemos visto la infraestructura común, ahora veremos como controlar por nuestra parte los mayhave/musthave/maynothave que son de nuestra competencia. Nada de esto lo tenemos por defecto en el servidor del centro y por tanto nos toca montar por nuestra cuenta el módulo puppet y los ficheros que distribuye.

En primer lugar aclarar los musthave.ies/mayhave.ies/maynothave.ies no los uso. Tuvieron su momento cuando teníamos Debian Wheezy pero ahora preferimos la solución mas elegante basada en los ficheros fragmentados a través de musthave.d/mayhave.d/maynothave.d. Si lo montamos bien, podremos tener un musthave general, que se aplica a todos los equipos, y un musthave por cada tipo-use2 de equipo.

En mi caso, en /etc/puppet/files/xubuntu tengo esta estructura de directorios creada:
├── mayhave.d
│   ├── mayhave.infolab
│   ├── mayhave.infolab.profesor
│   ├── mayhave.ltsp.profesor
│   ├── mayhave.notebookHP
│   ├── mayhave.portatil-alumno
│   ├── mayhave.siatic
│   ├── mayhave.workstation
│   ├── mayhave.workstation-i386
│   └── mayhave.xubuntu
├── maynothave.d
│   ├── maynothave.infolab
│   ├── maynothave.infolab.profesor
│   ├── maynothave.ltsp.profesor
│   ├── maynothave.notebookHP
│   ├── maynothave.portatil-alumno
│   ├── maynothave.siatic
│   ├── maynothave.workstation
│   ├── maynothave.workstation-i386
│   └── maynothave.xubuntu
└── musthave.d
    ├── musthave.infolab
    ├── musthave.infolab.profesor
    ├── musthave.ltsp.profesor
    ├── musthave.notebookHP
    ├── musthave.portatil-alumno
    ├── musthave.siatic
    ├── musthave.workstation
    ├── musthave.workstation-i386
    └── musthave.xubuntu
Los ficheros .xubuntu se copian a todo los equipos con ubuntu y los otros según los facter tipo (siatic, infolab, ltsp, workstation, etc) y use2 (profesor/alumno). Para ello uso la siguiente clase puppet basada en la original que compartieron mis compañeros del IES Téllez hace ya tiempo:
# cat /etc/puppet/modules/xubuntu_pkgsync/manifests/init.pp 

#Clase para control de los ficheros básicos para pkgsync de siatic, infolab y notebookHP
import "/etc/puppet/defines/*.pp"

class  xubuntu_pkgsync {

        case $use2 {
                "profesor":  { $esprofe=".profesor" }
                default: { $esprofe = "" }
        }

        #====================================================================================================================
        #Ficheros dependientes del tipo: Se pone un mayhave, musthave, maynothave personalizado en función del tipo y use2 (si es profesor) 

        case $tipo {

                "siatic", "infolab", "notebookHP", "ltsp", "workstation","portatil-alumno", "workstation-i386": {  
                        file { "mayhave.instituto":
                         path => "/etc/pkgsync/mayhave.d/mayhave.$tipo",
                         owner => root, group => root, mode => 644, 
                         source => "puppet:///files/xubuntu/mayhave.d/mayhave.${tipo}${esprofe}",
                         ensure => file,
                        }

                        file { "maynothave.instituto": 
                         path => "/etc/pkgsync/maynothave.d/maynothave.$tipo",
                         owner => root, group => root, mode => 644,
                         source => "puppet:///files/xubuntu/maynothave.d/maynothave.${tipo}${esprofe}",
                         ensure => file,
                        }

                        file { "musthave.instituto": 
                         path => "/etc/pkgsync/musthave.d/musthave.$tipo",
                         owner => root, group => root, mode => 644,
                         source => "puppet:///files/xubuntu/musthave.d/musthave.${tipo}${esprofe}",
                         ensure => file,
                        }
                }
                default: { }

        }

        #====================================================================================================================
        #Musthaves/maynothave/mayhave globales para todos los xubuntus controlados por mi

        # musthave global para todos los xubuntu
        file { "musthave.xubuntu":
                path => "/etc/pkgsync/musthave.d/musthave.xubuntu",
                owner => root, group => root, mode => 644,
                source => "puppet:///files/xubuntu/musthave.d/musthave.xubuntu",
                ensure => file,
        }

        # mayhave global para todos los xubuntu
                file { "mayhave.xubuntu":
                path => "/etc/pkgsync/mayhave.d/mayhave.xubuntu",
                owner => root, group => root, mode => 644,
                source => "puppet:///files/xubuntu/mayhave.d/mayhave.xubuntu",
                ensure => file,
        }

        # maynothave global para todos los xubuntu
        file { "maynothave.xubuntu":
                path => "/etc/pkgsync/maynothave.d/maynothave.xubuntu",
                owner => root, group => root, mode => 644,
                source => "puppet:///files/xubuntu/maynothave.d/maynothave.xubuntu",
                ensure => file,
        } 

        #====================================================================================================================
        #Ficheros mayhave/maynothave principales vacios por defecto si no existen.
        #El musthave debe ser creado por el administrador a mano. No se crea por defecto por su peligrosidad.

        # Por si no existen los ficheros, los creamos inicialmente a vacíos
        file { "mayhave.inicial": 
                path => "/etc/pkgsync/mayhave",
                owner => root, group => root, mode => 644,
                ensure => file,
        }

        file { "maynothave.inicial": 
                path => "/etc/pkgsync/maynothave",
                owner => root, group => root, mode => 644,
                ensure => file,
        }

        #El musthave no se crea por precaucion. Si existe el pkgsync se ejecutaría y puede destrozar el sistema de paquetes.
        #   cada /etc/pkgsync/musthave debe ser creado a mano por el administrador. Si no existe, pkgsync se aborta.

}
Y así es como lo tengo montado, utilizando los facter tipo y use2 para colocar los ficheros en las máquinas correspondientes. Los compañeros de Mérida tienen sus ficheros y yo tengo los míos.

Si quiero aplicar un paquete a todos los Ubuntu lo meto en el fichero *.xubuntu correspondiente. Si quiero aplicarlo a un tipo concreto (por ejemplo en los workstation de 64bits), lo meto en el fichero *.workstation correspondiente. En la siguiente ejecución de puppet en el cliente se copiarán los ficheros desde servidor:/etc/puppet/files/xubuntu y en la posterior ejecución de pkgsync se sincronizarán los paquetes de acuerdo con lo ordenado.

Como se ve, es una solución elegante que permite que trabajar colaborativamente sin machacarnos entre nosotros. Parlem.