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

domingo, 18 de marzo de 2018

Ocultando el código de un script bash

A veces necesitamos un script bash que puedan ejecutar los usuarios (por ejemplo, mediante un acceso directo en un fichero .desktop), pero dentro de él hay contraseñas o algún fragmento código que no nos interesa hacer público.

Por poner un ejemplo práctico, imaginemos un script que levanta/apaga a voluntad la red wifi de 5ghz de nuestros puntos de acceso DLink, que en mi caso mantengo apagada por defecto.

El script es sencillo:
# cat wifi5ghz.sh
#!/bin/bash
case "$1" in
  "on")   comando="ifconfig ba0 up"
          ;;
  "off")  comando="ifconfig ba0 down"
          ;;
  *)      echo "Error en parámetros"
          exit 1
          ;;
esac
sshpass -p "tuyM59b" ssh -o StrictHostKeyChecking=no root@192.168.0.1 "$comando"
exit 0
Usamos sshpass para entrar por ssh y ejecutar el comandod de apagado/encendido sin que se pida contraseña al usuario. El problema que tenemos es que si distribuimos el script tal que así, cualquier usuario puede ver la contraseña de administración del router DLink (la que he puesto es figurada) y eso no es tolerable.

Entre las diversas soluciones a este problema una consiste en hacer el script ilegible ofuscando su contenido. De las herramientas y trucos que hay para eso el mas potente que he encontrado es usando la aplicación "shc", que compila un script generando un fichero ejecutable ilegible. Nosotros nos quedaremos con el código fuente del script y a los usuarios les distribuiremos el ejecutable. Es raro esto de compilar scripts, pero para la situación descrita viene de perillas.

Una vez instalado compilaremos el script haciendo:
# shc -r -f wifi5ghz.sh -o wifi5ghz
Esto nos creará un fichero ejecutable wifi5ghz que hará lo mismo que wifi5ghz.sh sin que sea visible su código (si el usuario hace un cat sobre él verá un galimatías ASCII). El parámetro "-r" es importante porque permite que ese ejecutable pueda ejecutarse en otras máquinas distintas a aquella dónde se compila el script (si no se pone el ejecutable deja de funcionar a los pocos días).

Como apunte adicional, si consultamos los parámetros de shc veremos que tiene cosas bastante interesantes, como por ejemplo la posibilidad de hacer que el ejecutable caduque en una fecha predeterminada.

Hasta pronto.

domingo, 4 de marzo de 2018

Ejecutar comandos de consola de forma remota en Windows.

Como buen fanático del terminal, siempre prefiero hacer cosas de administración de sistemas tecleando antes que con ratón. En Unix esto es lo más habitual, pero en Windows no lo ponen tan fácil.

En cualquier caso siempre viene bien tener la opción de conectar con una consola remota de Windows y escribir los comandos en ella, sin necesidad de desplazarnos físicamente a la máquina ni de iniciar una sesión remota completa con rdesktop. En principio con un servidor OpenSSH en el equipo destino es suficiente. Un servidor ssh es un software que en Linux viene de serie mientras que en Windows, entre varias opciones, podemos poner una versión GPL liberada por Microsoft.

Pero no, vamos a ver una forma alternativa de ejecutar comandos de consola de forma remota en Windows que no necesita instalar nada especial en el PC destino. Contemplaremos 2 métodos: lanzar la ejecución de los comandos desde un equipo con Windows y hacerlo desde un equipo con Linux.

1. Consideraciones previas.

Para hacer que nuestros Windows estén dispuestos a aceptar órdenes remotas hay que prepararlos antes tocando varias configuraciones:

1.1. Abrir el puerto 445.

Para comunicarnos y dar órdenes al Windows remoto necesitamos comunicar con él por su puerto 445, denominado "microsoft-ds", que sirve para un montón de cosas incluida la entrada de virus ;-).

Como este puerto está cerrado normalmente por el firewall de Windows debemos primeramente abrirlo para que acepte conexiones entrantes. Podemos hacerlo con el confuso interface gráfico de configuración del firewall y tardar varios minutos:



o bien abrir una consola de comandos en modo administrador y teclear:
netsh advfirewall firewall add rule dir=in action=allow protocol=TCP localport=445 name="Allow_TCP-445" 
Seguramente después haya que reiniciar el servicio de firewall para que se aplique de forma efectiva.

Supongo que con este ejemplo de método gráfico versus método de terminal queda claro por qué muchas veces es preferible usar la línea de comandos.

1.2. Modificar el registro para permitir el acceso a los "administrative shares"

Si el equipo remoto trabaja con Windows 10 hay que añadir una entrada al registro para permitir las conexiones entrantes que ejecutarán comandos, ya que por defecto UAC impide que usuarios administradores se conecten a los "administrative shares". De nuevo lo haremos desde consola:
reg add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
Si no creamos esta entrada de registro obtendremos errores de "Acceso denegado" al intentar ejecutar comandos remotos.

1.3. Solo para Linux: activar SMB v1.

Esto solo debe hacerse si el equipo desde el que lanzamos el comando es un Linux.

Debido a la aparición del ransomware WannaCry, que aprovechaba una vulnerabilidad en SMB llamada EternalBlue (con la que seguramente los servicios de seguridad de USA nos han estado espiando a todos durante años) hubo que improvisar soluciones transitorias . Una de ellas, rápida y sucia, era deshabilitar SMB1 en Windows y así impedir la entrada de WannaCry.

Como efecto colateral, el programa que usamos en Linux para mandar comandos al Windows deja de funcionar, ya que necesita que SMB1 esté activo. Por tanto, dependiendo que tengamos o no activo SMB1 en nuestro Windows podrá funcionar o no la ejecución remota. A día de hoy EternalBlue ya ha sido solucionado con los parches de Microsoft, por lo que SMB1 puede estar de nuevo activo.

Para ver su estado abrimos una consola de PowerShell y tecleamos "Get-SmbServerConfiguration"
PS C:\> Get-SmbServerConfiguration

AnnounceComment                 : 
AnnounceServer                  : False
AsynchronousCredits             : 64
AuditSmb1Access                 : False
AutoDisconnectTimeout           : 15
AutoShareServer                 : True
AutoShareWorkstation            : True
CachedOpenLimit                 : 10
DurableHandleV2TimeoutInSeconds : 180
EnableAuthenticateUserSharing   : False
EnableDownlevelTimewarp         : False
EnableForcedLogoff              : True
EnableLeasing                   : True
EnableMultiChannel              : True
EnableOplocks                   : True
EnableSecuritySignature         : False
EnableSMB1Protocol              : False       
EnableSMB2Protocol              : True
...
...
Ahí vemos EnableSMB1Protocol a False. También podemos verlo directamente con:
PS C:\> Get-WindowsOptionalFeature –Online –FeatureName SMB1Protocol
...
...
¿Cómo lo activamos? Con:
PS C:\> Enable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol
Nos pedirá reiniciar para que sea efectivo el cambio. Este Windows es maravilloso: activas un protocolo y tienes que reiniciar la máquina completa, ahí queda eso.

Como curiosidad aquí tenemos recopilados todos los comandos para ver/cambiar el estado de los servicios SMBx.

2. Desde un equipo con Windows.

Bueno, ya tenemos el Windows remoto receptivo, vamos ahora a ver como darle órdenes. Necesitamos instalar las pstools en el PC desde donde daremos órdenes. Ojo: esto se instala en el PC desde donde se ejecuta el comando, el PC destinatario no necesita que instalemos nada, el único requisito para él es lo indicado en el apartado 1.

No viene mal echar un vistazo a todas las utilidades, son una maravilla que conviene poner de serie en todos nuestros Windows. La que usaremos nosotros es psexec. Veamos como ejecutar un comando remoto desde el Windows donde tenemos instalado psexec contra otro Windows:
psexec \\a20-o14 "ipconfig"
Esto anterior se ejecutaría usando las credenciales del usuario actual en el PC remoto. Si queremos dar otras credenciales especificamos el usuario (que debe ser un administrador) y contraseña:
psexec -u administrador -p password \\a20-o14 "ipconfig"

PsExec v2.2 - Execute processes remotely
Copyright (C) 2001-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

Starting PSEXESVC service on a20-o14...........
Configuración IP de Windows

Adaptador de Ethernet Ethernet:

Sufijo DNS específico para la conexión. . : vguadalupe
Vínculo: dirección IPv6 local. . . : fe80::f938:6376:3936:6a45%3
Dirección IPv4. . . . . . . . . . . . . . : 172.24.XXX.12
Máscara de subred . . . . . . . . . . . . : 255.255.255.0
Puerta de enlace predeterminada . . . . . : 172.24.XXX.2

Adaptador de túnel isatap.vguadalupe:

Estado de los medios. . . . . . . . . . . : medios desconectados
Sufijo DNS específico para la conexión. . : vguadalupe

Como se ve conectamos con el equipo a20-o14 y ejecutamos el comando "ipconfig" sobre él, dándonos la dirección IP y configuración de red el equipo. Con:
psexec \\a20-o14 "cmd"

PsExec v2.2 - Execute processes remotely
Copyright (C) 2001-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

Starting PSEXESVC service on a20-o14...........

Microsoft Windows [Versión 6.3.9600]
(c) 2013 Microsoft Corporation. Todos los derechos reservados.

