enchufado.com RSS 2.0 Feed http://enchufado.com/ Feed rss 2.0 del blog enchufado.com es Empezando a andar con Docker http://enchufado.com/post.php?ID=379 http://enchufado.com/post.php?ID=379 Tue, 11 Apr 2023 17:07:05 +0000Ya hace unos dias, aparecieron los contenedores. Si fue para quedarse o no, sólo el tiempo lo dirá, pero mientras tanto, llevan un tiempo dando vueltas, han ido madurando y sin duda pueden encontrarse diversos casos de uso para ellos. Así que en éste post vamos a tratar de introducirlos y a ver algún ejemplo básico.

Teoria de Docker

Arquitectura

Docker usa una arquitectura cliente-servidor en la que el cliente de Docker habla con el Docker daemon, el servicio, quien hace realmente las tareas de build, ejecución y distribución de los contenedores Docker.

Los contenedores

Un contenedor es un proceso ejecutado en una sandbox para aislarlo del resto de procesos del host. Ese aislamiento se produce basándose en los namespaces y cgroups del kernel de Linux. Algunas características de un contenedor:

  • Es una instancia ejecutable de una imagen. Puede crearse, iniciarse, pararse, moverse o eliminarse.
  • Pueden ser ejecutados (docker run) directamente sobre un host físico, en una VM o desplegados en el cloud.
  • Son portables (funcionan en diversos SO's).
  • Estan aislados de otros contenedores y procesos no sólo en su ejecución, sino también en cuanto al software, binarios y configuraciones.

Un contenedor supone una capa de abstracción más por encima del SO (Sistema Operativo). Añadí "más" porque hasta entonces, se puede decir que se operaba bajo el paradigma de la virtualización (VM o Máquina Virtual), ya de por sí una capa de hardware por software situada entre el hardware real y el SO.

Algunas ventajas de los contenedores son:

  • Portabilidad/distribución: Poder mover servicios/aplicaciones con cierta facilidad a otro host (bien a través de una definición de los servicios/aplicaciones, bien exportando/importando conenedores y/o imágenes de los mismos).
  • Segmentación/modularidad (orientación a microservicios): Idealmente cada servicio va en su propio contenedor y se conectan entre ellos en base a necesidad.
  • Ligereza: Cada servicio tiene su propio contenedor con lo mínimo imprescindible para que éste funcione como se desea.
  • Escalabilidad: Facilita poder dividir los servicios/aplicaciones en distintos hosts.
  • Gestión de versiones de servicios/aplicaciones y del SO, al estar éstas desacopladas del Sistema Operativo y existir un histórico de las mismas en los registries.
  • Seguridad: Por el aislamiento de cada contenedor tanto de otros contenedores, como del SO.

Existen diversos motores de contenedores: lxc, docker, RKT, CRI-O, podman, containerd... Y por encima de éstos, aunque no entraremos a comentarlos, como soluciones de orquestación y automatización de contenedores tenemos Docker Swarm, OpenShift y Kubernetes. El artículo que nos ocupa es sobre Docker.

Las imágenes

Una imagen es en realidad un sistema de archivos con todas las dependencias necesarias para ejecutar el servicio/aplicación. Ésta se tiene que construir (docker build).

Las imágenes son la base de los contenedores, ya que éstos se crean a partir de las mismas. Son inmutables en el sentido que funcionan como un template/plantilla, lo cual no significa que no las podamos modificar para adaptarlas a nuestras necesidades y posteriormente guardarlas (modificando ese template o creando uno nuevo). Pero es importante entender el contepto de que una imagen es una base; no se ejecuta. Lo que se ejecuta es un contenedor creado a partir de una imagen.

Para empezar con Docker, lo más conveniente es realizar los primeros pasos usando imágenes existentes sin modificar y aplicando las configuraciones que requiramos y que estén previstas por las mismas. La biblioteca más grande de imágenes Docker por antonomasia es Docker Hub, y es allí donde hemos de empezar a buscar los servicios que necesitemos.

Dockerfile

En relación a las imágenes, los archivos Dockerfile permiten modificarlas creando de nuevas. Dockerfile es un archivo con las instrucciones necesarias para realizar las modificaciones deseadas a la imagen. Normalmente se parte de una imagen base, a la que se añaden paquetes, se crean usuarios/archivos/directorios, se asignan permisos... Docker tomará éste archivo para hacer el build de la imagen, para lo cual se descargará (si no la tiene ya) la imagen base, ejecutará las instrucciones y acabará generando (en el repositorio de imágenes local) la imagen resultante.

Docker Registry

Como acabamos de avanzar, cuando se trabaja con una imagen Docker para lanzar un contenedor, el flujo que sigue Docker es: si no tengo la imagen localmente, la voy a pedir al registry público (Docker registry) para descargarla y posteriormente poder crear un contenedor en base a la misma. Desde ese mismo instante, esa imagen se servirá en local a ese mismo host siempre que la necesite. El entorno local es en cierta medida una biblioteca de imágenes con opciones sencillas que guarda aquellas con las que hemos trabajado. Si queremos opciones avanzadas como publicar esa biblioteca para que otros puedan hacer uso de nuestras imágenes, necesitariamos de un Docker Registry.

Redes y almacenamiento

Docker permite definir ambos tipos de recursos.

Por lo que respecta a redes, ofrece distintos tipos de redes (drivers) en función de las necesidades:

  • Bridge, el usado por defecto. Útil para cuando los contenedores necesitan hablar entre ellos. En éste caso, puede definir direcciones o rangos ip con direccionamiento dentro del scope privado de ipv4.
  • Host, elimina el isolation entre contenedores y el host, haciando uso directamente de la red del host.
  • Overlay, conecta múltiples Docker daemons entre ellos y habilita los servicios Swarm para hablar entre ellos.
  • Macvlan, permite asignar una MAC address a un contenedor, haciendo que parezca un host físico. Útil para aplicaciones legagy que no vayan bien pasándolas por el stack de red de Docker.
  • None, si queremos deshabilitar todo el networking.
  • Plugins de terceras partes.

Por lo que respecta a almacenamiento, los archivos que se crean dentro un contenedor, se crean en una capa escribible que se pierde cuando se elimina el contenedor. Esa capa de storage es gestionada por un storage driver (unionfs), pensada más bien para trabajar con las imágenes, y no es conveniente (por performance y espacio ocupado) hacer uso indiscriminado de la misma. Así pues, para la persistencia de datos, mejor performance y menor espacio ocupado, es mejor hacer uso de alguna solución de persistencia, bien sea en la propia imagen (versus contenedor) modificándola para que incluya de serie aquello que queremos, bien sea usando volúmenes. Tipos:

  • Bind mounts, que son puntos de montaje entre el host y el contenedor y por tanto los archivos serán visibles/estarán disponibles desde ambos lados.
  • Named volumes, que son volúmenes internos a Docker.

Práctica de Docker

Instalación en una Debian

$ curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian  $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
$ apt-get update
$ apt-get install docker-ce docker-ce-cli containerd.io

Ejemplos de linea de comandos

# Listado de contenedores mostrando también los parados
$ docker ps -a

# Ejecución de contenedores manual (docker run)

# Ejecuta un contenedor en base a la imagen "hello world". Éste 
# escribe un mensaje por pantalla y finaliza su ejecución.
$ docker run hello-world

# Ejecuta un contenedor en base a la imagen "docker/getting-started". Éste despliega un tutorial
# de inicio a Docker que puede ser navegado apuntando nuestro navegador a http://localhost/
$ docker run -d -p 80:80 docker/getting-started
#
# Aquí nos fijamos en los siguientes parámetros:
# -d ejecuta el contenedor en modo detached (lo desvincula/deja libre la consola desde dónde se lanza)
# -p publica un puerto del host (el de la izquierda) a otro del contenedor (el de la derecha)

# Ejecuta un contenedor en base a la imagen "bitnami/apache". Éste despliega
# un servidor apache sirviendo la web alojada en el path /home/web
$ docker run -d -v /home/web:/app -p 8080:8080 bitnami/apache
#
# Aquí se añade el parámetro -v, que mapea un volumen con un directorio del host dentro del contenedor (bind mount).

# Inicio/Parada/Reinicio de un contenedor
$ docker start|stop|restart nombre_contenedor

# Eliminado de un contenedor, sólo posible cuando está parado
$ docker rm nombre_contenedor

# Listado de imagenes
$ docker image ls

# Listado de redes
$ docker network ls

# Listado de volumenes
$ docker volume ls

Para la ejecución de contenedores organizadamente tenemos el cliente de Docker docker-compose. Ésta herramienta permite organizar la parametrización de cada contenedor, definir las relaciones entre los mismos, el orden de dependencia/arranque, usar uno o varios archivos de entorno (.env) y Dockerfiles, así como crear redes y volúmenes de forma más sencilla. Todo ésto mediante un archivo de definición .yaml, que podría considerarse un archivo de definición de proyecto y despliegue con un formato específico.

Para ver un ejemplo sencillo, tomaremos el docker-compose.yml público de la imagen Docker de Wordpress:

version: '3.1'

services:

  wordpress:
    image: wordpress
    restart: always
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wordpress:/var/www/html

  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - db:/var/lib/mysql

volumes:
  wordpress:
  db:

Como vemos, lo primero a indicar es la versión de la especificación de Docker Compose que vamos a desplegar. Y a continuación va la palabra services seguida por cada uno de los servicios que deseamos; en éste caso 2, wordpress y db. Éste nombre del servicio lo ponemos nosotros. Cada servicio de corresponde a un contenedor.

Dentro de cada servicio debemos especificar un mínimo obligatorio: la imagen a usar para cada uno de ellos y según cada imagen base, probablemente sea necesario especificar algunas variables de entorno para que éste inicie por primera vez correctamente. El resto de parámetros (redes, volúmenes, puertos...) ya depende de lo que quedamos hacer/montar. En éste aspecto, en el docker-compose.yml vas usando imágenes de diferentes distribuidores de software y montando un "Tetris" estableciendo puntos de unión entre servicios.

Para levantar los servicios con éste sistema en modo detached seria:

$ docker-compose up -d

El cliente nos irá informando de todo lo que va haciendo (descarga y extracción de imagen, levantamiento de cada servicio...) y finalmente del estado de cada servicio.

Creación de imágenes

No vamos a profundizar en éste punto, pero sí que podemos ver un ejemplo básico de modificación de una imagen mediante la instalación de un paquete para una distribución base (Debian):

FROM debian:latest-slim
RUN apt-get update && \
    apt-get install --no-install-recommends -y htop iotop procps psutils && \
    rm -rf /var/lib/apt/lists/*

En éste caso, estamos diciendo que vamos a usar la imagen slim de la última versión de la imagen de Docker de Debian y a instalar una serie de utilidades en la nueva imagen resultante. Si quisiéramos usarla, tendríamos que crear un contenedor en base a la misma, bien con docker run, bien con docker-compose o con cualquier otro cliente de Docker.

]]>
Laboratorio con KVM http://enchufado.com/post.php?ID=378 http://enchufado.com/post.php?ID=378 Sat, 16 Oct 2021 10:53:03 +0000En éste post vamos a ver cómo montar un laboratorio de hosts virtuales (guests) con KVM que compartan la misma red (el mismo rango/direccionamiento) que el hipervisor (host). El propósito es que a nivel de red, estén de igual a igual tanto con el hipervisor como con otros hosts físicos (o virtuales) de la propia red. Ésto es interesante para realizar pruebas (p.ej. montar un cluster de algo) sin tener que disponer de varias máquinas físicas y, por tanto, cuando se tiene/quiere destinar poco presupuesto (y evitar cacharrear con hardware).

Ya en el pasado he abordado parte del tema y voy a tratar de no repetirme demasiado, además de ser distinto en el sentido que:

  • Es más completo/actualizado.
  • Vamos a trabajar únicamente por consola/CLI.
  • Y adicionalmente se van a mencionar una serie de comandos de referencia/utilidad de KVM.

Vamos a usar (cómo no) nuestra amada Debian GNU/Linux en su versión 11 (bullseye), a usar sólo herramientas CLI y a tratar de instalar lo básico e imprescindible del lado del host que hará de hipervisor porque vamos a suponer que tenemos un presupuesto limitado. En mi caso es más que una suposición porque estoy hablando de un miniPC con un Intel Atom x5-Z8350, 4GB de RAM y 64GB de almacenamiento eMMC, así que aplicando ésta receta a éste host es garantia de funcionamiento con un "low powered PC". ¡Vamos allá!

Instalación de un setup KVM básico

Queremos los paquetes estrictamente necesarios para evitar bloatware en la medida de lo posible:

$ apt-get install --no-install-recommends qemu-system libvirt-clients libvirt-daemon-system virtinst bridge-utils

Crear una interficie de tipo bridge público

La idea es que el bridge sea quien pase a tener la dirección ip que hasta ahora tenia la interfície de red física:

$ vi /etc/network/interfaces

# Make sure we don't get addresses on our raw device (change 'static' to 'manual')
iface enp1s0 inet manual

# Comment out the rest of the info of the primary network interface
#auto enp1s0
#iface enp1s0 inet static
#       address 192.168.2.8
#       netmask 255.255.255.0
#       gateway 192.168.2.1

# Bridge will become the primary network interface owning the ip
auto br0
iface br0 inet static
        address 192.168.2.8
        netmask 255.255.255.0
        network 192.168.2.0
        broadcast 192.168.2.255
        gateway 192.168.2.1
        bridge_ports enp1s0
        bridge_stp off
        bridge_fd 0
        bridge_maxwait 0
        dns-nameservers 8.8.8.8
:wq

$ reboot

Después del reinicio, deberiamos tener red en el host hipervisor y tanto la interfície física como el bridge levantados, siendo éste último el que tenga asociada la ip:

root@bt3:~# ifconfig 
br0: flags=4163  mtu 1500
        inet 192.168.2.8  netmask 255.255.255.0  broadcast 192.168.2.255
        inet6 fe80::6436:60ff:fec8:8215  prefixlen 64  scopeid 0x20
        ether 66:36:60:c8:82:15  txqueuelen 1000  (Ethernet)
        RX packets 3402  bytes 319121 (311.6 KiB)
        RX errors 0  dropped 1890  overruns 0  frame 0
        TX packets 363  bytes 31256 (30.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp1s0: flags=4163  mtu 1500
        ether 84:39:be:74:66:67  txqueuelen 1000  (Ethernet)
        RX packets 4643  bytes 917990 (896.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 361  bytes 31148 (30.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Deshabilitamos el firewall en la interficie bridge

Así evitamos interferencias, al menos en las pruebas:

# Disable netfilter for the bridge to allow all traffic to be forwarded to it,
# and therefore to the virtual machines they are connected to
$ vi /etc/sysctl.d/99-netfilter-bridge.conf
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
:wq
$ modprobe br_netfilter
$ echo br_netfilter > /etc/modules-load.d/br_netfilter.conf
$ sysctl -p /etc/sysctl.d/99-netfilter-bridge.conf

Mapeamos ese bridge en KVM

Éste punto es imprescindible y a veces olvidado:

# Create a new virtual network
$ vi bridged-network.xml
<network>
    <name>bridged-network</name>
    <forward mode="bridge" />
    <bridge name="br0" />
</network>
:wq
$ virsh net-define bridged-network.xml
$ virsh net-start bridged-network
$ virsh net-autostart bridged-network
$ virsh net-list --all

Instalamos la/s VM

En éste punto instalamos el paquete libosinfo-bin, que nos permitirá conocer las "variantes" para indicar en el parámetro os-variant a Qemu/KVM. Uno de los puntos importantes es el de la red. Con el parámetro --network le indicamos la recién creada/mapeada a KVM:

$ apt install libosinfo-bin
$ osinfo-query os # Run it (and grep it!) to know the correct os-variant
$ virt-install --name vn_name --ram 1024 --vcpus 1 \
--disk path=/var/lib/libvirt/images/vm_name-vm.qcow2,size=10 \
--os-type linux --os-variant debian10 --network network=bridged-network \
--graphics none --console pty,target_type=serial \
--location 'http://ftp.debian.org/debian/dists/bullseye/main/installer-amd64/' \
--extra-args 'console=ttyS0,115200n8 serial'

En éste punto ya estamos ante una instalación normal y corriente de una Debian. Asignamos a la VM una dirección ip libre del mismo rango de red que el host hipervisor (p.ej. la 192.168.2.9) y comprobamos la conectividad. Haciendo p.ej. un ping al hipervisor o al gateway, deberiamos tener visibilidad.

Si queremos crear una nueva VM a imagen y semejanza de la que acabamos de crear, podemos clonarla (ver abajo VM clone) para ahorrar tiempo y esfuerzos. Os dejo a continuación el cheatsheet de lo que me parece más útil e interesante de conocer de Qemu/KVM, no sin antes comentaros que los comandos siempre hablan de domain para referirse a VM/máquina virtual ya que el término viene de la jerga Xen:

# Basic ops
$ virsh list
$ virsh list --all # Also show stopped VMs
$ virsh start|reboot|shutdown|destroy # Destroy forces stop
$ virsh undefine vm_name --remove-all-storage # Delete VM definition + data
or
$ virsh undefine vm_name (delete VM definition) # Delete VM definition
$ rm -f /var/lib/libvirt/images/vm_name-vm.qcow2 # Delete disc manually
or
$ virsh pool-list (list storage pools)
$ virsh pool-refresh pool_name
$ virsh vol-delete --pool images vm_name-vm.qcow2 # Delete disk from pool

# Attach to VM virsh console
$ virsh console vm_name
# Detach from VM virsh console
Ctrl+]+5

# View VM info
$ virsh dominfo vm_name
# Set autostart on a VM
$ virsh autostart vm_name

# Create additional volume/disk
$ virsh vol-create-as images test_vol.qcow2 2G
# List volumes
$ virsh vol-list --pool images
# Attach volume/disk to VM
$ virsh attach-disk --domain vm_name --source /var/lib/libvirt/images/test_vol.qcow2 --persistent --target vdb
# Detach volume/disk from VM
$ virsh detach-disk --domain vm_name --persistent --live --target vdb
# Delete a volume/disk
$ virsh vol-delete test_vol.qcow2 --pool images
# Resize a volume/disk
$ qemu-img resize /var/lib/libvirt/images/test.qcow2 +1G

# Edit a guest virtual machine's XML
$ virsh edit vm_name

# VM clone
$ virsh shutdown vm_name
$ virt-clone --original vm_name --name vm_name2 --file /var/lib/libvirt/images/vm_name2-vm.qcow2
(We will need to start it up later and change hostname and network info)

# Snapshot creation
$ virsh shutdowm vm_name
$ virsh snapshot-create-as --domain vm_name --name mysnapshot --description "preupdate snapshot"
# List snapshots
$ virsh snapshot-list vm_name
# Snapshot reversion
$ virsh snapshot-revert --domain vm_name --snapshotname mysnapshot --running
# Delete snapshots
$ virsh snapshot-delete --domain vm_name --snapshotname mysnapshot

# Backup creation
$ virsh shutdown vm_name
$ virsh dumpxml vm_name > /opt/kvm_backup/vm_name_config.xml (configuration)
$ virsh domblklist vm_name
$ cp /var/lib/libvirt/images/vm_name.qcow2 /opt/kvm_backup/ (data)
# Backup restore
$ cp /opt/kvm_backup/vm_name.qcow2 /var/lib/libvirt/images/
$ virsh define --file /opt/kvm_backup/vm_name.xml
]]>
Cámara virtual en GNU/Linux http://enchufado.com/post.php?ID=377 http://enchufado.com/post.php?ID=377 Sun, 03 May 2020 22:04:56 +0000En éstos tiempos de confinamiento por causa del COVID-19, se ha disparado el número de videoconferencias en remoto y las aplicaciones que lo permiten: Hangouts, Whatsapp y Skype en lo personal, y Zoom, Teams en lo empresarial, por mencionar unas cuantas.

Algunas de éstas aplicaciones tienen algunos efectos divertidos incorporados, como difuminar el fondo, o añadir uno divertido o imaginario. Se pueden hacer otras cosas, como realizar el stream de un video que dispongamos nosotros para p.ej. gastar una broma a los compañeros. Ésto se puede hacer en GNU/Linux y lo vamos a conseguir con v4l2loopback, un paquete que consiste en un modulo de kernel que creará una webcam virtual.

Video

Primero procederemos con la parte de video. Listaremos primero los dispositivos de video por si tenemos alguna otra webcam en el equipo (podria ser alguna incorporada o externa que ya tengamos conectada):

$ ls -lh /dev/video*
crw-rw----+ 1 root video 81, 0 may  3 19:15 /dev/video0
crw-rw----+ 1 root video 81, 1 may  3 19:15 /dev/video1

Hecho ésto, instalamos los paquetes correspondientes a v4l2loopback, que compilarán el módulo de carga dinámica para nuestra versión de kernel y lo cargaremos. Si volvemos a listar los devices, deberia salir uno nuevo adicional:

$ apt-get install v4l2loopback-dkms v4l2loopback-utils
$ modprobe v4l2loopback
$ ls -lh /dev/video*
crw-rw----+ 1 root video 81, 0 may  3 19:15 /dev/video0
crw-rw----+ 1 root video 81, 1 may  3 19:15 /dev/video1
crw-rw----+ 1 root video 81, 2 may  3 21:38 /dev/video2

Como podemos ver, el dispositivo de loopback de video creado es /dev/video2. Podemos probar a reproducir ya el video por el dispositivo con ffmpeg escogiendo (con -map) el primer stream de video disponible:

$ ffmpeg -re -i poltergeist.mp4 -map 0:v -f v4l2 /dev/video2

Para comprobar que funciona, podemos abrir VLC, ir a Medio -> Abrir dispositivo de captura y seleccionar el dispositivo de video correspondiente al dispositivo de loopback de video creado (/dev/video2 en nuestro caso).

Audio

De un modo similar a cuando hemos listado los dispositivos de video, haremos lo propio con los de audio para luego identificar cual es el que añadimos:

$ cat /proc/asound/cards
 0 [PCH            ]: HDA-Intel - HDA Intel PCH
                      HDA Intel PCH at 0xf1520000 irq 34

Cargamos el módulo correspondiente al audio y listamos de nuevo los dispositivos para identificar el nuevo:

$ modprobe snd-aloop
$ cat /proc/asound/cards
 0 [PCH            ]: HDA-Intel - HDA Intel PCH
                      HDA Intel PCH at 0xf1520000 irq 34
 1 [Loopback       ]: Loopback - Loopback
                      Loopback 1
# También lo podemos hacer con el comando: aplay -l

Ahora que ya tenemos ambos dispositivos, video y audio, obtenemos la información de los streams (tanto de video como de audio) del archivo de video que queremos reproducir via la webcam virtual, ya que podria ser que un video tuviera más de un stream de audio (p.ej. un video con diversos idiomas), y tenemos que seleccionar el deseado. Nos vamos a fijar en el string "Stream":

$ ffprobe poltergeist.mp4
ffprobe version 4.2.2-1+b1 Copyright (c) 2007-2019 the FFmpeg developers
  built with gcc 9 (Debian 9.2.1-28)
  configuration: --prefix=/usr --extra-version=1+b1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'poltergeist.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2018-09-30T09:49:41.000000Z
  Duration: 00:05:11.96, start: 0.000000, bitrate: 318 kb/s
    Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720 [SAR 1:1 DAR 16:9], 188 kb/s, 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc (default)
    Metadata:
      creation_time   : 2018-09-30T09:49:41.000000Z
      handler_name    : ISO Media file produced by Google Inc. Created on: 09/30/2018.
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)
    Metadata:
      creation_time   : 2018-09-30T09:49:41.000000Z
      handler_name    : ISO Media file produced by Google Inc. Created on: 09/30/2018.

En éste caso sólo hay 1 stream por medio: 1 de video y 1 de audio. Con éstos datos, podemos reproducir el video y el audio a través de la webcam virtual del siguiente modo:

$ ffmpeg -re -i poltergeist.mp4 -map 0:0 -f v4l2 /dev/video2 -map 0:1 -f alsa hw:1,1

Si queremos validarlo con VLC, volvemos a ir a Medio -> Abrir dispositivo de captura y seleccionar el dispositivo de video correspondiente al dispositivo de loopback de video creado (/dev/video2), y por lo que respecta al dispositivo de audio, seria en el formato "hw:1,1":

Con ésto funcionando, en aquellas aplicaciones en las que se pueda elegir el dispositivo de la supuesta webcam, podremos elegir la webcam de loopback, poner a reproducir el video que queramos y ver las caras de la gente :)

Para terminar, comentar que podemos ver la configuración del dispositivo de video y cambiarlo del siguiente modo (en el ejemplo, establecer el rate de fps a 25):

$ v4l2-ctl --all -d 2
$ v4l2loopback-ctl set-fps 25 /dev/video2

Éste post no hubiera sido posible de no ser por éste y éste otro artículo. Desde aquí mandamos agradecimientos a sus autores.

]]>
Servidor VPN fácil con Wireguard http://enchufado.com/post.php?ID=376 http://enchufado.com/post.php?ID=376 Tue, 02 Apr 2019 22:36:20 +0000En el post previo vimos cómo crear una VPN punto-a-punto con Wireguard para, por ejemplo, conectar dos sedes. Ésta vez, vamos a montar un servidor/concentrador VPN con el mismo software para que puedan conectarse clientes varios (roadwarriors). En nuestro caso, usaremos un móvil Android a través del cliente de VPN de Google Play que, a pesar de estar en Beta, parece funcionar correctamente.

Como anteriormente ya describimos ésta pieza de software, vamos directos al lío.

Servidor

  1. Habilitamos el ip forwarding:
  2. # Temporalmente
    $ sysctl net.ipv4.ip_forward=1
    
    # O de forma permanente
    $ vi /etc/sysctl.d/99-sysctl.conf
    net.ipv4.ip_forward = 1
    :wq
    $ sysctl -p
    
  3. Abrimos en el firewall el puerto 51820/udp y permitimos el forwarding a nivel de cortafuegos:
  4. iptables -I INPUT -p udp --dport 51820 -j ACCEPT
    iptables -P FORWARD ACCEPT
    
  5. Si tenemos un router, habrá que redireccionar el tráfico UDP del puerto 51820 a la dirección ip del servidor de la LAN que ejecutará el servicio/concentrador VPN Wireguard. Ésto hay que hacerlo a través del propio router.
  6. Generamos las claves pública y privada:
  7. $ wg genkey > privatekey
    $ chmod 600 privatekey
    $ wg pubkey < privatekey > publickey
    
  8. Creamos la configuración de Wireguard en el servidor. En éste caso creamos un solo peer, pero se pueden poner tantos como clientes VPN sean necesarios:
  9. $ mkdir /etc/wireguard
    $ vi /etc/wireguard/wg0.conf
    
    [Interface]
    Address = 10.200.200.1/24
    SaveConfig = true
    ListenPort = 51820
    PrivateKey = [SERVER PRIVATE KEY]
    
    # Sustituir 'eth0' por la interficie por la que el servidor escuchará de cara a Internet
    PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
    
    [Peer]
    # Cliente foo
    PublicKey = [FOO's PUBLIC KEY]
    #PresharedKey = [PRE-SHARED KEY]
    AllowedIPs = 10.200.200.2/32
    
    :wq
    
  10. En éste punto, podemos gestionar la interfaz "manualmente" usando wg-quick(8), o bien usando el servicio con el mismo nombre de systemd. Ejemplo de uno y otro:
  11. # Manualmente
    $ wg-quick up wg0
    [#] ip link add wg0 type wireguard
    [#] wg setconf wg0 /dev/fd/63
    [#] ip address add 10.200.200.1/24 dev wg0
    [#] ip link set mtu 1420 up dev wg0
    [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    
    $ ifconfig wg0
    wg0: flags=209  mtu 1420
            inet 10.200.200.1  netmask 255.255.255.0  destination 10.200.200.1
            unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
            RX packets 0  bytes 0 (0 KiB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 0  bytes 0 (0 KiB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    $ wg-quick down wg0
    [#] wg showconf wg0
    [#] ip link delete dev wg0
    [#] iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
    
    # Automáticamente
    $ systemctl enable wg-quick@wg0.service
    $ systemctl start wg-quick@wg0.service
    $ systemctl status wg-quick@wg0.service
    ● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0
       Loaded: loaded (/lib/systemd/system/wg-quick@.service; enabled; vendor preset: enabled)
       Active: active (exited) since Tue 2019-04-02 20:40:09 CEST; 1s ago
         Docs: man:wg-quick(8)
               man:wg(8)
               https://www.wireguard.com/
               https://www.wireguard.com/quickstart/
               https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8
               https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8
      Process: 7145 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS)
     Main PID: 7145 (code=exited, status=0/SUCCESS)
    
    abr 02 20:40:08 srv systemd[1]: Starting WireGuard via wg-quick(8) for wg0...
    abr 02 20:40:08 srv wg-quick[7145]: [#] ip link add wg0 type wireguard
    abr 02 20:40:09 srv wg-quick[7145]: [#] wg setconf wg0 /dev/fd/63
    abr 02 20:40:09 srv wg-quick[7145]: [#] ip address add 10.200.200.1/24 dev wg0
    abr 02 20:40:09 srv wg-quick[7145]: [#] ip link set mtu 1420 up dev wg0
    abr 02 20:40:09 srv wg-quick[7145]: [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    abr 02 20:40:09 srv systemd[1]: Started WireGuard via wg-quick(8) for wg0.
    

Cliente

Con un cliente normal en consola, tendríamos la siguiente configuración:

$ vi /etc/wireguard/wg0.conf

[Interface]
Address = 10.200.200.2/24
PrivateKey = [FOO's PRIVATE KEY]
DNS = 10.200.200.1

[Peer]
PublicKey = [SERVER PUBLICKEY]
PresharedKey = [PRE-SHARED KEY]
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = [SERVER PUBLIC IP]:51820
:wq

$ wg-quick up wg0

Comentar brevemente que el AllowedIPs es importante puesto que redigirá el tráfico para esos rangos de red por la VPN. Sería el análogo a las rutas pushed a los clientes de OpenVPN. Usando el catch-all 0.0.0.0/0, ::/0 redirigirá todo el tráfico a través de la VPN, pero podríamos decidir que sólo enrutamos ciertos rangos de red.

Por ejemplo, podemos poner sólo los rangos de red de nuestra LAN (192.168.1.0/24, 172.16.0.0/24) y de éste modo sólo se enrutará por la VPN cuando solicitemos alguna dirección de éstos rangos. El resto del tráfico irá por la ruta por defecto.

Veamos cómo proceder con la configuración del cliente Android:

  1. Instalamos el cliente Android.
  2. Lanzamos la aplicación y aparece vacía. Añadimos la siguiente configuración pulsando el botón '+' y seleccionando "Create from scratch". A parte de la interfaz wg0 (primera imagen), tendremos que añadir un peer (el servidor/concentrador VPN, segunda imagen):
  3. Y la guardamos (imagen del diskette arriba a la derecha). Nótese que ésto supone que tenemos un servidor DNS en el host servidor/concentrador de VPN. Si no es así, podemos poner algún otro servidor dns público (8.8.8.8, 1.1.1.1, etc). Con ésto ya lo tenemos configurado, y en la pantalla principal de la app nos saldria la siguiente interfaz wg0, que sólo tenemos que habilitar (encendiendo el interruptor) para establecer la conexión con el servidor/concentrador de VPN:
  4. A continuación podemos ver una muestra de captura de tráfico y estadísticas de la interfaz del lado del servidor/concentrador VPN:
  5. $ ifconfig wg0
    wg0: flags=209  mtu 1420
            inet 10.200.200.1  netmask 255.255.255.0  destination 10.200.200.1
            unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
            RX packets 158  bytes 16948 (16.5 KiB)
            RX errors 0  dropped 158  overruns 0  frame 0
            TX packets 63  bytes 7788 (7.6 KiB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    $ tcpdump -ni any udp and port 51820
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
    
    20:21:56.513268 IP 31.4.189.86.53659 > 192.168.2.7.51820: UDP, length 148
    20:22:01.532883 IP 31.4.189.86.53659 > 192.168.2.7.51820: UDP, length 148
    20:26:01.896448 IP 31.4.189.86.53619 > 192.168.2.7.51820: UDP, length 148
    20:26:01.897602 IP 192.168.2.7.51820 > 31.4.189.86.53619: UDP, length 92
    20:26:01.967003 IP 31.4.189.86.53619 > 192.168.2.7.51820: UDP, length 96
    20:26:01.995992 IP 31.4.189.86.53619 > 192.168.2.7.51820: UDP, length 112
    20:26:02.164035 IP 31.4.189.86.53619 > 192.168.2.7.51820: UDP, length 96
    20:26:03.155887 IP 31.4.189.86.53619 > 192.168.2.7.51820: UDP, length 96
    20:26:05.195718 IP 31.4.189.86.53619 > 192.168.2.7.51820: UDP, length 96
    
  6. Llegados a éste punto, podemos intentar conectar a algún servicio del servidor/concentrador VPN o a algún otro de algún host enrutado por la VPN y debería funcionarnos.

Y ésto es todo.

]]>