Movistar Bloquea Cloudflare: Impacto en Webs y Solución

Aunque pareciese que no tiene nada que ver una cosa con otra, lo que es cierto es que estas últimas semanas han ido ligadas de la mano, pero de la manera mala. Dejadme que me explique.

El caso es que una de las aplicaciones que mantenemos empezó en un momento dado a recibir incidencias como si estuviese caída. El servidor estaba bien y yo desde mi oficina llegaba perfectamente, después de mucho indagar descubirmos que no se llegaba desde las conexiones Movistar u O2, además, lo que vimos es que había ya quejas de otros servidores que estaban afectados y solo tenían en común una cosa, que estaban detrás de Cloudflare. La cosa es que media internet utiliza los servicios de cloudflare, como CDN o como protección contra ataques DDOS o como proxy para ahorrar ancho de banda. Entendimos que debía ser un problema de enrutamiento de Movistar y desactivamos cloudflare momentaneamente a la espera de que lo solucionasen… Estamos hablando del día 3 de febrero.

Esto estuvo pasando durante toda la tarde, llegado un momento nos dijeron que ya volvía a funcionar (y que todo debería ser una avería, no nos dijeron otra cosa)

Peeero, casualidades de la vida, el día 8 se jugaba el derbi y, mira por donde, resulta que empezamos a recibir incidencias de nuevo… Vuelta a desactivar cloudflare y esperando de nuevo… Pero esta vez no lo arreglaron, el domingo seguía la cosa igual:

Y esto se quedó así hasta el lunes (qué curioso!) sin que nadie se dignase dar ninguna explicación ni disculpa. Miles de negocios perdidos y webs expuestas a ataques sin remedio por alimentar a las alimañas del deporte. Era tan evidente que ya se publicó como noticia en varios medios (https://hipertextual.com/2025/02/movistar-cloudflare-futbol). El caso es que estuvo funcionando bien hasta los partidos de champions en los que se repitieron cortes de 15 minutos alternos (igual esto había cantado demasiado).

¿Qué hacer?

Ahora nos surge el problema de qué hacer el fin de semana. No podemos irnos a descansar porque si nos vuelven a cortar cloudflare tendríamos que volver a desactivarlo a mano, así que, para todos aquellos que tengáis cloudflare os recomiendo usar algo como esto:

import requests
import subprocess
import os

def check_ip_reachable(ip):
    try:
        result = subprocess.run(['ping', '-c', '4', ip], capture_output=True, text=True, timeout=10)
        return result.returncode == 0
    except subprocess.TimeoutExpired:
        return False

def pause_cloudflare(domain, email, api_key):
    headers = {
        'X-Auth-Email': email,
        'X-Auth-Key': api_key,
        'Content-Type': 'application/json'
    }
    
    # Obtener el Zone ID
    zone_response = requests.get(f'https://api.cloudflare.com/client/v4/zones?name={domain}', headers=headers)
    zone_data = zone_response.json()
    
    if not zone_data['success']:
        print("Error al obtener el Zone ID")
        return False
    
    zone_id = zone_data['result'][0]['id']
    
    # Pausar el servicio
    pause_url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}'
    pause_data = {'paused': True}
    
    response = requests.patch(pause_url, headers=headers, json=pause_data)
    result = response.json()
    
    if result['success']:
        print(f"Servicio pausado para el dominio {domain}")
        return True
    else:
        print(f"Error al pausar el servicio: {result['errors']}")
        return False

# Configuración
ip_to_check = "192.168.1.1"  # Reemplaza con la IP que quieres comprobar
domain = "ejemplo.com"  # Reemplaza con tu dominio
cloudflare_email = "[email protected]"  # Reemplaza con tu email de Cloudflare
cloudflare_api_key = "tu_api_key"  # Reemplaza con tu API key de Cloudflare

# Comprobar si la IP está accesible
if not check_ip_reachable(ip_to_check):
    print(f"La IP {ip_to_check} no está accesible. Pausando el servicio en Cloudflare...")
    if pause_cloudflare(domain, cloudflare_email, cloudflare_api_key):
        print("Servicio pausado exitosamente")
    else:
        print("No se pudo pausar el servicio")