C:\Windows\system32>

Se nos abre una consola de comandos remota, que ejecutará en la otra máquina lo que tecleemos.

Si alguna de las dos órdenes anteriores produce errores del tipo:
Couldn't access a20-o14:
No se ha encontrado la ruta de acceso de la red.
Make sure that the default admin$ share is enabled on a20-o14.
En mi caso era síntoma de que el puerto 445 estaba cerrado. Si ese es tu caso, abrelo como hemos comentado en la parte 1 y ya debería funcionar. Si además dice:
PsExec v2.2 - Execute processes remotely
Copyright (C) 2001-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
Couldn't access a20-o14:
Acceso denegado.
Entonces tendremos que añadir la entrada de registro descrita en la parte 1.2.

Mas cosas interesantes relacionadas con psexec:

  • Como ejecutar en varios PC a la vez el mismo script almacenado en un servidor:
    psexec \\computername1,computernamfe2,computernameN -d -u usuario -p password \\servidor\scripts\script.bat
    
    Esto es parecido al comando dsh de Linux.
  • Con el modificador -c podemos enviar desde el ordenador local un fichero .exe para ejecutarlo remotamente:
    psexec \\computername1,computernamfe2,computernameN -d -c programa.exe -u usuario -p password programa.exe
    
  • Para ver cuantas sesiones hay abiertas en una máquina remota ejecutamos el comando de Windows "query session":
    psexec \\a20-o19 query session
    
    NOMBRE DE SESIÓN  NOMBRE DE USUARIO     ID  ESTADO TIPO   DISPOSITIVO
    >services                                0  Desc
    console           GM_SEGUNDO             1  Activo
    rdp-tcp#1         Administrado           2  Activo
    rdp-tcp                               5536  Escuchar
    
    Vemos que en consola hay una sesión abierta con el usuario GM_SEGUNDO y en remote desktop hay otra abierta con el usuario Administrador (¿cómo consigo sesiones de escritorio de usuarios concurrentes sobre un mismo Windows?: instalando rdpwrapper).
  • Psexec está concebido para ejecutar comandos de consola, con entrada/salida en texto. Cualquier aplicación gráfica que lancemos aparecerá en el escritorio remoto. Por ejemplo:
    psexec \\a20-o19 -i 1 -d notepad
    
    Hará que al usuario GM_SEGUNDO del ejemplo anterior (el de la sesión con ID 1, de ahí el parámetro "-i 1". Este es el valor por defecto) se le abra el bloc de notas en la pantalla ante su total estupefacción. Esta es la teoría, pero en las pruebas prácticas que he hecho se abre una ventana con el contenido en negro y que no permite interactuar. Debe ser algún bug o feature de Windows 10.

    Si queremos lanzar aplicaciones gráficas en un Windows remoto y que aparezcan en nuestro escritorio local (sería una conexión Remota tipo VNC o Remote Desktop pero a nivel de aplicación en lugar de a nivel de escritorio completo) habría que usar SeamlessRDP, pero ese tema escapa al contenido de este artículo.

A modo de resumen: psexec es adecuado para ejecutar comandos de terminal de forma remota sin tener que iniciar sesión en los escritorios Windows. Como truco: cuando queremos ejecutar varios comandos la mejor solución es ponerlos dentro de un script .bat en una carpeta compartida por Samba accesible por todos los Windows y lanzar dicho script desde psexec.

3. Desde un equipo con Linux.

Bueno, ¿y que pasa si el equipo cliente es un Linux y desde él queremos ejecutar los comandos remotamente en nuestros Windows? ¿Hay algun psexec para Linux?

Pues si hay algo que se le parece: winexe, que sigue la idea de usar el puerto 445 para mandar ordenes a los Windows de forma remota. Es un proyecto algo abandonado y del cual no hay paquetes oficiales en .deb ni en .rpm (para Manjaro/Arch por supuesto que sí, pero es que ahí jugamos en otra liga). Además es fácil encontrar la versión 1.0, pero la que ahora funciona con Windows 10 es la 1.1.

Al final lo he encontrado aquí, con versiones .deb para Debian y Ubuntu. En mi caso he bajado el paquete winexe-static-1.1-0-jessie.deb para ejecutarlo desde mi servidor montado Debian Jessie.

El paquete se llama "static" porque tiene las librerías de Samba metidas dentro del ejecutable durante la compilación. La causa es que al cambiar la versión de Samba esto tiende a fallar, así que han optado por llevarlas al estilo años 70, embebidas en el ejecutable para evitar errores.

# dpkg -i winexe-static-1.1-0-jessie.deb
# winexe-static
winexe version 1.1
This program may be freely redistributed under the terms of the GNU GPLv3
Usage: winexe-static [OPTION]... //HOST COMMAND
Options:
  -h, --help                                  Display help message
  -V, --version                               Display version number
  -U, --user=[DOMAIN/]USERNAME[%PASSWORD]     Set the network username
  -A, --authentication-file=FILE              Get the credentials from a file
  -N, --no-pass                               Do not ask for a password
  -k, --kerberos=STRING                       Use Kerberos, -k [yes|no]
  -d, --debuglevel=DEBUGLEVEL                 Set debug level
  --uninstall                                 Uninstall winexe service after
                                              remote execution
  --reinstall                                 Reinstall winexe service before
                                              remote execution
  --system                                    Use SYSTEM account
  --profile                                   Load user profile
  --convert                                   Try to convert characters
                                              between local and remote
                                              code-pages
  --runas=[DOMAIN\]USERNAME%PASSWORD          Run as the given user (BEWARE:
                                              this password is sent in
                                              cleartext over the network!)
  --runas-file=FILE                           Run as user options defined in a
                                              file
  --interactive=0|1                           Desktop interaction: 0 -
                                              disallow, 1 - allow. If allow,
                                              also use the --system switch
                                              (Windows requirement). Vista
                                              does not support this option.
  --ostype=0|1|2                              OS type: 0 - 32-bit, 1 - 64-bit,
                                              2 - winexe will decide.
                                              Determines which version (32-bit
                                              or 64-bit) of service will be
                                              installed.
Como se ve los parámetros son muy distintos a los de psexec, ya que es una herramienta independiente. En este enlace se ve un buen repaso de todos ellos.

Veamos como ejecutar un comando:
# winexe-static --convert -U administrador%password //a20-o14 "ipconfig"
Error: error Unknown argument (get codepage)
CTRL: Probably old version of service, reinstalling.

Configuración IP de Windows

Adaptador de Ethernet Ethernet:

Sufijo DNS específico para la conexión. . : vguadalupe
Vínculo: dirección IPv6 local. . . : fe80::f938:6376:3936:6a45%3
Dirección IPv4. . . . . . . . . . . . . . : 172.19.XXX.100
Máscara de subred . . . . . . . . . . . . : 255.255.255.0
Puerta de enlace predeterminada . . . . . : 172.19.XXX.2

Adaptador de túnel isatap.vguadalupe:

Estado de los medios. . . . . . . . . . . : medios desconectados
Sufijo DNS específico para la conexión. . : vguadalupe
O abrir un interpréte de comandos remoto y escribir las órdenes allí:
# winexe-static  --convert -U administrador%password //a20-o04 "cmd"
Microsoft Windows [Versión 6.3.9600]
(c) 2013 Microsoft Corporation. Todos los derechos reservados.

C:\Windows\system32>cd \users
cd \users

C:\Users>dir
dir
El volumen de la unidad C no tiene etiqueta.
El número de serie del volumen es: B62C-6CE1

Directorio de C:\Users

16/06/2016  08:09    DIR          .
16/06/2016  08:09    DIR          ..
26/01/2017  12:45    DIR          Administrador
22/08/2013  16:36    DIR          Public
20/07/2017  05:43    DIR          UpdatusUser
28/07/2017  08:04    DIR          usuario
0 archivos              0 bytes
6 dirs  33.118.711.808 bytes libres

C:\Users>exit
exit
# 
Si en algún momento da este error:
# winexe-static -U administrador%password //a20-o04 "cmd"
ERROR: Failed to open connection - NT_STATUS_CONNECTION_RESET
La causa mas probable es que esté desactivado SMB1 en el PC remoto, lo cual se soluciona como vimos en el apartado 1.

Finalizamos con una recopilación de errores:
  • ERROR: Failed to open connection - NT_STATUS_LOGON_FAILURE : credenciales incorrectas.
  • ERROR: CreateService failed. NT_STATUS_ACCESS_DENIED : puerto 445 cerrado o registro no modificado según apartado 1.1 y 1.2.
  • ERROR: Failed to open connection - NT_STATUS_CONNECTION_RESET : servicio SMB1 deshabilitado. Habilitar según apartado 1.3 .

Y con esto ya tenemos expuestas todas las posibilidades para interactuar con nuestros Windows desde terminal.


Me despido con una imagen impactante que no tiene nada que ver con el blog, pero que está aquí al lado y no puedo dejar pasar:


Esto tiene 66.700 años según las últimas dataciones y es la prueba mas antigua de arte realizada por un homo sapiens de la especie vecina, la neanderthalensis. En esa época los homo sapiens sapiens estábamos empezando a salir de África por Oriente Próximo y andábamos ocupados en otras cosas. Y aquí, al lado de la Ribera del Marco ya había una especie que desarrollaba pensamiento simbólico en este planeta. Impresionante.

Es nuestro pequeño Gobekli Tepe, espero que sepamos cuidarlo.


jueves, 22 de febrero de 2018

Montemos una IP-Webcam barata - Reloaded III

Este tema es inacabable. Después de todos los posts sobre la cámara IP a partir de una cámara usb y un router ADSL casero con OpenWRT/LEDE todavía me siguen saliendo cositas y ampliaciones cada pocos meses, así que no me queda otra opción que seguir revisitando el tema. Hagamos una retrospectiva:


Ahora el problema es que, recordemos, el guardado de las imágenes de captura se realizaba en una carpeta de un servidor remoto montada por sshfs. No es infrecuente que haya algún problema de red y esa conexión quede interrumpida. No siempre es restaurada esa conexión en tiempo y forma y a veces me encuentro con que llevamos varios días desechando imágenes ya que en el mermado espacio de almacenamiento del router ADSL no se puede guardar nada.

Para evitar esto he añadido código para que cuando se detecte que se ha perdido el montaje remoto por sshfs se envíe un correo especial de aviso, para que al menos pueda ponerle remedio cuanto antes. Marco en negrita la parte con el pequeño fragmento de código nuevo (el resto es como la ha ido quedando las últimas veces):
# cat /root/controlador.sh
#!/bin/ash

log() {
  test $montado -eq 1 && echo "$1" >> /mnt/snapshot/log.txt  
}

email() {
  test $email -eq 1 && echo -e "$1" | sendmail correo.aviso@gmail.com
}

monta_sshfs() {
  sshfs -o ssh_command="ssh -i /root/.ssh/id_rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" -o nonempty openwrt@192.168.0.25:/home/openwrt /mnt
}

envia_secuencia() {

   if [ -e /tmp/motion.txt ]
   then

      #Recopila todos los ficheros de /tmp/motion/txt y los envia en un correo

      ficheros=$(cat /tmp/motion.txt | tr '\n' ' ')
      rm /tmp/motion.txt

      log "Historia de capturas $ficheros"
      zip -9 /mnt/escena.zip $ficheros

      echo "Secuencia $1" > /mnt/mensaje.txt
      test $email -eq 1 && mutt -s "Evento camara" correo.aviso@gmail.com -a /mnt/escena.zip < /mnt/mensaje.txt

      rm /mnt/escena.zip
      #Si no hay acceso al almacenamiento remoto, borramos las capturas para no llenar el disco del router.

      test $montado -eq 0 && rm -rf $destino

   fi

}

#Parametro 1: start, picture, end
#Parametro 2: fecha
#Parametro 3: nombre fichero (opcional)

#Obtenemos fecha y hora por si queremos poner algun filtro sobre ella y solo avisar en determinados
#momentos

time=$(date +%s)
anio=$(date +%Y @$time)
mes=$(date +%m @$time)
dia=$(date +%d @$time)
hora=$(date +%H @$time)
minuto=$(date +%M @$time)
diasemana=$(date +%u @$time) #El 1 es lunes
destino="/mnt/snapshot/$anio-$mes-$dia"
montado=1
email=0

if [ $diasemana -ge 6 -o $hora -le 7 -o $hora -ge 17 ]  # sabado/domingo o cualquier dia antes de las 8:00 o despues de las 17:00
then
   email=1
fi

#Si existe el fichero /mnt/testigo no se ha montado directorio remoto por sshfs,
#Intentamos montarlo y otra vez y luego preguntamos de nuevo.
#Si no se ha montado nada es que no se puede guardar nada de forma permanente ya que estamos escribiendo en la
#memoria interna del router y eso se llena rapido

test -e /mnt/testigo || monta_sshfs

if test -e /mnt/testigo
then
  #No está montado el directorio remoto, no hay donde guardar las imagenes
  montado=0
  #No hay almacenamiento por dias, no vamos a guardar tanto tiempo el fichero.
  destino="/mnt/snapshot"
  #Si no se ha avisado antes se avisa por correo
  if ! test -e /tmp/avisado
  then
     touch /tmp/avisado
     echo -e "Subject: Camara sin almacenamiento\r\n\r\nSe ha perdido la conexión remota con el directorio de almacenamiento de la cámara" | sendmail correo.aviso@gmail.com
  fi
else
  #Tenemos montado el directorio remoto, borramos el testigo de aviso.
  rm -f /tmp/avisado
fi

test -d $destino || mkdir -p $destino

case $1 in

  "start")
     log "Detectado inicio de movimiento $2" 
     email "Subject: Evento camara\r\n\r\nDetectado inicio de movimiento $2"
     #El fichero motion.txt tiene dos finalidades: 1) testigo para indicar que estamos detectando movimiento 
     #                                             2) guarda los nombres de los ficheros de captura de las fotos
     touch /tmp/motion.txt
     ;;
  "picture")
     fichero=$(basename $3)
     log "Guardando imagen $2 : $destino/$fichero" 

     #Si estamos en una ráfaga de movimiento detectado, guardamos el nombre de fichero con la foto.
     test -e /tmp/motion.txt && echo "$destino/$fichero" >> /tmp/motion.txt

     #Se guarda el fichero en el almacenamiento destino
     mv "$3" "$destino/$fichero"

     #Si no está montado el almacenamiento remoto y vemos que llevamos mas de 10 imagenes en
     #movimiento tenemos que borrarlas y enviarlas ya mismo, para no saturar el espacio.
     if [ $montado -eq 0 ]
     then
        #Si estamos en una ráfaga de movimiento detectado....
        if [ -e /tmp/motion.txt ]
        then
          lineas=$(wc -l /tmp/motion.txt | cut -d" " -f1)
          test $lineas -gt 10 && envia_secuencia $2
        else
          #Si no lo estamos, es una captura *snapshot* regular sin interés, la borramos.
          rm -f "$destino/$fichero"
        fi  
     fi
     ;;
  "end") 
     log "Detectado fin de movimiento $2"
     envia_secuencia $2
     ;;
   *) log "Evento $1"
esac


#Borra fichero "sent" creado por sendmail si existe, para liberar espacio
rm -rf /root/sent
rm -rf /sent
Para evitar enviar el mensaje en cada invocación del script se usa un fichero "avisado" en /tmp/ como bandera para verificar si se ha enviado o no ese mensaje. Ese fichero se borra cuando se detecta que se ha montado el directorio remoto o al reiniciar el equipo (todo el contenido de /tmp se borra en cada reinicio).

Bueno, pues seguro que vuelve la burra al trigo pronto y estamos otra vez aquí.



Hasta siempre Forges, me has hecho reírme de mí mismo muchas veces.

lunes, 19 de febrero de 2018

Ejecutar en los infolabs aplicaciones gráficas entrando con "ssh -X"

Esta mañana tenía que instalar Adobe Air y una aplicación .air remotamente en varios infolabs y como el instalador es "al estilo Windows" debía entrar por "ssh -X" y lanzar el ejecutable gráfico de instalación.

Me ha sorprendido bastante que fallase con el error de que no se podía abrir el display, como si no tuviera permisos para ejecutar aplicaciones X. La verdad es que nunca había tenido necesidad de ejecutar aplicaciones gráficas en remoto entrando por ssh en los infolab. He probado varias aplicaciones y en todas había problemas del tipo:
Failed to open display
o
Error: GDK_BACKEND does not match available displays
Lo primero ha sido mirar en /etc/ssh/sshd_config y ver que aparece:
X11Forwarding yes
Ante esto he quedado desconcertado un rato, hasta que he visto que ya lo solucionó mi compañero Manuel del IES Eugenio Hermoso. Hay que añadir a /etc/ssh/sshd_config la línea:
X11UseLocalHost no
Y reiniciar ssh en el infolab. ¡Gracias, Manolo!

¿Por qué esta línea no es necesaria en otros equipos como los Siatic y compañía? Pues no sabemos.

domingo, 18 de febrero de 2018

Windows: actualiza como puedas.

Vaya por delante que opino que usar Windows en un centro educativo es, salvo casos muy justificados, un disparate económico, ético y tecnológico. Pero por desgracia esa puerta quedó abierta en tiempos pasados y debemos lidiar con la situación como buenamente podamos.

1. Parte de guerra.

Una vez tienes instalados tus Windows "pelados" aparte de los problemas típicos de gestión de usuarios y administración te encuentras con la cuestión de sus actualizaciones, que tiene una idiosincrasia especial en un entorno como el nuestro. Repasemos:

Primero. Cualquier usuario, incluidos los defensores mas acérrimos, habrán comprobado que Windows tiene la irritante costumbre de actualizarse sin control y sin contemplaciones de forma extemporánea: al iniciar, al apagar o en medio de la operación de un PC. De una extraña manera, estas actualizaciones incluso no dudan en arrasar cuando les parece con el arranque dual haciendo desaparecer Linux del menú de arranque. Esto en mi opinión roza lo delictivo.

Segundo. Windows tiene un sistema de actualizaciones totalmente ilógico: si dejas un equipo sin actualizar 6 meses al lanzar el update no instala lo último, sino que pasa por casi toda la secuencia de actualizaciones que se han quedado atrás (pocas veces una actualización es descartada en favor de otra posterior), reiniciando el equipo varias veces. Eso, para la gente razonable acostumbrada a derivados de Debian y a las rolling relaease como mi querida Manjaro es sencillamente inconcebible. Me ha llegado a pasar que instalas un Windows 7 en una hora y luego te pasas 20 horas descargando actualizaciones por fases de descarga-instalación-reinicio hasta tenerlo al día.