else:
    print(f"La IP {ip_to_check} está accesible")

Este programita básicamente lo que hace es comprobar si una IP responde al ping y si no lo hace pone en pausa cloudflare para el dominio que queramos. Para ello solo hay que configurarlo con la IP, el dominio y los datos del API de cloudflare de nuestra cuenta. Lo recomendable es hacer un bucle que lo chequee cada cierto tiempo y (código que todavía no he hecho) que cuando vuelva la conectividad se vuelva a activar cloudflare.

Para mi estas actuaciones son completamente ilegales y atentan contra la neutralidad de la red. ¿Qué será lo siguiente? ¿Evitar que visitemos blogs de ideologías no aprobadas?, ¿Hacer inaccesible el porno? Por desgracia en mi casa usamos O2, pero igual tengo que cambiarme visto lo visto.

Generar imágenes por IA en tu propio ordenador

Ya vimos en la entrada anterior como poder tener nuestro propio chat-gpt sin pagar nada a nadie usando modelos opensource y nuestra GPU, ahora le toca el turno a la posibilidad de generar imágenes por Inteligencia Artificial mediante el mismo método, en casa, de forma privada y sin tener que pagar licencias. Al lío…

La imagen anterior ha sido generada en mi ordenador, con mi tarjeta gráfica y con un prompt muy sencillito, básicamente le he pedido un robot pintando con pinceles en la pantalla del ordenador… Y me ha salido esto (hay más parámetros, pero no he tocado nada especial). Para generar estas imágenes vamos a utilizar Stable Diffussion, que es un modelo de aprendizaje automático para generar imágenes digitales de alta calidad a partir de descripciones en lenguaje natural (wikipedia). Es de código abierto y no impone restricciones a las imágenes que produce.

Como somos hombres (y mujeres) de acción os voy a dar la receta rápida para tener stable difussion y un interfaz de usuario (automatic1111) funcionando en cuestión de minutos (bueno, esto depende de vuestra conexión a internet que hay muchos gigas que descargarse). La receta original, que os recomiendo seguir si queréis experimentar un poco más con el tema, la saqué de aquí: https://github.com/AbdBarho/stable-diffusion-webui-docker pero yo he preparado una imagen que ya tiene todo lo necesario, así que lo único que tenéis que hacer es crear un archivo docker-compose.yml con este contenido:

services:
  sd-auto:
    image: yoprogramo/sd-auto:78
    ports:
      - "7860:7860"
    volumes:
      - ./data:/data
      - ./output:/output
    stop_signal: SIGKILL
    environment:
      - CLI_ARGS=--allow-code --medvram --xformers --enable-insecure-extension-access --api
      - COMMANDLINE_ARGS=--share
    restart: unless-stopped
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids:
                - "0"
              capabilities:
                - compute
                - utility

Luego, estoy suponiendo que usáis linux y tenéis la configuración de docker y de la tarjeta gráfica que ya vimos en el anterior post, solo hay que ejecutar:

docker compose up -d

Como os he dicho el proceso de descarga inicial de la imagen y del modelo van a tardar un poco (reservaos mínimo 20Gb para todo), pero si todo va bien en unos minutos podréis acceder a la url http://localhost:7860 y veréis la interfaz de AUTOMATIC1111 para stable difussion.

Inicialmente el modelo descargado es sdv1.5-pruned-emaonly que tiene sus limitaciones pero cabe en casi todas la memorias. Ya solo queda hacer la prueba, poniendo algo en el prompt y dandole a Generate.

Si no tienes demasiada memoria en tu tarjeta gráfica te saldrá algo como esto:

Pero si has sido capaz de generar una imagen, se abre todo un abanico de modelos que probar y opciones con las que trastear… El primer sitio para visitar es este:

https://civitai.com

Depurando PHP con Xdebug y Docker