Por otro lado, el proceso de actualización es tremendamente oscuro y nunca sabes que está pasando y si el sistema se ha quedado parado en alguna operación. Sabes cuando empieza y cuando acaba, pero no como avanza. Para los acostumbrados a la verbosidad de las actualizaciones en Linux este mutismo es incomprensible.

Tercero. Como ya avisamos en su momento a la gente que decide, para poner orden en este caos hace falta un sistema que nos permita administrar y dosificar el flujo de actualizaciones. Eso implica un Windows Server y un servicio WSUS por centro junto con el hardware para ello, lo cual representa un sensible gasto adicional en dinero y tiempo para administrarlo. Esto no se tuvo en cuenta por las mentes pensantes que decidieron adquirir licencias de Windows sin tino y nos dejaron el papelón a los de abajo.

También puedo hablar según mi experiencia: habiendo servido durante varios años en ICM, la antigua Agencia de Informática y Comunicaciones de la Comunidad de Madrid (muy conocida por colaborar con las tramas corruptas Púnica y Lezo; tranquilos: yo soy inocente, todo eso pasó mayormente mientras estaba de excedencia) donde todo se basaba en Windows. Incluso con toda la infraestructura de clientes y servidores Windows montada para gestionar las actualizaciones de forma controlada los problemas en los clientes eran constantes: parches que se enrocaban en una instalación iniciada e interrumpida infinitamente, equipos en que fallaba algo y se quedaban atrás sin actualizar, errores y pantallazos azules en arranques tras actualizaciones fallidas... y todo esto con Windows XP, en los que el que ritmo de actualizaciones era mucho mas reducido que en los Windows posteriores.

Cuarto. Para mas INRI, esto último no sirve nada tan pronto como comprendes la realidad de funcionamiento de los PC de los centros educativos. Esto no es una oficina al uso donde los PC se encienden de 8 a 15 o de 9 a 17. Un PC en un centro educativo se enciende y se apaga de forma aleatoria a lo largo del día según el uso en las materias y horarios de clase. Incluso podemos encontrarnos con PC que no se encienden durante muchos días y de repente luego se encienden una mañana entera con 3 reinicios al cambiar de clase. Evidentemente disparar unas actualizaciones que pueden tardar horas en un entorno tan aleatorio es un proceso abocado a la catástrofe: tanto por el consumo de ancho de banda que se incrementa de golpe al encender un grupo de PC de alumnos, así como por el ralentizamiento del PC asociado a la actualización y los problemas derivados de apagarlos cuando están a medias.

Cinco. Podríamos pensar que los PC de uso de los profesores (salas comunes, aulas, departamentos) si que están encendidos de forma continua. Falso: son ordenadores de uso compartido y una de las pequeñas luchas que tenemos los administradores informáticos es limitar la pulsión que tienen muchos usuarios de apagar el sistema operativo cuando se acaba su sesión, máxime teniendo en cuenta que en 5 minutos va a haber otro docente sentado en ese mismo sitio. Pues bien: no hay manera, cuando alguien quiere apagar, apaga.

Seis. ¿Puede empeorar la cosa? Si: las aulas de portátiles. Nada mas terrible que ir a guardar un portátil en un armario de carga porque acaba la clase y encontrarnos que nos dice que esperemos porque hay que instalar 13 actualizaciones. O encenderlo para empezar la clase y esperar 10 minutos a que se actualice. Pero eso no es todo: tenemos 20 portátiles conectados a un punto de acceso wifi normal y corriente pugnando por actualizar, descargando megas y megas cada uno e impidiendo el uso normal de todos los dispositivos conectados a la misma wifi. Como seguramente en una hora de clase no de tiempo a descargar todo en la próxima sesión volveremos a la casilla de salida.


2. Estrategias paliativas.

Ante este panorama no nos queda otra que lidiar como buenamente podamos con todo ello. Si quedamos las actualizaciones a su libre albedrío la tragedia en forma de sistemas dañados por el apagado en medio de una actualización o redes colapsadas está garantizada. Parafraseando el proverbio chino: simplemente tenemos que sentarnos a la orilla del río y esperar hasta que pasen los Windows muertos flotando corriente abajo. El problema es que al final esos Windows tenemos que resucitarlos nosotros.

En mi caso he optado por dos estrategias para evitar la debacle:

  1. Cuando es posible por la potencia del equipo cliente virtualizo los Windows y los dejo con las actualizaciones desactivadas. Tengo un master que actualizo de vez en cuando en un PC concreto y distribuyo luego el fichero .VDI (imagen del disco duro) a todos los demás.
  2. Si no es posible virtualizar, porque los PC andan justitos de procesador y memoria opto por dejar un arranque dual y: desactivar en la medida de lo posible las actualizaciones automáticas de Windows, usar un sistema basado en wsusoffline, como recomienda nuestro compañero Esteban, para actualizar contra un repositorio interno al IES y no contra Internet (con lo cual ahorramos tráfico de red) y cada cierto tiempo me toca perder varios días actualizando semi-manualmente los equipos.

Vamos a ver esto con detalle.

3. Parar, reanudar y limitar las actualizaciones.

No es plato de buen gusto parar las actualizaciones de un PC, se supone que incluyen mejoras y parches contra agujeros de seguridad como Wannacry, Meltdown o Spectre. Pero cuando la alternativa es tener el aula parada y los usuarios malhumorados porque todo el tiempo y ancho de banda se va en actualizar dices "¡pero que coño!" y paras todo.

La forma más efectiva que he encontrado de parar las actualizaciones automáticas es con estos comandos que se pueden meter en un script:
sc config wuauserv start= disabled
net stop wuauserv
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU  /v NoAutoUpdate /t REG_DWORD /d 1 /f
Con esto el Windows queda congelado en el tiempo salvo que Microsoft use algún truco artero para colarnos algo, tal como hace con esas instalaciones de Cortana u OneDrive que reaparecen después de haberlos eliminado una y otra vez. Si queremos volver a activar haremos los pasos contrarios:
sc config wuauserv start= auto
net start wuauserv
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU  /v NoAutoUpdate /t REG_DWORD /d 0 /f
Otra opción que puede resultar interesante es usar una característica que tiene Windows de definir una conexión inalámbrica de red como "metered" o de uso medido. Eso viene a significar que le avisamos de que esa conexión nos cuesta dinero cada megabyte y que por tanto, tenga ojito con lo que se descarga por ella. Pensemos por ejemplo en una conexión móvil 4G, un operador de wifi o el futuro Starlink de Elon Musk, que nos facturase por datos transferidos.

Para ello debemos establecer la conexión a la red y una vez hecha tecleamos:
netsh wlan show profiles
Esto nos mostrará el nombre de la conexiones wlan (redes wifi o 4G), por ejemplo algo así como "Airtel-WRTR301GN-8897_1". Una vez sabido el nombre, con:
netsh wlan show profile name="Airtel-WRTR301GN-8897_1"
Vemos los parámetros de la conexió. En ellos el parámetro "Cost" puede valer Unrestricted (no limitado) o Fixed (limitado). Por tanto, si queremos indicar que la conexión es metered hacemos:
netsh wlan set profileparameter name=”Airtel-WRTR301GN-8897_1″ cost=Fixed
Esto hará que Windows se lo piense antes de consumir ancho de banda en una actualización por esa conexión. Pero, ojo, esto no descarta todas las actualizaciones y las que se consideren críticas se realizarán. Aunque está concebido para conexiones inalámbricas, también podemos activarlo en conexiones cableadas trasteando el registro.

4. Encauzar las actualizaciones con WSUS Offline Update.

WSUS Offline Update es un sistema montado para realizar las actualizaciones de los Windows/Office usando la red local y minimizando el tráfico externo. Incluso permite actualizar Windows/Office sin conexión a Internet, descargando las actualizaciones a un pendrive y actualizando luego con él. Yo lo descubrí, como tantas otras cosas, gracias a mi compañero Esteban.

Todo rota en torno a 3 elementos:

  • Una aplicación/script que descarga las actualizaciones y parches disponibles para los sistemas seleccionados.
  • Un repositorio donde guardar lo descargado. Traduciendo: una carpeta local o de red donde guardar los archivos.
  • Una aplicación/script que examina el sistema Windows y actualiza con los parches pendientes.

Una visión detallada del sistema la podemos encontrar aquí.

En mi caso no he usado las aplicaciones gráficas para realizar las tareas, ya que como se deducirá me va mas la línea de comandos. Además he intentado que las dos primeras partes se realicen con Linux y un servidor samba. Solo la parte de aplicación de las actualizaciones es, evidentemente, sobre Windows. Para ello me he guiado por este manual.

En un servidor auxiliar creo una carpeta /datos/wsus, con permisos 777 para todo su contenido. Sobre esa carpeta descomprimo la última versión de wsusoffline, que a fecha de escribir este post es la 11.1.1.

Una vez descomprimido el .zip escribo el script que me hará la descarga de los parches y actualizaciones para los sistemas que le indique:
# cat /datos/wsus/update.sh
bash /datos/wsus/wsusoffline/sh/download-updates.bash w100 esn /includedotnet /includemsse /includewddefs /verify
bash /datos/wsus/wsusoffline/sh/download-updates.bash w63-x64  esn /includedotnet /includemsse /includewddefs /verify
bash /datos/wsus/wsusoffline/sh/download-updates.bash w100-x64 esn /includedotnet /includemsse /includewddefs /verify
# chmod +x datos/wsus/update.sh
Este script descarga lo que haya de Windows 10 (64 y 32 bits) y Windows 8 (64 bits). Mirar la documentación para ver todas las posibles opciones de descarga (Windows 7, Office, Visual C++, .NET, ...). Lanzamos la ejecución del script y nos vamos a hacer otras cosas, porque tarda un rato en descargar todo:
# /datos/wsus/update.sh
Cuando termina, vemos que ha hecho:
# du -sh /datos/wsus/wsusoffline/client
2,6G w100
4,8G w100-x64
1,9G w63-x64
# ls -1 /datos/wsus/wsusoffline/client/w100-x64/glb/
windows10.0-kb3172729-x64_bb12a14ec3891ec0a9e24edb529632263783d389.cab
windows10.0-kb3172729-x64_f4fc9775baa98c176f43e87c40088231a884122b.cab
windows10.0-kb3173427-x64_d95e56e499e2c281a1f59585221dc891253414c7.cab
windows10.0-kb3173427-x64_e3da65fe753d24a1759cdd029028cde743a62a23.msu
windows10.0-kb3173428-x64_52fa3686737353fae20ab55fa9c924bd90558a31.cab
windows10.0-kb4035632-x64_ea26f11d518e5e363fe9681b290a56a6afe15a81.msu
windows10.0-kb4049011-x64_a58e4c340939a7b23b86055a18abdc9c59308094.msu
windows10.0-kb4049065-x64_f92abbe03d011154d52cf13be7fb60e2c6feb35b.msu
windows10.0-kb4056887-x64_0fcf1ff448cea9474ea089a3d866beeb7212cec1.cab
windows10.0-kb4056887-x64_12a57a1104466ca878eb096fefa6c6babe520810.cab
windows10.0-kb4056887-x64_1e5c03724988979684c35a5493829e4abe9bdbfc.cab
windows10.0-kb4056887-x64_57d92c622280f34d0ee271a9c58fdce91c3c6808.cab
windows10.0-kb4056887-x64_8ad98c5fb14f2938536d794074a5b901b6eb0d72.cab
windows10.0-kb4056888-x64_5943823d032a25e40f8eee1613aa849d3963bd1d.cab
windows10.0-kb4056890-x64_2d0216b0e6b49f457e5111fbd7d766984af3f6cf.cab
windows10.0-kb4056891-x64_99b9dadbd6a71c8c9ca58b8e53285f8320dd32e3.cab
windows10.0-kb4056892-x64_39198b373a07390a3afdf2a64d19f24d59fca7a6.cab
windows10.0-kb4056893-x64_f025805bafd32c5f9d7ca37eb255a894dfb025d8.cab
windows10.0-kb4058702-x64_4ed8d0df4f3c190d3e423bf287d7e95a21a9124a.msu
Y ahí vemos todos los ficheros con las distintas actualizaciones y parches descargados esperando para ser aplicados según necesite el cliente Windows. Este script lo ejecutaremos de vez en cuando o al menos cuando se actualice wsusoffline.

Estos ficheros hay que compartirlos usando una carpeta samba de solo lectura accesible desde los Windows:
# cat /etc/samba/smb.conf
...
...
[wsus]
comment = WSUS Windows Updates
path = /datos/wsus
writable = no
browseable = yes
guest ok = yes
force directory mode = 0777
...
...
Reiniciamos samba y desde un Windows podemos abrir una ventana de comandos con credendiales de usuario administrador y teclear:
net use z: \\ip-servidor\wsus /persistent:no
Z:\wsusoffline\client\cmd\DoUpdate.cmd
Con lo cual empezará el laaaargo proceso de actualizaciones, que puede durar varias horas. En la ventana de terminal nos va informando escuetamente de cada parche aplicado. No nos desanimemos: si fuese mediante actualizaciones online podría tardar varios días y saturar la conexión de red del centro. De esta manera al menos tenemos controlado lo que se va haciendo y que se va actualizando.

5. Cosas a tener en cuenta.

Esto no es un camino de rosas, simplemente es algo para aminorar los problemas. Por eso debemos tener en cuenta varias cosas:

  • El wsusoffline es el sistema mas rápido si tenemos varios PC con Windows y el que menos tráfico de red genera, pero va siempre desactualizado respecto a los repositorios oficiales. Hay que vigilar si hay versiones nuevas de vez en cuando para actualizalo. A fecha de escribir esto la última actualización descargada es KB4058702, pero en Microsoft van por la KB4074608, que además es incompatible con la anterior, lo cual puede causar problemas en el script.
  • El update mas fiable es el realizado online contra los repositorios de Microsoft. El problema es que puede tardar mucho tiempo, descargar varios gigas y exigir varios reinicios. Multiplica eso por el número de Windows de tu red. De todas formas, si no nos queda otro remedio que hacerlo para salir de alguna situación complicada podemos abrir de forma directa la ventana para actualizaciones haciendo en un intérprete de comandos de Windows:
    C:\Windows\System32\control.exe /name Microsoft.WindowsUpdate 
    
  • Una alternativa a lo anterior o un método para salir de un atasco en que todas las actualizaciones fallan porque hay que subir de versión (por ejemplo, pasar de la 1703 a la 1709 en Windows 10) es descargar la última ISO de Windows (a día de hoy la 1709), montarla con wincdemu y actualizar desde allí.

Después de todas estas actualizaciones es bueno saber en que versión tenemos el Windows. Por ahí se comenta que ejecutando en una consola Powershell:
Get-CimInstance Win32_OperatingSystem | Select-Object buildnumber,version
Se muestra la versión, pero es falso ya que no sale completo todo el número de compilación. La manera de saberlo es con:
winver
Nos muestra:


Este Windows tiene por tanto en el momento en que escribimos esto la versión 1709, compilación 16299.192. Esta bastante actualizado a día de hoy, si buscamos en internet el "Historial de actualizaciones de Windows 10" vemos:


Podemos ver que nuestro Windows está en la compilación .192 y según la página de Microsoft posteriormente "sólo" han salido la 201, 214 y la 248. Cuando empecé a elaborar esta entrada ibamos por la 214 y ya ha salido otra, ahí es nada. Es verlo y me da la bajona... vaya cruz.

jueves, 8 de febrero de 2018

Acceso directo una camara IP Dlink DCS-5000L.

Tenemos una cámara IP DCS-5000L que cumple todas las expectativas de un dispositivo de este tipo: interface web, acceso y control remoto (mediante un motor para girar el objetivo), grabación, detección y aviso de movimiento y/o sonido, visión nocturna....

Además de esto echaba en falta poder acceder desde fuera, con programas mas potentes y versátiles como motion (que ya usamos con OpenWrt anteriormente) o su estupendo frontend para videovigilancia motionEye.

Ya puestos en tarea, por su buena calidad de imagen veía interesante usarla poder usar para videoconferencias o emisiones de vídeo grupales, con Google Hangouts, Skype o similares.

Por todo ello vamos a tratar en este post como conectarnos a ella con aplicaciones diferentes al tradicional navegador e ir probando cosas a ver hasta donde llegamos.

1) Acceso directo al stream de vídeo/audio.

Lo primero es acceder a las imágenes y audios de forma directa, sin pasar por el interface web, el cual tiene este aspecto:


Como vemos la visualización de las imágenes está basada en tecnologías rabiosamente nuevas como Flash, Java y ActiveX. El interface permite ver, oír, mover la cámara y acceder a su configuración.

Normalmente en foros no oficiales aparecen las URL de acceso directo a los streams de audio y vídeo de las cámaras IP de Dlink, pero para esta webcam no había manera de encontrarlos. Afortunadamente mi buen compañero David usó sus oscuras artes de teleco para llegar a acceder y pasarme esto:

  • Foto: http://192.168.0.112:8080/image/jpeg.cgi. Descarga un jpg con una foto fija tomada de forma instántanea con la cámara. Podemos tomarla con :
    wget -O imagen.jpg http://192.168.0.112:8080/image/jpeg.cgi
  • Vídeo: http://192.168.0.112:8080/video.cgi. Emite un stream de vídeo en tiempo real con formato MJPEG y resolución de 640x480 píxeles. Podemos verlo con:
    vlc http://192.168.0.112:8080/video.cgi
  • Audio: http://192.168.0.112:8080/audio.cgi. Si hemos activado el audio en la cámara y subido el volumen del micro lo suficiente accederemos al stream de audio del micrófono. Nos llega como un wav en formato PCM de 16bits de frecuencia 11025Hz. Podemos oírlo en directo con:
    vlc http://192.168.0.112:8080/audio.cgi
De esta manera podríamos monitorizar la cámara con motion usando el parámetro netcam_url:
# cat /etc/motion/motion.conf:
.....
.....
netcam_url http://192.168.0.112:8080/video.cgi
.....
.....
Como vemos aparte de acceder a cámaras locales, motion permite controlar cámaras de red. Incluso podríamos hacer cosas mas interesantes, como activar la grabación de sonido ante un evento detección de movimiento.