Hasta hace poco he estado depurando PHP al viejo estilo, poniendo error_log, print_r y demás mensajes aquí y allá. La verdad es que era algo poco dañino y que, si programas bien, tampoco da mucho trabajo. Sin embargo echaba de menos las facilidades de java en el eclipse donde podía depurar de manera increíblemente eficiente sin modificar nada de mi código. El caso es que utilizando docker como base del desarrollo/despliegue de PHP se hacía muy complicado configurar las cosas para depurar. Al final, por circunstancias que no vienen al caso, me animé a ver cómo podríamos depurar código php desplegado usando docker… Y os hago aquí un resumen:

El problema básico

Supongamos que queremos hacer un programa básico en php y desplegarlo para poder verlo en nuestro navegador. Por ahora nos vamos a limitar a este archivo

phpinfo.php

<?php
phpinfo();

Si, es un archivo muy simplón, pero primero tenemos que desplegarlo utilizando docker y verlo en un navegador. Para ello creamos un directorio para nuestro proyecto (llamemosle php-samples, por ejemplo) y dentro del mismo creamos un subdirectorio llamado web y dentro de este directorio creamos el archivo phpinfo.php con el contenido anterior.

La forma más rápida

En el directorio superior creamos un archivo llamado docker-compose.yml con este contenido:

services:
  php:
    image: yoprogramo/php8.2:1.0.2
    volumes:
        - ./web:/var/www/Website
    ports:
        - 8080:80

Ya puestos cargamos el proyecto en visual studio code y nos quedará algo así:

Desde el terminal, o desde el menú de la extensión docker si la tenéis instalada, levantamos el contenedor. Una vez levantado ya podremos acceder a la url:

http://localhost:8080/phpinfo.php

Vale, prueba superada, ya tenemos nuestro servidor ejecutando php, ahora crearemos otro archivo dentro de web con un programa en php un poco más complejo para poder depurarlo a gusto. Vamos a llamar a ese archivo index.php y le daremos este contenido:

<?php
function isPrime($num) {
    if ($num <= 1) return false;
    if ($num == 2) return true;
    if ($num % 2 == 0) return false;
    for ($i = 3; $i <= sqrt($num); $i += 2) {
        if ($num % $i == 0) return false;
    }
    return true;
}

$randomNumber = rand(0, 19);

echo "<table border='1'>";
echo "<tr><th>Número</th><th>Primos menores que $randomNumber</th></tr>";
echo "<tr><td>$randomNumber</td><td>";

for ($i = 0; $i < $randomNumber; $i++) {
    if (isPrime($i)) {
        echo "$i ";
    }
}

echo "</td></tr>";
echo "</table>";
?>

Ahora podemos acceder a la url index.php (o no poner ruta ya que es el archivo que se cargará por defecto)

Vale, pero todo este rollo era para poder depurar el código, para poder hacerlo tenemos que hacer tres cosas:

  • Cambiar la imagen docker por yoprogramo/php8.2:1.0.2-xdebug en el docker-compose.yml y añadir este apartado al servicio php:
extra_hosts:
  - "host.docker.internal:host-gateway"
  • Instalar el plugin «PHP Debug»
  • Configurar el visual studio code para php, para ello pinchamos en el icono de depuración (run and debug) y pinchamos donde pone «create a launch.json file)

Esto nos creará un archivo por defecto donde tendremos que modificar la configuración «Listen for Xdebug» para añadir esto:

"pathMappings": {
   "/var/www/Website": "${workspaceRoot}/web"
}

Pulsamos sobre el icono de iniciar depuración con es configuración (o pulsamos F5)