2) Reproducción en ventana independiente y grabación.

El siguiente paso es ver si podemos reproducir el flujo de vídeo en una ventana independiente usando esa navaja suiza de los streams multimedia que es gst-launch.

Empecemos con:
gst-launch-1.0  souphttpsrc location=http://192.168.0.112:8080/video.cgi ! jpegdec ! autovideosink
Básicamente cogemos el stream remoto, lo pasamos por el plugin "jpegdec" que decodifica los fotogramas jpeg que forman la imagen y lo volcamos en el sink (un sink es un sumidero donde acaba el stream tras pasar por los plugins) autovideosink, que muestra el vídeo con una fluidez estupenda en una ventana independiente tal que así:


De esta manera logramos ver la cámara en directo prescindiendo de Flash, Java y demás puñetas anteriores al Holoceno.

Gstreamer tiene plugins y filtros de todo tipo, como por ejemplo este para girar la imagen 90º:
gst-launch-1.0  souphttpsrc location=http://192.168.0.112:8080/video.cgi ! jpegdec ! videoflip method=clockwise ! autovideosink
Aquí tenemos varios ejemplos más. Estos son todos los plugins y sinks aplicables.

Ahora vamos a por el stream de audio. Por ejemplo, veremos como grabar el audio a un fichero, primero con mplayer:
mplayer http://192.168.0.112:8080/audio.cgi  -ao pcm:file=/tmp/mystream.wav -vc dummy
Y ahora con gst-launch:
gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! filesink location=/tmp/mystream.wav
Ahora oímos el sonido en directo (ojo, si estamos en la misma habitación el sonido acaba acoplándose con un chirrido irritante), como si fuese un micrófono espía :
gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! wavparse ! autoaudiosink
Lo mismo, pero reproduciendo el sonido por pulseaudio:
gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! wavparse ! pulsesink 
Ahora juntamos ambos streams (audio y vídeo) para reproducirlos a la vez:
gst-launch-1.0  souphttpsrc location=http://192.168.0.112:8080/video.cgi ! jpegdec ! autovideosink  souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! wavparse ! autoaudiosink
Si hacemos pruebas veremos que el audio va con un retraso de varios segundos respecto al vídeo. Después de varias pruebas concluyo que es algo ineludible, tiene pinta de ser una feature de la cámara.

3) Vídeo remoto como local: dispositivo /dev/videoX y loopback.

Vamos un paso mas allá: ¿y si queremos usar la cámara para una aplicación de videoconferencia, tipo Skype o Hangouts? ¿Y si queremos hacer una captura del vídeo para algún tipo de procesado con cheese? Estas aplicaciones no trabajan con cámaras remotas, sino que lo hacen con dispositivos físicos /dev/videoX creados al conectar una fuente de vídeo USB o PCI.

¿Podemos hacer que la cámara IP sea accedida en /dev/videoX como una local? Pues sí, Linux es maravilloso. Todo pasa por crear un dispositivo de vídeo virtual, que hará aparecer un /dev/videoX y luego podremos conectar el stream de vídeo de la cámara a dicho dispositivo. Esto se consigue con el driver v4l2loopback.

Para Ubuntu 14.04 ésta es la versión ya compilada (para Manjaro/Arch sería ésta). Los pasos para descargarlo y cargar en memoria el módulo son:
# wget https://launchpad.net/ubuntu/+archive/primary/+files/v4l2loopback-dkms_0.10.0-1_all.deb
# apt-get install v4l-utils
# dpkg -i v4l2loopback-dkms_0.10.0-1_all.deb
# rmmod v4l2loopback
# modprobe v4l2loopback -r
# modprobe v4l2loopback devices=1 exclusive_caps=1 video_nr=1 card_label="virtualvideo1"
Con esto se carga el módulo v4l2loopback en memoria y se crea un dispositivo virtual /dev/video1. Podemos comprobarlo con:
# v4l2-ctl --list-devices
De momento este dispositivo está vacío. Si abrimos vlc o cheese leyendo de él no se muestra nada. Hay que conectar el stream remoto de la cámara con el módulo v4l2loopback, el cual a su vez lo hará emerger por /dev/video1.

Esto lo hacemos con gst-launch usando el sink "v4l2sink" para enviar los datos a /dev/video1:
gst-launch-1.0  souphttpsrc location=http://192.168.0.112:8080/video.cgi ! jpegdec ! tee ! v4l2sink device=/dev/video1 sync=false
O también podemos usar ffmpeg (en ubuntu se instala antes desde un PPA).
ffmpeg -re -f mjpeg -i http://192.168.0.112:8080/video.cgi  -f v4l2 -vcodec rawvideo -pix_fmt yuv420p /dev/video1
En las pruebas realizadas con Google Hangouts, usando ffmepg la transmisión de vídeo hacia el dispositivo virtual y al cliente remoto es más fiable, estable y fluida que con gst-launch. Recomendamos por tanto usar ffmpeg sobre gst-launch para enviar el stream de la cámara al dispositivo v4l2loopback.

Para ampliar, aquí tenemos documentados varios métodos de enviar fuentes de vídeo a v4l2loopback.

Una vez creado /dev/video1 estas son las pruebas que he hecho:
  • Con vlc leyendo de /dev/video1 ("vlc v4l2:///dev/video1") la imagen se ve sin problemas.
  • Con cheese no funciona a la primera, pero quitando "exclusive_caps=1" al cargar el módulo si funciona.


  • Con Google Hangouts: he tenido problemas (no detección de la cámara) según la versión de Google Chrome y Firefox. En Chromium si ha funciona correctamente.


  • Con Skype: no lo he probado por los problemas que da con Linux últimamente, pero debe funcionar según el enlace comentado anteriormente.

Resumiendo: podemos usar la cámara IP cómo si fuera una cámara local, tanto para programas de captura de vídeo tipo cheese como para una videoconferencia con Hangouts o Skype. Las ventajas son:
  • Tiene mejor calidad que una cámara USB normal.
  • Puede colocarse en sitios que permiten tomas panorámicas para videconferencias grupales.
  • Conectados al interfaz web estándar de la webcam podemos girar/mover la cámara remotamente durante la emisión.




4) Videoconferencia con sonido.

El módulo v4l2loopback transmite vídeo, pero no sonido. En el apartado 1 y 2 hemos oído el sonido remoto recogido por la cámara en el PC local, pero para videconferencias habría que tratar el sonido remoto como una fuente de sonido (vamos, que el sistema vea el stream entrante como un micrófono). Después de varias pruebas con pulsesink y distintas configuraciones de pulseaudio según este ejemplo se consigue conectar el audio remoto con ciertas aplicaciones:

gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! wavparse ! pulsesink 

En este ejemplo anterior redirigimos el sonido del stream por la tarjeta HDMI y luego emerge en los Dispositivos de Salida, permitiendo realizar una grabación con un tercer programa (un grabador de audio en nuestro caso).

¿Esto podría usarse para videoconferencias? En teoría si, en la práctica no porque los programas de videoconferencia que he probado solo aceptan entrada de sonido de micrófonos reales, no de streams que provienen de una fuente remota o de un "monitor of ..." en pulseaudio.

Seguramente hay una manera de convertir el stream en un micrófono virtual (como hicimos con el vídeo y /dev/videoX) encadenando módulos loobpacks y null de pulseaudio, o como se comenta en diversos foros usando jackaudio , pero realmente no merece la pena. El sonido de la cámara llega con tanto retraso que desmerece el conjunto y hace vano el esfuerzo.

Mi conclusión es que es mucho más cómodo usar un micro local estándar conectado al PC desde donde hacemos la videoconferencia, de tal manera que el vídeo proviene de la cámara mediante v4l2loopback, pero el audio se recoge de forma directa por el PC, despreciando el stream de audio que genera la cámara.

Aquí una captura de pantalla de una videoconferencia de Google Hangouts mediante Chromium Browser. La imagen "ojo de pez" es la imagen de la cámara web y el sonido se recoge con un micro normal y corriente.


En la parte inferior derecha en pequeñito está la imagen tomada por el receptor, que es un móvil apuntando a la cámara web :-).

Otra imagen usando visión nocturna:


Recordemos, como dijimos en el anterior apartado, que para videoconferencias con Hangouts da mucho mejor resultado usar ffmpeg (y no gst-launch) para enviar el flujo de vídeo a v4l2loopback.

5) Emisión RTSP.

Por último, también puede ser interesante usar los streams de audio y vídeo para crear una emisión RTSP emitiendo por la red lo capturado por la cámara de forma que cualquier receptor pueda "sintonizarnos".

Esto son solo varias pinceladas para abrir las posibles vías de prueba:
  • Usando v4l2rtspserver: al parecer genera una emisión rtsp a partir de un dispositivo v4l2 (como v4l2loopback) y una fuente de sonido. Los paquetes .deb están aquí. Ademas necesita v4ltools, que hay que compilar a mano.
    El github de Michel Promonet, de donde viene esto, tiene varios proyectos muy interesantes para la emisión de vídeo.
  • Usando gstreamer: por desgracia gst-launch por si solo no es capaz de redigir el vídeo hacia rtsp. Existe una solución de pago en la que se usa un sink llamado gstrtspsink que es a su vez un servidor rtsp. Podemos pedirles una versión de evaluación.
  • Usando gstreamer para emisión rtp, mas sencilla que la de rtsp:
    # gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/video.cgi !  jpegdec ! rndbuffersize max=1316 min=1316  ! udpsink host=127.0.0.1 port=5000  
    
    Para la recepción hace falta un fichero .sdp, pero no le he dedicado mucho tiempo a hacerlo funcionar.
  • Usando una utilidad específica para montar un rtsp server: el problema es que la mayoría son sistemas profesionales de pago (por poner un ejemplo: este o este otro). He encontrado algo sencillo y gratuito hecho en perl. Compilamos según las instrucciones y arrancamos el servidor:
    # /usr/local/share/perl/5.18.2/RTSP/rtsp-server.pl
    Arrancamos la fuente de vídeo/audio:
    # ffmpeg -re -f mjpeg -i http://192.168.0.112:8080/video.cgi -i http://192.168.0.112:8080/audio.cgi -f rtsp  -muxdelay 0.1 rtsp://127.0.0.1:5545/abc
    
    La recepción se haría:
    # vlc rtsp://127.0.0.1/abc
    En las pruebas realizadas la emisión se ve pixelada y el audio viene con retraso. Utilizando audio con micro local y jugando con parámetros de ffmpeg seguramente se pueda mejorar.
  • Usando ffserver, una herramienta más de la suite FFmpeg. Tomamos este texto como guía:
    # cat /etc/ffserver.conf
    
    Port 8090
    BindAddress 0.0.0.0
    MaxClients 1000
    MaxBandwidth 10000         
    
    <feed feed1.ffm>
    File /tmp/feed1.ffm
    FileMaxSize 5M
    </Feed>
    
    <stream camara1.avi>
    Feed feed1.ffm
    Format mpjpeg
    VideoFrameRate 25
    VideoIntraOnly
    VideoSize 640x480
    NoAudio
    Strict -1
    </Stream>
    
    <stream stat.html>
    Format status
    </Stream>
    
    Arrancamos el servidor:
    # ffmpeg -re -f mjpeg -i http://192.168.0.112:8080/video.cgi  http://localhost:8090/feed1.ffm
    
    La recepción se haría en la URL:
    http://localhost:8090/camara1.avi
    Esto es sin sonido, para añadir audio debemos emitir en formato .webm.
    # cat /etc/ffserver.conf
    
    Port 8090
    BindAddress 0.0.0.0
    MaxClients 1000
    MaxBandwidth 10000         
    
    <feed feed1.ffm>
    File /tmp/feed1.ffm
    FileMaxSize 5M
    </Feed>
    
    <stream camara1.flv>
    Feed feed1.ffm
    Format webm
    
    # Audio settings
    AudioCodec vorbis
    AudioBitRate 64          # Audio bitrate
    
    # Video settings
    VideoCodec libvpx
    VideoSize 640x480        # Video resolution
    VideoFrameRate 25        # Video FPS
    AVOptionVideo flags +global_header  # Parameters passed to encoder
    # (same as ffmpeg command-line parameters)
    cpu-used 0
    AVOptionVideo qmin 10
    AVOptionVideo qmax 42
    AVOptionVideo quality good
    AVOptionAudio flags +global_header
    PreRoll 15
    StartSendOnKey
    VideoBitRate 400         # Video bitrate
    
    </Stream>         
    
    <stream stat.html>
    Format status
    </Stream>
    
    Empezamos emisión:
    # ffmpeg -re -f mjpeg -i http://192.168.0.112:8080/video.cgi -i  http://192.168.0.112:8080/audio.cgi
    
    Y conectamos con el navegador:
    http://localhost:8090/feed1.ffm
    Como antes, se ve pixelado y con el audio retrasado. La solución sería la planteada en su momento: audio local con micrófono y ajuste de opciones de ffmpeg.

Hasta aquí todo por ahora, creo que le hemos sacado a la cámara todo el jugo que le podíamos sacar.

No puedo evitar despedirme con una de las imágenes mas emocionantes de los últimos años: los dos side boosters del Falcon Heavy de SpaceX aterrizando al unísono:


Gracias Elon y buen viaje a Starman en su Tesla hacia la órbita de Marte.



martes, 30 de enero de 2018

Instalación de una aplicación mediante wine para todos los usuarios.

Hace poco me pidieron instalar la aplicación PhotoFiltre en Ubuntu ya que necesitaban usar una aplicación sencilla de retoque fotográfico. Solo tiene versión Windows, pero tras descargar el instalador y lanzarlo con wine confirmé que era totalmente funcional.

El problema que tiene wine es que la instalación de las aplicaciones va asociada al usuario, lo cual me obligaría a instalar la misma aplicación una y otra vez en todas las cuentas necesarias. En este caso la única molestia es el tiempo perdido apretando una y otra vez la misma tuerca, pero en aplicaciones que ocupan mucho espacio tenemos además el problema del disco desperdiciado con instalaciones idénticas en el home de cada alumno/profesor.

Según recuerdo con Codeweavers Crossover podías hacer una sola instalación de una aplicación Windows en una bottle (wine->bottle, son unos cachondos) y reusar luego esa bottle en distintas cuentas de usuario. Pero Crossover es de pago y yo tengo que usar wine de forma gratuita.

Así que buscando una manera de hacer una única instalación por máquina encuentro ésto y pruebo a ponerlo en práctica:

Creamos un usuario "windows" local con home en /var/home (para no interferir con nuestro /home en NFS):
# adduser --home /var/home/windows windows
Hago login con el nuevo usuario, descargo el PhotoFiltre y los ficheros de idioma español y los instalo con:
$ wine pf7-setup-en-7.2.1.exe
Después instalo el idioma español copiando el fichero según comentan en la página del programa.

El programa se ha instalado como es normal en wine, en la ruta:
/var/home/windows/.wine/drive_c/Program\ Files\ \(x86\)/PhotoFiltre\ 7/
habiéndose creado un acceso directo en el escritorio del usuario "windows". Si lo lanzamos desde dicho acceso directo se abre y funciona sin problema.

El reto está en hacer que funcione para los otros usuarios de la máquina sin tener que volver a instalarlo. Para ellos vamos a hacer uso de "sudo", que nos permitirá adoptar la identidad del usuario "windows" y ejecutar el programa desde otras sesiones de usuario. Empezamos tocando en el fichero sudoers:
# cat /etc/sudoers
..........
..........
# Configuracion Wine:
User_Alias WINDOWS_USERS = %teachers
Cmnd_Alias WINDOWS = /usr/bin/wine,/usr/bin/winecfg,/var/home/windows/pf7

Defaults:WINDOWS_USERS env_reset
Defaults:WINDOWS_USERS env_keep += DISPLAY
Defaults:WINDOWS_USERS env_keep += XAUTHORITY

WINDOWS_USERS ALL = (windows) NOPASSWD: WINDOWS

En este sudoers damos permisos a todos los usuarios del grupo "teachers" (aquí especificaremos los usuarios o grupo de usuario que queramos) a ejecutar wine, winecfg y el script /var/home/windows/pf7 con la identidad del usuario "windows". El contenido del script pf7 sería:
# cat /var/home/windows/pf7
#!/bin/bash
env WINEPREFIX="/var/home/windows/.wine" wine C:\\Program\ Files\ \(x86\)\\PhotoFiltre\ 7\\PhotoFiltre7.exe
exit 0
Lo que hace es fijar un WINEPREFIX en el directorio donde está wine y lanzar el ejecutable. Esta ruta está sacada del interior archivo .desktop que comentamoss anteriormente, creado por la aplicación en el escritorio durante su instalación con wine.

Ahora tenemos que establecer el medio para que los otros usuarios arranquen el script /var/home/windows/pf7 con la identidad del usuario "windows". Para ello definimos un nuevo script en una carpeta accesible para todo el mundo:
# cat /usr/bin/pf7
#!/bin/bash
xhost +
sudo -u windows -H /var/home/windows/pf7
exit 0
En él ponemos "xhost +" para permitir al usuario "windows" abrir aplicaciones dentro de nuestra sesión Xorg y hacemos el "sudo .../var/home/windows/pf7" para ejecutar el programa con la identidad "windows" sin que nos pida contraseña alguna.

Por último nos hace falta un fichero .desktop para poner en el escritorio de los usuario que vayan a ejecutar la aplicación:
# cat PhotoFiltre7.desktop
[Desktop Entry]
Name=PhotoFiltre 7
Exec=/usr/bin/pf7
Type=Application
StartupNotify=true
Path=/usr/bin
Icon=/usr/share/icons/hicolor/pf7.png
El icono /usr/share/icons/hicolor/pf7.png lo he extraido del .EXE con la aplicacion wrestool:
# wrestool -x fichero.exe -o .
Ultimando las cosas hemos creado una carpeta /wine con propietario "windows" y mapeado z: y "Mis Documentos" sobre ella usando winecfg con la cuenta "windows". De esta manera la aplicación podrá guardar sus datos en una carpeta accesible y fácil de encontrar.