Con todo configurado solo nos queda reiniciar el docker con la nueva imagen y poner algún breakpoint en el código (eso se hace pinchando a la izquierda del número de línea donde queramos que se pare la ejecución, por ejemplo dentro del bucle de index.php

Ahora volvemos a cargar la página index.php y si todo ha ido bien la ejecución se parará y podremos ver en el editor los valores de las variables

Y continuar la ejecución o hacerlo paso a paso

Si queréis saber cual es el camino difícil o cómo hacerlo con vuestras propias imágenes docker, solo tenéis que preguntar…

Usando una fuente de texto en IoT

Una de las características de los dispositivos que se usan para IoT es la limitación de recursos que tiene. No tienen un sistema operativo disponible y todo lo que queramos que haga hay que programarlo prácticamente de cero. Eso incluye cosas tan peregrinas como definir las fuentes de texto que se utilizarán para escribir en pantalla. Esto es algo que, generalmente, damos por hecho en cualquier otro sistema que programamos, pero que ofrece la oportunidad de ver un poco mejor cómo funciona la informática por dentro.

En principio las pantallas en las que querramos utilizar estas fuentes (en las que queramos escribir algo) son bastante pequeñas, la que vemos en la siguiente imagen, por ejemplo, es de 128×64 pixels (SSD1306):

¿Cómo hacemos para escribir un caracter en una pantalla de estas características? Quitando los comandos propios de inicialización, limpieza y demás, lo único que hacemos es indicar qué pixel queremos que se ilumine y cual no (si hay interés ya desarrollaremos un poco más en profundidad los comandos que se le mandan) y, para ahorrar ciclos y ancho de banda, hay que enviarle los datos en bytes completos (8 bits) y cada bit representa un pixel que se ilumina o no.

Hay software ya desarrollado para convertir fuentes ttf en código c que se puede usar con ciertas librerías, pero vamos a escoger el camino difícil, supongamos que queremos utilizar los primeros 95 caracteres ASCII empezando por el espacio. Serían estos:

Quitando el 127 que no nos interesa porque no es imprimible, una vez decididos los caracteres que queremos imprimir tenemos que decidir el tamaño que queremos utilizar. En este caso queremos que tengan una altura de 8 pixels y, como van a ser de ancho fijo, tendrán una anchura de 8 pixels igualmente. Un carácter se representaría entonces de esta manera:

Y su representación en bits de la primera fila sería 00011000 o lo que es lo mismo 0x18 en hexadecimal. Esta letra, traducida a bytes quedaría: 0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66,0x66,0x00 y con esta información ya sabríamos qué bytes mandar a la pantalla cuando tuviésemos que escribir la letra.

Hacer este proceso para cada letra sería muy pesado, más teniendo en cuenta que son 95 caracteres, pero como nosotros somos programadores os voy a contar un método para extraer estos datos de una mejor manera. Vamos a generar una imagen y vamos a pintar allí las letras que queremos. Para ello abriremos el programa gráfico favorito que tengamos (el mío es gimp) y crearemos una imagen justo del tamaño para que quepan todas nuestras letras (ni más ni menos). En este caso son 95*8 = 760 y 8 de altura, con el fondo negro.

Una vez creada esa imagen, creamos un texto en la fuente que queramos y escribimos los caracteres que queremos:

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Ajustamos para que quepan todos (quitamos alisado y hints varios) y quedará algo así:

Luego cambiamos el tipo de imagen a indexada con 2 colores (solo blanco y negro) y lo exportamos a formato raw:

Esto lo que hace es generarnos un archivo .data que contiene 1 byte por pixel que, como hemos indexado solo contendrá ceros o unos… Y ahora toca programar.

El objetivo es obtener una ristra de bytes juntando los ceros y unos agrupándolos de 8 en ocho. Luego veremos cómo usarlos. Lo más sencillo es hacer un scrip en python, así que escribimos algo así:

def convert_to_hex(byte_data):
    # Agrupamos los datos de 8 en 8
    byte_data = [byte_data[i:i+8] for i in range(0, len(byte_data), 8)]
    # Convertimos cada byte a su representación en hexadecimal
    resu = []
    for i in range(len(byte_data)):
        byte = sum([byte_data[i][j] << j for j in range(8)])
        # Añadimos el valor en hexadecimal a la lista resu
        resu.append(f'0x{byte:02X}')

    return resu

Y ahora solo nos queda leer de un archivo y escribirlo en otro como código c:

def read_binary_file(file_path):
    with open(file_path, 'rb') as file:
        return file.read()

def write_hex_file(hex_data, output_path):
    with open(output_path, 'w') as file:
        # Separamos los valores por comas y ponemos un salto de linea cada 16 valores
        file.write ('static const char font_8x8[] = {\n\t')
        slice_size = 16
        for i in range(0, len(hex_data), slice_size):
            file.write(', '.join(hex_data[i:i+slice_size]))
            file.write(',\n\t')
        file.write('};')

Lo juntamos todo en una función principal pasándole como parámetro el archivo de entrada y el de salida:

def main(input_file, output_file):
    binary_data = read_binary_file(input_file)
    hex_data = convert_to_hex(binary_data)
    write_hex_file(hex_data, output_file)

Y ya podemos ejecutarlo, el resultado sería algo así:

static const char font_8x8[] = {
	0x00, 0x0C, 0x36, 0x36, 0x0C, 0x00, 0x1C, 0x06, 0x18, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
	0x3E, 0x0C, 0x1E, 0x1E, 0x38, 0x3F, 0x1C, 0x3F, 0x1E, 0x1E, 0x00, 0x00, 0x18, 0x00, 0x06, 0x1E,
	0x3E, 0x0C, 0x3F, 0x3C, 0x1F, 0x7F, 0x7F, 0x3C, 0x33, 0x1E, 0x78, 0x67, 0x0F, 0x63, 0x63, 0x1C,
	0x3F, 0x1E, 0x3F, 0x1E, 0x3F, 0x33, 0x33, 0x63, 0x63, 0x33, 0x7F, 0x1E, 0x03, 0x1E, 0x08, 0x00,
	0x0C, 0x00, 0x07, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x07, 0x0C, 0x30, 0x07, 0x0E, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x07, 0x6E, 0x00,
	0x1E, 0x36, 0x36, 0x3E, 0x63, 0x36, 0x06, 0x0C, 0x0C, 0x66, 0x0C, 0x00, 0x00, 0x00, 0x30, 0x63,
	0x0E, 0x33, 0x33, 0x3C, 0x03, 0x06, 0x33, 0x33, 0x33, 0x0C, 0x0C, 0x0C, 0x00, 0x0C, 0x33, 0x63,
	0x1E, 0x66, 0x66, 0x36, 0x46, 0x46, 0x66, 0x33, 0x0C, 0x30, 0x66, 0x06, 0x77, 0x67, 0x36, 0x66,
	0x33, 0x66, 0x33, 0x2D, 0x33, 0x33, 0x63, 0x63, 0x33, 0x63, 0x06, 0x06, 0x18, 0x1C, 0x00, 0x0C,
	0x00, 0x06, 0x00, 0x30, 0x00, 0x36, 0x00, 0x06, 0x00, 0x00, 0x06, 0x0C, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x0C, 0x3B, 0x00, 0x1E,
	0x36, 0x7F, 0x03, 0x33, 0x1C, 0x03, 0x06, 0x18, 0x3C, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x73, 0x0C,
	0x30, 0x30, 0x36, 0x1F, 0x03, 0x30, 0x33, 0x33, 0x0C, 0x0C, 0x06, 0x3F, 0x18, 0x30, 0x7B, 0x33,
	0x66, 0x03, 0x66, 0x16, 0x16, 0x03, 0x33, 0x0C, 0x30, 0x36, 0x06, 0x7F, 0x6F, 0x63, 0x66, 0x33,
	0x66, 0x07, 0x0C, 0x33, 0x33, 0x63, 0x36, 0x33, 0x31, 0x06, 0x0C, 0x18, 0x36, 0x00, 0x18, 0x1E,
	0x06, 0x1E, 0x30, 0x1E, 0x06, 0x6E, 0x36, 0x0E, 0x30, 0x66, 0x0C, 0x33, 0x1F, 0x1E, 0x3B, 0x6E,
	0x3B, 0x3E, 0x3E, 0x33, 0x33, 0x63, 0x63, 0x33, 0x3F, 0x0C, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0x00,
	0x36, 0x1E, 0x18, 0x6E, 0x00, 0x06, 0x18, 0xFF, 0x3F, 0x00, 0x3F, 0x00, 0x0C, 0x7B, 0x0C, 0x1C,
	0x1C, 0x33, 0x30, 0x1F, 0x18, 0x1E, 0x3E, 0x00, 0x00, 0x03, 0x00, 0x30, 0x18, 0x7B, 0x33, 0x3E,
	0x03, 0x66, 0x1E, 0x1E, 0x03, 0x3F, 0x0C, 0x30, 0x1E, 0x06, 0x7F, 0x7B, 0x63, 0x3E, 0x33, 0x3E,
	0x0E, 0x0C, 0x33, 0x33, 0x6B, 0x1C, 0x1E, 0x18, 0x06, 0x18, 0x18, 0x63, 0x00, 0x00, 0x30, 0x3E,
	0x33, 0x3E, 0x33, 0x0F, 0x33, 0x6E, 0x0C, 0x30, 0x36, 0x0C, 0x7F, 0x33, 0x33, 0x66, 0x33, 0x6E,
	0x03, 0x0C, 0x33, 0x33, 0x6B, 0x36, 0x33, 0x19, 0x07, 0x00, 0x38, 0x00, 0x00, 0x0C, 0x00, 0x7F,
	0x30, 0x0C, 0x3B, 0x00, 0x06, 0x18, 0x3C, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x6F, 0x0C, 0x06, 0x30,
	0x7F, 0x30, 0x33, 0x0C, 0x33, 0x30, 0x00, 0x0C, 0x06, 0x00, 0x18, 0x0C, 0x7B, 0x3F, 0x66, 0x03,
	0x66, 0x16, 0x16, 0x73, 0x33, 0x0C, 0x33, 0x36, 0x46, 0x6B, 0x73, 0x63, 0x06, 0x3B, 0x36, 0x38,
	0x0C, 0x33, 0x33, 0x7F, 0x1C, 0x0C, 0x4C, 0x06, 0x30, 0x18, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x03,
	0x33, 0x3F, 0x06, 0x33, 0x66, 0x0C, 0x30, 0x1E, 0x0C, 0x7F, 0x33, 0x33, 0x66, 0x33, 0x66, 0x1E,
	0x0C, 0x33, 0x33, 0x7F, 0x1C, 0x33, 0x0C, 0x0C, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x36, 0x1F,
	0x66, 0x33, 0x00, 0x0C, 0x0C, 0x66, 0x0C, 0x0C, 0x00, 0x0C, 0x03, 0x67, 0x0C, 0x33, 0x33, 0x30,
	0x33, 0x33, 0x0C, 0x33, 0x18, 0x0C, 0x0C, 0x0C, 0x3F, 0x0C, 0x00, 0x03, 0x33, 0x66, 0x66, 0x36,
	0x46, 0x06, 0x66, 0x33, 0x0C, 0x33, 0x66, 0x66, 0x63, 0x63, 0x36, 0x06, 0x1E, 0x66, 0x33, 0x0C,
	0x33, 0x1E, 0x77, 0x36, 0x0C, 0x66, 0x06, 0x60, 0x18, 0x00, 0x00, 0x00, 0x33, 0x66, 0x33, 0x33,
	0x03, 0x06, 0x3E, 0x66, 0x0C, 0x33, 0x36, 0x0C, 0x6B, 0x33, 0x33, 0x3E, 0x3E, 0x06, 0x30, 0x2C,
	0x33, 0x1E, 0x7F, 0x36, 0x3E, 0x26, 0x0C, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x36, 0x0C, 0x63,
	0x6E, 0x00, 0x18, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x01, 0x3E, 0x3F, 0x3F, 0x1E, 0x78, 0x1E,
	0x1E, 0x0C, 0x1E, 0x0E, 0x0C, 0x06, 0x18, 0x00, 0x06, 0x0C, 0x1E, 0x33, 0x3F, 0x3C, 0x1F, 0x7F,
	0x0F, 0x7C, 0x33, 0x1E, 0x1E, 0x67, 0x7F, 0x63, 0x63, 0x1C, 0x0F, 0x38, 0x67, 0x1E, 0x1E, 0x3F,
	0x0C, 0x63, 0x63, 0x1E, 0x7F, 0x1E, 0x40, 0x1E, 0x00, 0x00, 0x00, 0x6E, 0x3B, 0x1E, 0x6E, 0x1E,
	0x0F, 0x30, 0x67, 0x1E, 0x33, 0x67, 0x1E, 0x63, 0x33, 0x1E, 0x06, 0x30, 0x0F, 0x1F, 0x18, 0x6E,
	0x0C, 0x36, 0x63, 0x30, 0x3F, 0x38, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x1F, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
	};

Y ya estaría… ¿Cómo usaríamos esto en nuestra aplicación IoT? Pues básicamente incluiríamos la cabecera font_8x8.h y cuando necesitásemos localizar un carácter ASCII simplemente tendremos que restarle 32 al caracter, multiplicarlo por el número de caracteres de nuestra fuente (95) y ese sería el primer byte del caracter dentro del array. Como vamos a necesitar todas las líneas del caracter podemos hacer una función que las recupere de esta manera:

char getcharslicefrom8x8font(char c, int rowInChar)
{
    return font_8x8[(c - 32) + (rowInChar)*95];
}

Para escribir una cadena completa en una línea habría que localizar la posición inicial de la línea, recuperar todos los bytes de cada caracter que queramos escribir y mandarlos a esa línea de la pantalla (así explicado simplificadamente).

Tenéis todo el código de la conversión de bitmap a c en este repositorio: https://github.com/yoprogramo/font_to_c

Happy coding!

UPDATE: Ya se puede encontrar la fuente así creada en los repositorios de un par de proyectos muy interesantes: https://github.com/fhoedemakers/pico-infonesPlus/ y https://github.com/fhoedemakers/pico-smsplus

De puertas traseras y software libre

Es casi imposible que no hayas oido hablar del backdoor xz, no es que yo pueda darte más información sobre el tema, os dejo un video de alguien que os cuenta el caso completo como si de un episodio de serie negra se tratase:

El caso es que, alguien durante tres años ha ido infiltrándose en un repositorio de un elemento pequeño pero crítico de software libre llamado xz, de tal manera que consiguió, no solo quedarse como mantenedor de ese repositorio sino que fue introduciendo, poco a poco, una puerta trasera que permitía el acceso remoto (todavía hay que ver el payload real lo que llegaba a hacer) y conseguir que ese backdoor se distribuyese en algunas de las más importantes distribuciones.

Por suerte, o mejor dicho, por la misma estructura del software libre, esta versión no pasó de las versiones inestables de las distribuciones y se descubrió el pastel porque una persona notó que algo iba más lento de lo que debía después de la actualización. Esta persona (Andres Freund) no se paró en medir el tiempo de respuesta sino que terminó encontrando la causa subyacente y la puerta trasera que habían metido (aquí el aviso que dió a la comunidad) y, obviamente, la reacción de los mantenedores de la distribución, e incluso del antiguo mantenedor del repositorio fue inmediata y reliminó todas las trazas del código dañino.

Hay varias cosas que podemos destacar, pero yo me quedo con un par de ellas:

  1. La dependencia que tenemos de código que han escrito terceros y que pueden estar bien mantenidos o no (dependiendo del ánimo de esa persona o de sus circunstancias personales)
  2. La potencia del ecosistema open source para descubrir y arreglar este tipo de problemas. Todos los sistemas operativos actuales, desde mac os hasta windows usan componentes externos, no hay ninguno 100% original y tampoco es que se pueda saber qué es lo que usan exactamente. Si hay un backdoor en windows o en macos nos lo tendremos que comer con patatas porque nadie puede mirar lo que hay dentro.

Estoy seguro de que esta forma de actuar, por muy inteligente y paciente que sea, no deja de ser un ataque en toda regla con unas finalidades seguramente malvadas (crear una botnet inmensa, por ejemplo) y no creo que sepamos realmente quien está detrás de ello y, posiblemente, nos de para una docuserie de Netflix un día de estos.

En fin, no ha pasado nada, todo está en orden de nuevo y lo malo es que nos deja un regusto amargo y hace bajar un peldaño la confianza que teníamos en el ecosistema (pero no mucho, oye, que seguimos estando a salvo).