Para finalizar, bloqueamos el login en la cuenta "windows", para que nadie trastee con ella y solo esté accesible a través de sudo:
# passwd -l windows
Y con esto ya tenemos hecha la tarea. A modo de recapitulación las ventajas son:
  • Realizamos una sola instalación y configuración por máquina, ahorrando espacio y trabajo.
  • Si la aplicación se estropea o deja de funcionar (en aplicaciones wine no es infrecuente, ya que el usuario tiene acceso total a la carpeta Archivos de Programa) no tenemos nada más que hacer una copia previa del directorio .wine con tar.gz y descomprimirla para dejar todo otra vez funcionando para todos los usuarios.
Y los inconvenientes:
  • Todos los usuarios de un mismo PC comparten la misma aplicación y carpeta de datos en /wine.
  • La aplicación va vinculada a la máquina y no al usuario. Si se mueve a otro PC no tiene acceso a sus datos o incluso puede no tener acceso a la aplicación
Por tanto, corresponde a cada cual valorar si merece o no la pena hacerlo así o de la forma tradicional.

Vamos que nos vamos.



lunes, 15 de enero de 2018

Poner las aplicaciones de Manjaro en español después una instalación fresca.

Después de instalar nuestro Manjaro XFCE en el primer arranque nos pide que instalemos los paquetes de idioma. Con las ansias de empezar a enredar lo dejamos para mas tarde y cerramos la ventana...de tal forma que luego no lo vuelve a pedir más veces y nos encontramos con que Firefox, Libreoffice y otras cosas están en inglis.

¿Cómo instalamos los paquetes de idiomas perdidos? Podemos ir al Gestor de Paquetes y buscarlos uno a uno, pero Manjaro nos tiene un sistema mejor para hacerlo de forma centralizada y ordenada.

Nos vamos al Gestor de Configuración de Manjaro en el menú:


Ahí elegimos "Configuración de idioma":


En esta ventana podemos ver los paquetes de idiomas instalados y los disponibles:


Con el botón de "Instalar paquetes" podemos elegir nuevos paquetes para añadir a las distintas aplicaciones:


Y ya está. Sencillo e indoloro, como todo en Manjaro.

domingo, 14 de enero de 2018

Como meter un recovery en Android desde linea de comandos.


Un dispositivo android tiene varios modos de arranque, desde un modo "mayormente muerto" (como el Pirata Roberts de La princesa prometida) hasta el modo completamente funcional.


Un modo muy usado si queremos trastear con el móvil es el "modo recovery", pero para sacarle toda su potencialidad normalmente nos vemos obligados a sustituir el recovery que trae por defecto el terminal por otro con más opciones. Aquí describiremos como meter un recovery nuevo mas potente a un dispositivo android.

Antes de entrar en faena y a modo de repaso para que no se me olvide, voy a enumerar los modos de los cuales tenemos noticia:

1. Modo normal/system.

Es el modo usual, con el dispositivo arrancado y funcional. Podemos interactuar con el mediante la pantalla táctil o mediante adb, conectándolo a un PC con el cable USB y enviando comandos hacia él.

El adb es además la vía mas sencilla y universal para reiniciar el móvil en los otros modos.

2. Modo recovery.:

En este modo el dispositivo arranca en un entorno restringido, que permite hacer varias tareas de mantenimiento que nos facilitan enredar con el dispositivo. Aquí el móvil se maneja con la pantalla táctil y/o los botones laterales y no es funcional en el sentido usual, ya que no se llega a cargar el sistema android.

Muchos móviles traen un stock recovery por defecto muy limitado, pero afortunadamente la comunidad ha creado recoverys sustitutos bastante avanzados, como CWM o TWRP.

Stock recovery, como vemos bastante insulso:


Recovery CWM, aquí hay mas opciones:


Recovery TWRP, con un GUI bonito y encima táctil:


Algunas de las funcionalidades que traen estos recoverys vitaminados son:

  • Cargar nuevas ROMs.
  • Hacer un reset de las distintas particiones y datos.
  • Instalar aplicaciones especiales, tipo SuperSU (para rootear) o Xposed (para añadir funcionalidades variadas).
  • Formateo, borrado, creación y montaje de particiones de la memoria SD interna del móvil.

Aquí y acá viene mejor explicado.

Para arrancar el móvil en recoverý suele haber una combinación especial de teclas de encendido o bien usando adb, como podemos ver aquí.

3. Modo fastboot/bootloader.

En este modo el móvil no puede ser manejado por su pantalla o botones, pero si podemos interactuar con él usando un cable USB y la utilidad fastboot.

Muchas veces tenemos un móvil brickeado (se queda congelando arrancando) o en bootloop (reinicia continuamente sin cargar el sistema). En estos casos si conseguimos ponerlo en modo fastboot tendremos una vía para restaurar o arreglar el sistema.

Normalmente se entra desde adb con el comando "adb reboot bootloader", aunque algunos dispositivos tienen combinaciones de teclas para arrancar en este modo. Es algo que tendremos que investigar en cada caso concreto.

Desde este modo se puede desbloquear el bootloader (en algunos móviles viene bloqueado para impedir instalar otras ROMs o recoverys) y trabajar con las particiones: borrar, formatear, sobreescribir imágenes nuevas. Es el modo que vamos a usar para meter un recovery más adelante.

4. Más abajo: modos cercanos al Amenti.

Aquí llegamos a terreno envuelto en brumas. Cada fabricante puede tener modos más profundos para arrancar el móvil, según estas normas básicas:
  • La nomenclatura depende del fabricante: download mode (aunque en algunos fabricantes es equivalente al fastboot), emergency mode, QDL mode, y seguro que hay muchos más.
  • En cada caso se usa herramientas especializadas, como Odin, SPFlashTool, MTKDroidTools, KDZ Firmware Update, MiFlash,...
  • Desde estos modos normalmente sólo se consigue flashear el móvil cargando una Stock ROM (ROM Oficial)
Cuando uno llega aquí es que tiene el móvil tan brickeado que no podemos conectar con él mediante fastboot y ya estamos en modo pánico. Mis tribulaciones con los malditos Xiami Redmi 1S son una prueba de ello.


Vamos al lío.

Yo he venido aquí a hablar de mi recovery, así que vamos a ello. Mi problema es que cada vez que tengo que meter un recovery me lanzo una y otra vez por el camino mas pedregoso: bajarme los drivers USB el teléfono, instalar una utilidad de flasheo, conseguir el fichero con el recovery para mi modelo de móvil (no vale cualquiera, hay que buscar el correcto en htcmania/xda-developers o generarlo nosotros) y realizar el flasheo de la partición que lo aloja.

Esa es la teoría. En la práctica, como la mayoría de estas utilidades solo funcionan bien en Windows no queda otra que arrancar dicho sistema, sortear los problemas con los drivers no firmados (la mayoría de los drivers de móviles están sin firmar), ver si detecta o no el dispositivo USB, ver si hay comunicación estable... y un sinfín de contratiempos más. Después de un par de horas infructuosas recuerdo que hay un método mucho más sencillo usando la línea de comandos de linux:

  1. Localizar y descargar el recovery. Suele ser un fichero .img que contiene dentro imagen de una partición con un mini-sistema Linux
  2. Conectar por adb y arrancar en modo fastboot.
  3. Desbloquear el bootloader, cargar el recovery y reiniciar.

Veamos la secuencia de comandos. Antes de nada debemos instalar las android-tools-adb y android-tools-fastboot en nuestro Linux, a veces vienen por separado o a veces en un único paquete android-tools. No hace falta ningún driver. También hay que habilitar el modo depuración USB en el menú de desarrollo del móvil.

Conectamos el móvil al PC por usb y tecleamos:
# adb devices
Si todo va bien saldrán numeritos y el nombre del teléfono. Ojo: quizá el móvil nos pregunte ahora o más adelante en su pantalla si damos permisos para aceptar la conexión desde el PC.

Reiniciamos el móvil en modo fastboot:
# adb reboot bootloader
En ese modo la pantalla no muestra nada interesante, la única manera de interactuar es a través del comando fastboot en el PC. Vemos si hay comunicación con dicho modo desde el ordenador tecleando:
# fastboot devices
Antes de nada, puede que el terminal tenga el bootloader bloqueado. Eso quiere decir que viene de fábrica configurado para impedir cambiar el recovery y/u otras partes del sistema. En ese caso lo que haremos será desloquearlo con uno de estos 2 comandos:
# fastboot oem unlock
o
# fastboot oem unlock-go
Podemos ver el estado del bootloader con:
# fastboot oem device-info
y/o
# fastboot getvar all
Debe aparecer algo así como: "Device Unlocked: true".

Una vez confirmamos que el móvil está desbloqueado, procedemos a sustituir el revovery por uno nuevo descargado de Internet (recordemos: el recovery debe ser compatible con nuestro terminal, no se pueden hacer mezclas a riesgo de brickear el movil).
# fastboot flash recovery twrp.img
También es posible hacer una prueba del recovery en dique seco, sin reescribir el original. Así confirmamos su buen funcionamiento/compatibilidad o bien lo ejecutamos para algo puntual sin sustituir el recovery que hay instalado:
# fastboot boot twrp.img
Una vez sobreescrito el nuevo recovery, reiniciamos:
# fastboot reboot
El móvil debe reiniciar bien y una vez arrancado, desde el adb podemos reiniciar en el recovery para operar con él:
# adb devices
# adb reboot recovery
Y ya está, espero que la próxima vez no tenga que perder el tiempo con Windows cuando quiera hacer esto.