<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>bisko.be - Dev</title><description>Tecnología, desarrollo, hardware y experimentos.</description><link>https://bisko.be/es/posts/dev/</link><language>es</language><item><title>Cómo ejecutar Ollama con GPUs antiguas en CachyOS</title><link>https://bisko.be/es/posts/dev/running-ollama-with-older-gpus-on-cachyos/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/running-ollama-with-older-gpus-on-cachyos/</guid><description>Hoy estaba migrando mi VM de Windows 11 a Linux, para tener más flexibilidad con los servicios que corren en ella, y me encontré con un problema: Ollama no podía ejecutarse en mis GPUs antiguas, una GTX1070 y una GTX970</description><pubDate>Sat, 28 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;tldr&quot;&gt;TL;DR;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ollama-cuda&lt;/code&gt; en CachyOS viene (a día de hoy) con soporte únicamente para &lt;code&gt;compute &gt;= 7.5&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Si tienen GPUs antiguas con un &lt;code&gt;compute&lt;/code&gt; menor, TIENEN QUE hacer una de estas 2 cosas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compilar &lt;code&gt;ollama-cuda&lt;/code&gt; desde el código fuente, con soporte para los targets de compute más antiguos&lt;/li&gt;
&lt;li&gt;Instalar &lt;code&gt;ollama-vulkan&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yo elegí &lt;code&gt;ollama-vulkan&lt;/code&gt; y funciona perfectamente.&lt;/p&gt;
&lt;h3 id=&quot;hora-de-la-historia&quot;&gt;Hora de la historia&lt;/h3&gt;
&lt;p&gt;Hoy me llegó la inspiración para apagar por fin mi VM de Windows 11 y migrarla a Linux.&lt;/p&gt;
&lt;p&gt;Los requisitos eran poder ejecutar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ollama - para la magia de los modelos locales&lt;/li&gt;
&lt;li&gt;Docker - para desarrollo remoto&lt;/li&gt;
&lt;li&gt;Steam - para jugar&lt;/li&gt;
&lt;li&gt;Jellyfin - para el archivo de videos caseros&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ahora me voy a centrar solo en Ollama, porque fue una experiencia interesante y me dejó preguntándome por qué las cosas no son más obvias.&lt;/p&gt;
&lt;p&gt;La máquina tiene 2 GPUs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GTX 1070 - 8GB&lt;/li&gt;
&lt;li&gt;GTX 970 - 4GB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Quería ejecutar Ollama en ellas para jugar con modelos locales y ver cómo podría integrarlos en algunos proyectos. Por ejemplo: anonimizar datos de forma local, hacer una implementación no destructiva de OpenClaw que se ajuste a mis casos de uso, OCR local para los documentos que escaneo con el teléfono, o automatizar algunas cosas de la gestión cotidiana de una casa.&lt;/p&gt;
&lt;p&gt;Así que, naturalmente, instalé &lt;code&gt;ollama&lt;/code&gt; y &lt;code&gt;ollama-cuda&lt;/code&gt;, ya que tengo 2 GPUs Nvidia, esperando que funcionara sin problemas, pues en Windows funcionaba perfectamente apenas unos minutos antes.&lt;/p&gt;
&lt;p&gt;Arranqué Ollama, cargué &lt;code&gt;qwen3:8b&lt;/code&gt; para un experimento rápido y… se estaba ejecutando en la CPU 🤔&lt;/p&gt;
&lt;p&gt;Las GPUs eran perfectamente capaces de mantenerlo en la VRAM, pero por alguna razón Ollama prefería ejecutarlo en la CPU.&lt;/p&gt;
&lt;p&gt;Después de intentar descifrarlo a partir de los logs, le pedí ayuda a Claude y llegó a una conclusión bastante desalentadora:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; logs&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; tell&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; story&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; clearly.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Both&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; your&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GPUs&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; detected&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; but&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; then&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; filtered&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; because&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; they&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; fail&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CUDA&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; initialization:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  &quot;filtering device which didn&apos;t fully initialize&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; id=GPU-eb132a13-b9f4-40e3-d85d-60f211dfd21e&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; library=CUDA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  &quot;filtering device which didn&apos;t fully initialize&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; id=GPU-550263c8-37e2-2a25-c0b6-10cc7511ed20&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; library=CUDA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; reason&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CUDA&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; library&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bundled&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Ollama&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0.17.4&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; was&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; compiled&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; these&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; architectures&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; only:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  CUDA.0.ARCHS&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;=750,800,860,870,880,890,900,1000,1030,1100,1200,1210&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; minimum&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; supported&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; compute&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; capability&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 7.5&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (Turing &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; RTX&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 20xx&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; series&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;). Your GPUs are older than that:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  ┌──────────┬────────────────────┬──────────────┐&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   GPU&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Compute&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Capability&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Architecture&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  ├──────────┼────────────────────┼──────────────┤&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GTX&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1070&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; │&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 6.1&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Pascal&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  ├──────────┼────────────────────┼──────────────┤&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GTX&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 970&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  │&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 5.2&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Maxwell&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  └──────────┴────────────────────┴──────────────┘&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  Neither&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; supported&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; list,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; so&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CUDA&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; initialization&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; fails&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; both,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; and&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Ollama&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; falls&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; back&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CPU-only&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; inference.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  Your&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; options:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  1.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Downgrade&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Ollama&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; older&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; version&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; that&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; still&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; included&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Pascal/Maxwell&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; support&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (pre-0.6.x &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;era&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; had&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; broader&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; arch&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; coverage,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; but&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; check&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; release&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; notes&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; specifics&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  2.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Build&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Ollama&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; source&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CUDA_ARCHITECTURES=&quot;52;61;75;80;86&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; include&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; your&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GPU&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; architectures.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  3.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Upgrade&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GPUs&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; at&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; least&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; RTX&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 2060&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; or&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; newer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (compute &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;7.5+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esto fue un poco decepcionante para mí, porque quiero aprovechar las GPUs al máximo antes de pasar a una nueva.&lt;/p&gt;
&lt;p&gt;No quería compilar Ollama desde el código fuente, ya que debería funcionar directamente y me confundía mucho que no lo hiciera, cuando en la instalación de Windows sí soportaba esas arquitecturas.&lt;/p&gt;
&lt;p&gt;¡Además, esas tarjetas aparecían como compatibles en el sitio web de Ollama!&lt;/p&gt;
&lt;p&gt;Para resumir una larga historia de búsquedas en Google:&lt;/p&gt;
&lt;p&gt;Resulta que &lt;code&gt;ollama-cuda&lt;/code&gt; viene solo con los requisitos mencionados arriba… y la sugerencia era instalar &lt;code&gt;ollama-vulkan&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;¡Después de hacerlo, todo empezó a funcionar sin ningún problema!&lt;/p&gt;</content:encoded><category>linux</category><category>llm</category><category>ollama</category><category>cachyos</category><category>vulcan</category><category>cuda</category></item><item><title>[Linux] Recuperar una sesión desconectada de `apt dist-upgrade`</title><link>https://bisko.be/es/posts/dev/linux-recovering-a-disconnected-apt-dist-upgrade-session/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/linux-recovering-a-disconnected-apt-dist-upgrade-session/</guid><description>Hoy estaba actualizando mi instalación de Proxmox de forma remota a través de la consola integrada, pero olvidé lo que estaba haciendo y cerré la pestaña a medias. Como resultado, no pude volver a conectarme, ya que los paquetes se seguían actualizando en segundo plano y la actualización había hecho una pregunta sobre reemplazar un archivo y estaba esperando una respuesta</description><pubDate>Wed, 23 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hoy estaba actualizando mi instalación de Proxmox de forma remota a través de la consola integrada, pero olvidé lo que estaba haciendo y cerré la pestaña a medias.&lt;/p&gt;
&lt;p&gt;Como resultado, no pude volver a conectarme, ya que los paquetes se seguían actualizando en segundo plano y la actualización había hecho una pregunta sobre si reemplazar un archivo y estaba esperando mi respuesta.&lt;/p&gt;
&lt;p&gt;Así que podía entrar por SSH, pero no podía hacer nada, porque seguía esperando una respuesta en una consola a la que no me podía conectar, ya que la interfaz no se abría.&lt;/p&gt;
&lt;p&gt;Después de buscar un poco, me topé con un &lt;a href=&quot;https://serverfault.com/questions/19634/how-to-reconnect-to-a-disconnected-ssh-session&quot;&gt;hilo de Serverfault&lt;/a&gt; que mencionaba el uso de &lt;code&gt;reptyr&lt;/code&gt; para tomar el control de la sesión de terminal por SSH.&lt;/p&gt;
&lt;p&gt;Dejo aquí exactamente lo que hice para recuperar la sesión y terminar la actualización:&lt;/p&gt;
&lt;p&gt;De &lt;a href=&quot;https://serverfault.com/questions/19634/how-to-reconnect-to-a-disconnected-ssh-session#comment1506308_979180&quot;&gt;este comentario&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://github.com/nelhage/reptyr&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; reptyr&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esto instaló &lt;code&gt;reptyr&lt;/code&gt; en la máquina.&lt;/p&gt;
&lt;p&gt;Luego busqué todas las sesiones abiertas con &lt;code&gt;ps ax | grep pts&lt;/code&gt; para encontrar el proceso que andaba buscando.&lt;/p&gt;
&lt;p&gt;Después encontré el proceso más alto en el árbol de llamadas que tuviera un &lt;code&gt;pts&lt;/code&gt; asignado y me adjunté a él mediante su PID:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;reptyr&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1390511&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;¡Y así pude terminar la actualización! 🎉&lt;/p&gt;</content:encoded><category>Uncategorized</category></item><item><title>[Linux] Syncthing – sistema de archivos de solo lectura al ejecutarlo como servicio</title><link>https://bisko.be/es/posts/dev/linux-syncthing-read-only-file-system-when-running-as-service/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/linux-syncthing-read-only-file-system-when-running-as-service/</guid><description>Llevo un tiempo usando Syncthing para sincronizar mi copia de trabajo con un servidor de desarrollo local, unos cuantos meses ya. Al principio lo usaba iniciando sesión y ejecutándolo manualmente desde la línea de comandos. Funcionaba bien, así que no lo pensé dos veces cuando pasé a ejecutarlo como servicio.</description><pubDate>Wed, 06 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Llevo un tiempo usando &lt;a href=&quot;https://syncthing.net/&quot;&gt;Syncthing&lt;/a&gt; para sincronizar mi copia de trabajo con un servidor de desarrollo local, unos cuantos meses ya.&lt;/p&gt;
&lt;p&gt;Al principio lo usaba iniciando sesión y ejecutándolo manualmente desde la línea de comandos. Funcionaba bien, así que no lo pensé dos veces cuando pasé a ejecutarlo como servicio. Arrancó, sincronizó algunos datos, todo iba bien.&lt;/p&gt;
&lt;p&gt;Hasta que dejó de ir bien, ¡por supuesto! 🙂&lt;/p&gt;
&lt;p&gt;Resulta que no me di cuenta de que en los logs estaban apareciendo un montón de errores que decían que el sistema de archivos del servidor de desarrollo era de solo lectura, y eso hacía que Syncthing no sincronizara nada.&lt;/p&gt;
&lt;p&gt;Pensé que era un bug y que simplemente mostraba &lt;code&gt;Out of Sync&lt;/code&gt; en las carpetas, pero que en realidad sí estaba sincronizando. Pues me equivocaba.&lt;/p&gt;
&lt;p&gt;Después de depurar durante varias horas y de no encontrar ninguna pista en los foros con unas cuantas búsquedas en Google, llegué a un &lt;a href=&quot;https://wiki.archlinux.org/title/syncthing#read-only_file_system_error_on_/etc_although_run_as_root&quot;&gt;artículo sobre Syncthing en la ArchWiki&lt;/a&gt;, que mencionaba justo mi caso:&lt;/p&gt;
&lt;p&gt;read-only file system error on /etc although run as root&lt;/p&gt;
&lt;p&gt;Era exactamente mi caso de uso, salvo que la carpeta problemática no era &lt;code&gt;/etc&lt;/code&gt;, sino &lt;code&gt;/usr/local/src&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Bueno, después de leer lo que decía, resulta que si ejecutas Syncthing (o cualquier cosa) mediante &lt;code&gt;systemd&lt;/code&gt; y tienes &lt;code&gt;ProtectSystem&lt;/code&gt; configurado en &lt;code&gt;full&lt;/code&gt; para el servicio, algunos directorios se vuelven de solo lectura por defecto. &lt;code&gt;/usr&lt;/code&gt; es una de esas rutas.&lt;/p&gt;
&lt;p&gt;Para solucionarlo, hay que añadir una nueva línea en la configuración del servicio y reiniciarlo (y hacer &lt;code&gt;daemon-reload&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ReadWritePaths=/usr/local/src&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Después de esto, Syncthing debería funcionar bien.&lt;/p&gt;</content:encoded><category>Homelab</category><category>linux</category><category>permissions</category><category>read-only</category><category>syncthing</category><category>systemd</category></item><item><title>[Linux] Ejecutar varios comandos en varios servidores en paralelo</title><link>https://bisko.be/es/posts/dev/linux-running-multiple-commands-on-multiple-servers-in-parallel/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/linux-running-multiple-commands-on-multiple-servers-in-parallel/</guid><description>Tengo en casa un clúster de Raspberry Pi con Docker Swarm. Para no perder el control, todas usan la misma configuración: usuarios, versiones de Docker, etc. A veces algunas actualizaciones requieren ejecutar lo mismo en todos los nodos del clúster, por ejemplo, crear una nueva red macvlan de Docker. Para</description><pubDate>Mon, 19 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tengo en casa un clúster de Raspberry Pi con Docker Swarm. Para no perder el control, todas usan la misma configuración: usuarios, versiones de Docker, etc.&lt;/p&gt;
&lt;p&gt;A veces algunas actualizaciones requieren ejecutar lo mismo en todos los nodos del clúster, por ejemplo, crear una nueva red macvlan de Docker.&lt;/p&gt;
&lt;p&gt;Para hacerlo, hay que ejecutar los mismos comandos en todos los nodos manualmente, entrando por &lt;strong&gt;ssh&lt;/strong&gt; a cada uno, ejecutando los comandos y pasando al siguiente.&lt;/p&gt;
&lt;p&gt;Como intento reducir mi trabajo a la larga y tener todo lo más organizado posible, empecé a buscar formas de ejecutar comandos en todos los nodos al mismo tiempo.&lt;/p&gt;
&lt;h3 id=&quot;iteración-1-un-script-de-python-a-medida&quot;&gt;Iteración 1: un script de Python a medida&lt;/h3&gt;
&lt;p&gt;La primera iteración consistió en crear un script de Python propio. Por un lado, quería aprender un poco mejor Python; por otro, no encontraba una forma buena de ejecutar los comandos de manera más nativa.&lt;/p&gt;
&lt;p&gt;Como cabe esperar (oigan, todavía tengo poca experiencia con el lenguaje 🙂), se convirtió en un desastre enorme muy rápido y se rompía con facilidad.&lt;/p&gt;
&lt;p&gt;Tenía que encontrar otra manera de hacerlo, sin tener que volver a aprender lo que hice hace 3 semanas y lo que se rompió la última vez.&lt;/p&gt;
&lt;h3 id=&quot;iteración-2-pssh&quot;&gt;Iteración 2: &lt;code&gt;pssh&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Buscando por internet me topé con &lt;code&gt;[pssh](https://linux.die.net/man/1/pssh)&lt;/code&gt;, que puede ejecutar cosas en paralelo mediante SSH.&lt;/p&gt;
&lt;p&gt;Pensándolo bien, fue bastante fácil ponerlo en marcha.&lt;/p&gt;
&lt;p&gt;El problema era que, para ejecutar algunas cosas, necesitaba hacerlo con &lt;code&gt;sudo&lt;/code&gt;. Pasar contraseñas de un lado a otro no era lo ideal, así que tuve que buscar de nuevo cómo enviar la contraseña de &lt;code&gt;sudo&lt;/code&gt; cuando el servidor remoto la pidiera.&lt;/p&gt;
&lt;p&gt;Resulta que es bastante fácil si das con el &lt;a href=&quot;https://unix.stackexchange.com/a/487558/134974&quot;&gt;comentario de StackOverflow&lt;/a&gt; adecuado.&lt;/p&gt;
&lt;h3 id=&quot;la-versión-resumida-es&quot;&gt;La versión resumida es:&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stty&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -echo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Password: &quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;read&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; PASS&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stty&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;PASS&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pssh&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -h&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;HOSTFIL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;E&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /tmp/output&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -I&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;`&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./commands.sh`&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hay que escribir &lt;code&gt;-I &quot;`cat ./commands.sh`&quot;&lt;/code&gt; exactamente así (entre comillas y comillas invertidas); de lo contrario, no ejecuta bien los comandos. La contraseña se enviará como un comando aparte y no se tomará como entrada de &lt;strong&gt;sudo&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;La otra trampa es que, por la manera en que está montado todo esto, hay que ejecutar &lt;code&gt;sudo&lt;/code&gt; con el parámetro &lt;code&gt;-S&lt;/code&gt; para que lea bien la contraseña.&lt;/p&gt;</content:encoded><category>Uncategorized</category></item><item><title>Adivinando la contraseña numérica de un archivo zip usando solo utilidades de línea de comandos</title><link>https://bisko.be/es/posts/dev/guessing-a-numeric-zip-file-password-only-with-command-line-utilities/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/guessing-a-numeric-zip-file-password-only-with-command-line-utilities/</guid><description>Hoy recibí mi factura mensual de mi proveedor de internet y algo me pareció extraño. La factura, incluida en el correo, venía en un archivo ZIP con contraseña. Sin mencionar lo extraño que resulta en estos días recibir un adjunto en forma de archivo ZIP (spam, malware, phishing, etc.), la</description><pubDate>Mon, 14 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hoy recibí mi factura mensual de mi proveedor de internet y algo me pareció extraño.&lt;/p&gt;
&lt;p&gt;La factura, incluida en el correo, venía en un archivo ZIP con contraseña.&lt;/p&gt;
&lt;p&gt;Sin mencionar lo extraño que resulta en estos días recibir un adjunto en forma de archivo ZIP (spam, malware, phishing, etc.), la contraseña que eligieron me pareció muy débil.&lt;/p&gt;
&lt;p&gt;Es la fecha de nacimiento del titular de la cuenta en el formato &lt;code&gt;YYMMDD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Hice un cálculo muy burdo mentalmente mientras estaba en el gimnasio y resultó en muy pocas combinaciones posibles. Después de usar una herramienta en línea para calcular cuántos días han pasado (ya que no todas las combinaciones resultarían en una fecha válida) desde el 1900-01-01, resulta que las combinaciones son solo 44,075 (hasta el 2020-09-14).&lt;/p&gt;
&lt;p&gt;Sabiendo que las herramientas optimizadas que usan el poder de las GPU para descifrar hashes pueden hacer millones de intentos por segundo, quise ver qué tan rápido se podía adivinar la contraseña sin usarlas y utilizando solo la CLI de Linux/macOS (ya que solo tenía acceso a un VPS en el gimnasio).&lt;/p&gt;
&lt;p&gt;Se me ocurrió el siguiente código que funciona en macOS:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;echo {1..44000} | xargs -P 16 -n 1 -I{} date -v &quot;-{}d&quot; +&quot;%y%m%d&quot; | xargs -P 16 -I{} sh -c &apos;unzip -qqoP &quot;{}&quot; 0417160508.zip &amp;#x26;&amp;#x26; echo &quot;{}&quot; &amp;#x26;&amp;#x26; exit 255&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;El comando es bastante simple en su funcionamiento, aunque parece complicado.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;echo {1..44000}&lt;/code&gt; genera los números del 1 al 44,000 y los pasa al siguiente paso.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;xargs -P 16 -n 1 -I{} date -v &quot;-{}d&quot; +&quot;%y%m%d&quot;&lt;/code&gt; – toma la entrada del primer paso y genera fechas en el formato &lt;code&gt;YYMMDD&lt;/code&gt; aprovechando la funcionalidad de &lt;code&gt;date&lt;/code&gt; para “aumentar” una fecha con una cantidad específica. En este caso tomaría X días a partir de la fecha actual, donde X es el número generado en el primer paso. De esta forma podemos generar fechas desde hoy hasta los años 1900.&lt;/p&gt;
&lt;p&gt;El tercer paso es el más interesante, así que lo voy a dividir en dos partes:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;xargs -P 16 -I{} sh -c &apos;...&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Esto toma las fechas generadas en el paso 2 y ejecuta un comando sobre cada una de esas fechas. &lt;code&gt;-P 16&lt;/code&gt; aquí significa que va a ejecutar esto en 16 procesos paralelos, para hacer que el procesamiento de los datos sea más rápido. El mismo truco se aplica en el paso 2 para acelerar la generación de fechas.&lt;/p&gt;
&lt;p&gt;La segunda parte es donde ocurren los intentos:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;unzip -qqoP &quot;{}&quot; 0417160508.zip &amp;#x26;&amp;#x26; echo &quot;{}&quot; &amp;#x26;&amp;#x26; exit 255&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Lo que hace esto es primero intentar descomprimir el archivo zip usando la contraseña generada en el paso 2. Si tiene éxito, imprime (&lt;code&gt;echo&lt;/code&gt;) la contraseña adivinada y luego &lt;code&gt;exit 255&lt;/code&gt; obliga a &lt;code&gt;xargs&lt;/code&gt; a detener la ejecución. No queremos seguir adivinando si ya se encontró la contraseña.&lt;/p&gt;
&lt;p&gt;En resumen, fue una muy mala decisión elegir esto como contraseña para el archivo, porque ejecutar el comando anterior toma solo unos 10 segundos para adivinar la contraseña correcta de mi factura. No mucho más para cualquier otra persona.&lt;/p&gt;</content:encoded><category>Uncategorized</category></item><item><title>Cómo lidiar con el código espagueti en PHP: usar funciones anónimas para extraer fragmentos de código de plantillas</title><link>https://bisko.be/es/posts/dev/dealing-with-spaghetti-code-in-php-using-anonymous-functions-to-extract-pieces-of-template-code/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/dealing-with-spaghetti-code-in-php-using-anonymous-functions-to-extract-pieces-of-template-code/</guid><description>Hace poco he estado ayudando a un amigo a entender un código y a modificarlo para que funcione bien con llamadas asíncronas al backend. Un extra sería poder escribirlo de una forma más modular y reutilizable. El código es un plugin de WordPress que fue escrito</description><pubDate>Sat, 08 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hace poco he estado ayudando a un amigo a entender un código y a modificarlo para que funcione bien con llamadas asíncronas al backend. Un extra sería poder escribirlo de una forma más modular y reutilizable.&lt;/p&gt;
&lt;p&gt;El código es un plugin de WordPress escrito con un enfoque muy legacy: una mezcla de lógica de negocio y de frontend en un solo lugar. Manipulaciones de datos hechas en medio del código de la plantilla y cosas por el estilo.&lt;/p&gt;
&lt;p&gt;Básicamente, lo que se veía en los sitios PHP a principios de los 2000, antes de todas esas estupendas incorporaciones de programación orientada a objetos al lenguaje y de un montón de frameworks maduros.&lt;/p&gt;
&lt;p&gt;El caso en el que estuve trabajando hoy era el siguiente: tienes una tabla que muestra un montón de entradas. Necesitábamos añadir varios filtros a la tabla y hacer que funcionara vía AJAX para refrescar rápidamente la interfaz, sin recargar toda la página de administración de WordPress.&lt;/p&gt;
&lt;p&gt;Filtrar los datos fue fácil: un &lt;code&gt;array_filter&lt;/code&gt; rápido en línea para filtrar los datos antes de pasarlos al código de la plantilla (para mantener el mismo estilo de código, esto también estaba en el archivo de la plantilla 😇).&lt;/p&gt;
&lt;p&gt;El problema llegó cuando empezamos a trabajar en hacer que la tabla pudiera renderizarse mediante AJAX.&lt;/p&gt;
&lt;p&gt;Está en medio de un montón de otro código de plantilla y de negocio. Si queríamos limitar la salida vía AJAX solo a la tabla, teníamos pocas formas de lograrlo.&lt;/p&gt;
&lt;p&gt;La primera sería envolver el HTML anterior y posterior a la tabla en ENORMES sentencias &lt;code&gt;if&lt;/code&gt; para asegurarnos de que no se renderice.&lt;/p&gt;
&lt;p&gt;Otra sería extraer la plantilla a un archivo aparte e incluirlo en la acción AJAX que definiríamos en el código del plugin.&lt;/p&gt;
&lt;p&gt;¡Las funciones anónimas al rescate!&lt;/p&gt;
&lt;p&gt;Pensando en cómo resolver esto de la manera más sencilla posible, intenté definir una función normal de PHP que contuviera el código de la tabla, pero había un problema.&lt;/p&gt;
&lt;p&gt;El código se ejecuta dentro de un método de una clase. Hay referencias a &lt;code&gt;$this&lt;/code&gt; en la plantilla de la tabla. Si no queremos cambiar demasiado el código, tendríamos que pasar &lt;code&gt;$this&lt;/code&gt; como argumento de la función. Pues bien, eso no funciona, porque no se puede pasar &lt;code&gt;$this&lt;/code&gt; como parámetro 🤷‍♂️.&lt;/p&gt;
&lt;p&gt;Me decepcionó un poco pensar que tendríamos que reescribir un montón de cosas en la plantilla, y había bastantes referencias que queríamos tener en cuenta para que todo siguiera funcionando.&lt;/p&gt;
&lt;p&gt;Entonces recordé que el sitio no corre en PHP 5.2, sino en PHP 7, así que las funciones anónimas estarían soportadas.&lt;/p&gt;
&lt;p&gt;Y tienen una característica muy útil: puedes definir qué variables del ámbito que hace la llamada se pasarán a la función cuando esta se invoque. Por ejemplo:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$table_function &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ( $pages, $current_page, $items ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // ... Table render code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esto te permite hacer referencia directamente a &lt;code&gt;$pages&lt;/code&gt; o &lt;code&gt;$items&lt;/code&gt; en el cuerpo de la función.&lt;/p&gt;
&lt;p&gt;¡Lo genial es que también puedes pasar &lt;code&gt;$this&lt;/code&gt; como una variable en el ámbito de la función!&lt;/p&gt;
&lt;p&gt;¡Así pudimos extraer por completo el código de renderizado de la tabla a una función separada que podemos llamar de forma aislada durante la llamada AJAX!&lt;/p&gt;
&lt;p&gt;Por supuesto, hay que tener mucho cuidado con las variables usadas dentro del cuerpo de la función, ya que tienen que estar definidas, así que hay que fijarse bien desde dónde se puede llamar a la función AJAX.&lt;/p&gt;
&lt;p&gt;En nuestro caso, esto significará que añadiremos una pequeña acción en la clase principal que seguirá haciendo el &lt;code&gt;require&lt;/code&gt; del archivo, pero tendremos más control para desactivar todas las demás salidas que se producirían.&lt;/p&gt;</content:encoded><category>Snippets</category><category>lambda-functions</category><category>php</category><category>WordPress</category></item><item><title>Arduino/ESP32: Cómo desactivar el watchdog de tareas</title><link>https://bisko.be/es/posts/dev/arduino-esp32-disabling-the-task-watchdog/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/arduino-esp32-disabling-the-task-watchdog/</guid><description>Últimamente he estado jugando con la funcionalidad multinúcleo del ESP32 y con la asignación de tareas a núcleos específicos. Estaba usando AutoConnect para permitir la conexión WiFi sin guardar las credenciales en el código y para ofrecer actualizaciones OTA desde la interfaz web. Era para un proyecto que iba a controlar unos ventiladores PWM de CPU de 4 pines que están en</description><pubDate>Wed, 29 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Últimamente he estado jugando con la funcionalidad multinúcleo del ESP32 y con la asignación de tareas a núcleos específicos.&lt;/p&gt;
&lt;p&gt;Estaba usando &lt;a href=&quot;https://github.com/Hieromon/AutoConnect&quot;&gt;AutoConnect&lt;/a&gt; para permitir la conexión WiFi sin guardar las credenciales en el código y para ofrecer actualizaciones OTA desde la interfaz web.&lt;/p&gt;
&lt;p&gt;Era para un proyecto que iba a controlar unos ventiladores PWM de CPU de 4 pines que tengo en el armario de mi servidor, para enfriar el aire de su interior. Para eso quería dividir la lógica en dos tareas.&lt;/p&gt;
&lt;p&gt;Una tarea para manejar las APIs, la lectura de sensores, las actualizaciones, el WiFi, etc., y otra para manejar la interrupción y el control PWM de los ventiladores.&lt;/p&gt;
&lt;p&gt;Todo iba bien, las tareas se crearon, pero al ejecutar el código, si ponía el ESP32 en el modo en el que hay que conectarse a él para indicarle que se conecte a tu red WiFi, empezaba a mostrar un comportamiento extraño: reiniciaba el controlador cada pocos segundos y no lograba que la conexión funcionara.&lt;/p&gt;
&lt;p&gt;Fue un rato molesto, porque tuve que desmontar el controlador de su lugar y conectarlo por USB para cargarle un firmware nuevo.&lt;/p&gt;
&lt;p&gt;Después de hacer eso noté que el error venía del watchdog:&lt;/p&gt;
&lt;p&gt;Task watchdog got triggered. The following tasks did not feed the watchdog in time:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;IDLE (CPU 1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Tasks currently running:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CPU 0: ManageTask&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CPU 1: ControllerTask&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Investigando un poco, parece que esto lo dispara el Task Watchdog del ESP32, que se encarga de asegurar que las tareas tengan su turno de ejecución en la CPU.&lt;/p&gt;
&lt;p&gt;Además, parecía que AutoConnect no era muy flexible al trabajar dentro del contexto de una tarea, en comparación con ejecutarlo fuera de una, directamente con el resto del código y la funcionalidad.&lt;/p&gt;
&lt;p&gt;Después de buscar un poco y probar distintas cosas, parece que la forma más fácil de salir de esta situación es simplemente desactivar el watchdog y esperar que las cosas se ejecuten a tiempo 🙂 La tarea no es crítica, así que no pasa nada si tarda más en completar lo que está haciendo.&lt;/p&gt;
&lt;p&gt;Para desactivar el watchdog, hay que hacer dos cosas:&lt;/p&gt;
&lt;p&gt;Primero, incluir el encabezado &lt;code&gt;esp_task_wdt.h&lt;/code&gt;. Para eso, agrega &lt;code&gt;#include &amp;#x3C;esp_task_wdt.h&gt;&lt;/code&gt; a tu sección de includes.&lt;/p&gt;
&lt;p&gt;Segundo, hacer que el watchdog no reinicie el controlador y que espere más tiempo. Para ello, hay que añadir &lt;code&gt;esp_task_wdt_init(30, false);&lt;/code&gt; al inicio del código de tu tarea.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; manage_core_task&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;pv_parameters&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    esp_task_wdt_init&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;30&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // .... rest of the code ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esto establecerá el tiempo de espera del watchdog en 30 &lt;strong&gt;SEGUNDOS&lt;/strong&gt; y desactivará el reinicio del controlador si se dispara.&lt;/p&gt;
&lt;p&gt;Si quieres que el controlador se reinicie de todos modos cuando se dispare el watchdog, cambia el segundo parámetro de la llamada a la función de &lt;code&gt;false&lt;/code&gt; a &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;</content:encoded><category>Uncategorized</category><category>arduino</category><category>code</category><category>esp32</category><category>espressif</category><category>multi-core</category><category>multi-threading</category></item><item><title>Configurar una YubiKey para firmar commits de Git</title><link>https://bisko.be/es/posts/dev/setting-up-a-yubikey-for-git-commit-signing/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/setting-up-a-yubikey-for-git-commit-signing/</guid><description>He estado usando una YubiKey como llave de autenticación de dos factores y para firmar mis commits de Git. Hasta hace unos días usaba una YubiKey 4. Como mi computadora actual no tiene ningún puerto USB-A, llevaba un adaptador de USB-A a USB-C de un lado a otro en la mochila, y usar la llave ha sido una molestia durante el</description><pubDate>Wed, 08 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;He estado usando una YubiKey como llave de &lt;a href=&quot;https://es.wikipedia.org/wiki/Autenticaci%C3%B3n_de_m%C3%BAltiples_factores&quot;&gt;autenticación de dos factores&lt;/a&gt; y para firmar mis commits de Git. Hasta hace unos días usaba una YubiKey 4. Como mi computadora actual no tiene ningún puerto USB-A, durante el último año he tenido que andar con adaptadores de USB-A a USB-C en la mochila, y usar la llave ha sido una molestia. Por eso decidí que ya era hora de pasarme a mi YubiKey 5C “nueva” y así no tener que lidiar más con esos adaptadores.&lt;/p&gt;
&lt;p&gt;En realidad ya tenía la llave desde hace tiempo, porque la pedí junto con una YubiKey 4 de reemplazo que conseguí por el problema del &lt;a href=&quot;https://www.yubico.com/2017/10/infineon-rsa-key-generation-issue/&quot;&gt;TPM de Infineon&lt;/a&gt; que ellos identificaron, pero apenas ahora decidí que era el momento de cambiar a la llave nueva.&lt;/p&gt;
&lt;p&gt;La otra razón para hacerlo ahora es que mis viejas llaves GPG caducaron y tuve que volver a emitirlas, así que puedo aprovechar para usar la llave y firmar mis commits de Git.&lt;/p&gt;
&lt;h3 id=&quot;la-locura-de-gpg&quot;&gt;La locura de GPG&lt;/h3&gt;
&lt;p&gt;Si has lidiado con llaves GPG en el pasado (o lo haces actualmente), sabes que puede ser bastante difícil ponerlas en marcha de forma correcta y segura si nunca lo has hecho. Mantener las llaves maestras sin conexión, ir moviendo llaves públicas, reemitir las llaves cuando caducan, quizás firmarlas con llaves anteriores para verificarlas, revocar llaves… No es fácil seguir las mejores prácticas ni siquiera en teoría. Cuando toca llevarlas a la práctica, todo se complica muchísimo.&lt;/p&gt;
&lt;p&gt;La mayoría de los tutoriales en línea te mandan a generar las llaves desde la línea de comandos, navegando menús con un patrón de diseño poco común, y son un auténtico dolor de cabeza para ponerlas a funcionar. Casi todos dicen que uses una máquina sin conexión, como una Raspberry PI, y/o que generes las llaves maestras y las subllaves sin conexión y mantengas la llave maestra fuera de tu computadora.&lt;/p&gt;
&lt;p&gt;🤯&lt;/p&gt;
&lt;p&gt;Llevo casi 4 años lidiando con esto cada inicio de enero. Temo el momento en que aparece el siguiente error, avisándome de que mis llaves de firma han caducado.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;error: gpg failed to sign the data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fatal: failed to write commit object&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este año decidí que era hora de intentar que el proceso fuera mucho más sencillo, usar la generación de certificados integrada y evitarme todo el lío de generar llaves GPG y demás.&lt;/p&gt;
&lt;h3 id=&quot;generación-de-llaves-en-la-yubikey&quot;&gt;Generación de llaves en la YubiKey&lt;/h3&gt;
&lt;p&gt;Con esto todavía tenemos que usar la interfaz de línea de comandos, pero es un poco más evidente porque tiene menos pasos.&lt;/p&gt;
&lt;p&gt;Primero tenemos que configurar nuestra tarjeta/dispositivo.&lt;/p&gt;
&lt;h3 id=&quot;configuración-de-la-tarjetadispositivo&quot;&gt;Configuración de la tarjeta/dispositivo&lt;/h3&gt;
&lt;p&gt;Ejecuta &lt;code&gt;gpg --card-status&lt;/code&gt; en la terminal y deberías ver algo como esto:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Reader ………..: Yubico Yubikey 4 OTP U2F CCID&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Application ID …: &amp;#x3C;Serial number&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Version ……….: 2.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Manufacturer …..: Yubico&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Serial number ….: &amp;#x3C;Device serial number&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Name of cardholder: unspecified&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Language prefs …: en&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Sex …………..: unspecified&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;URL of public key : \[not set\]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Login data …….: &amp;#x3C;e-mail or nothing&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Signature PIN ….: not forced&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Key attributes …: rsa4096 rsa4096 rsa4096&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Max. PIN lengths .: 127 127 127&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;PIN retry counter : 3 0 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Signature counter : 9&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si ves algo parecido y no aparece ningún error, puedes continuar.&lt;/p&gt;
&lt;p&gt;Ejecuta &lt;code&gt;gpg --card-edit&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Llegarás a una interfaz de línea de comandos que muestra la misma información del inicio y, al final, un prompt &lt;code&gt;gpg/card&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Escribe &lt;code&gt;admin&lt;/code&gt; y presiona Enter.&lt;/p&gt;
&lt;p&gt;Ahora puedes escribir &lt;code&gt;help&lt;/code&gt; para saber cómo cambiar la información del titular y los PIN de la tarjeta.&lt;/p&gt;
&lt;p&gt;Algo que &lt;strong&gt;&lt;em&gt;DEBES&lt;/em&gt;&lt;/strong&gt; hacer es cambiar los PIN predeterminados de la tarjeta. Hay dos. El primero es el PIN que usas para acceder a las llaves de la tarjeta. El segundo es el PIN de “Admin” o PUK (Personal Unblocking Code, código personal de desbloqueo).&lt;/p&gt;
&lt;p&gt;Para hacerlo, escribe &lt;code&gt;passwd&lt;/code&gt; y verás un menú:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg/card&gt; passwd  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg: OpenPGP card no. &amp;#x3C;Serial number&gt; detected&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1 - change PIN  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2 - unblock PIN  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3 - change Admin PIN  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4 - set the Reset Code  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Q - quit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Escribe &lt;code&gt;1&lt;/code&gt; y sigue las indicaciones para actualizar tu PIN. El PIN predeterminado es &lt;code&gt;123456&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Cuando termines, elige &lt;code&gt;3&lt;/code&gt; para cambiar el PIN de Admin. El predeterminado es &lt;code&gt;12345678&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Una vez cambiados ambos PIN, vuelve al menú principal para terminar de actualizar la información de la llave.&lt;/p&gt;
&lt;p&gt;Los comandos que puedes usar son:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; – para cambiar el nombre del titular&lt;/li&gt;
&lt;li&gt;&lt;code&gt;login&lt;/code&gt; – para cambiar el correo electrónico o el login del titular&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Después de eso podemos pasar a la generación de las llaves propiamente dicha.&lt;/p&gt;
&lt;h3 id=&quot;generación-de-llaves&quot;&gt;Generación de llaves&lt;/h3&gt;
&lt;p&gt;Ahora sí toca generar las llaves.&lt;/p&gt;
&lt;p&gt;Para ello, escribe &lt;code&gt;generate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Se te preguntará &lt;code&gt;Make off-card backup of encryption key? (Y/n)&lt;/code&gt;, pero, como se menciona en la documentación de YubiKey, esto es solo un respaldo parcial y no creará una copia completa de las llaves secretas, así que da igual si respondes &lt;code&gt;Yes&lt;/code&gt; o &lt;code&gt;No&lt;/code&gt;. Yo respondo &lt;code&gt;No(n)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Si ya hay llaves guardadas en la tarjeta, se te preguntará si quieres reemplazarlas:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg: Note: keys are already stored on the card!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Replace existing keys? (y/N)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Asegúrate de no reemplazar algo importante de lo que no tengas respaldo y continúa con &lt;code&gt;Y&lt;/code&gt;. Si hay algo importante, es mejor abandonar todo el proceso con &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Se te pedirá el PIN de la tarjeta que configuraste antes.&lt;/p&gt;
&lt;p&gt;Luego tendrás que elegir durante cuánto tiempo serán válidas las llaves:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Please specify how long the key should be valid.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        0 = key does not expire&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;n&gt; = key expires in n days&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     &amp;#x3C;n&gt;w = key expires in n weeks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     &amp;#x3C;n&gt;m = key expires in n months&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     &amp;#x3C;n&gt;y = key expires in n years&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; Key is valid for? (0)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si quieres que las llaves sean válidas solo por 1 año, escribe &lt;code&gt;1y&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Se te pedirá que confirmes la fecha de caducidad. Revisa que todo esté bien y confirma.&lt;/p&gt;
&lt;p&gt;Después se te pedirá que ingreses la información del propietario de la llave:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Is this correct? (y/N) y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GnuPG needs to construct a user ID to identify your key.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Real name: &amp;#x3C;Your name&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Email address: &amp;#x3C;Your e-mail&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Comment: &amp;#x3C;Comment is optional&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; You selected this USER-ID:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     &quot;&amp;#x3C;Your name&gt; &amp;#x3C;comment&gt; &amp;#x3C;your e-mail&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Type `O` to confirm if everything seems correct.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Luego se te pedirá el PIN de Admin o PUK. Es necesario porque se realizará una operación de escritura en la tarjeta.&lt;/p&gt;
&lt;p&gt;Si todo está bien, la YubiKey empezará a parpadear y seguirá así un buen rato (en mi caso, unos cinco minutos).&lt;/p&gt;
&lt;p&gt;Cuando termine verás el siguiente mensaje:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg: key &amp;#x3C;Short ID&gt; marked as ultimately trusted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg: revocation certificate stored as &apos;/Users/bisko/.gnupg/openpgp-revocs.d/&amp;#x3C;LONG ID&gt;.rev&apos; public and secret key created and signed.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anota el &lt;code&gt;&amp;#x3C;Short ID&gt;&lt;/code&gt;, porque lo usaremos más adelante.&lt;/p&gt;
&lt;p&gt;En este punto puedes salir del prompt de GPG con &lt;code&gt;quit&lt;/code&gt; o &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ahora toca guardar la llave pública, para poder reutilizar la llave en otra computadora.&lt;/p&gt;
&lt;p&gt;Para ello, ejecuta el siguiente comando:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --armor&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.asc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esto exportará la llave pública a un archivo llamado &lt;code&gt;&amp;#x3C;Short ID&gt;.asc&lt;/code&gt;. Lo usarás en el futuro si quieres poder usar la YubiKey en otra computadora o si pierdes tu configuración de GPG.&lt;/p&gt;
&lt;p&gt;Para restaurar la llave pública en tu llavero, tienes que hacer lo siguiente:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --import&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.asc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; public&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Your Name &amp;#x3C;your@email&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; imported&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Total&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; processed:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; imported:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Una nota muy importante.&lt;/em&gt;&lt;/strong&gt; Si se trata de una instalación nueva de GPG o de otra computadora, GPG no reconocerá por sí solo las llaves que están en la tarjeta. Necesitas ejecutar &lt;code&gt;gpg --card-status&lt;/code&gt; para que lea la tarjeta y detecte las llaves que contiene.&lt;/p&gt;
&lt;h3 id=&quot;configurar-git-para-firmar&quot;&gt;Configurar Git para firmar&lt;/h3&gt;
&lt;p&gt;Para configurar Git de modo que firme tus commits, debes agregar lo siguiente a tu &lt;code&gt;~/.gitconfig&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;En la sección &lt;code&gt;[user]&lt;/code&gt;, tienes que agregar (o actualizar) la siguiente línea:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;signingkey = 0x&amp;#x3C;Short ID&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Luego, si quieres firmar todos tus commits, agrega lo siguiente al final del archivo:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;[gpg]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  program&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; = /usr/local/bin/gpg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;[commit]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  gpgsign&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; = true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con esto Git firmará todos tus commits.&lt;/p&gt;
&lt;p&gt;Para configurar GitHub, tienes que agregar tu llave pública a tu perfil. Para ello, ve a &lt;code&gt;GitHub -&gt; Settings -&gt; SSH and GPG keys -&gt; New GPG key&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Verás un cuadro de texto donde debes pegar todo el contenido del archivo &lt;code&gt;&amp;#x3C;Short ID&gt;.asc&lt;/code&gt; que generamos antes.&lt;/p&gt;
&lt;p&gt;Si quieres copiar el contenido del archivo (en macOS) puedes hacer lo siguiente:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cat &amp;#x3C;Short ID&gt;.asc | pbcopy&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Esto copiará el contenido completo del archivo al portapapeles.&lt;/p&gt;
&lt;p&gt;Pega eso en el cuadro de texto de GitHub y haz clic en el botón &lt;code&gt;Add GPG key&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Después de eso puedes intentar hacer un commit y un push a GitHub, y deberías ver la bonita etiqueta &lt;code&gt;Verified&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;depuración&quot;&gt;Depuración&lt;/h3&gt;
&lt;p&gt;A veces las cosas no funcionan a la primera. Para reunir más información puedes probar lo siguiente:&lt;/p&gt;
&lt;h3 id=&quot;firmar-con-gpg&quot;&gt;Firmar con GPG&lt;/h3&gt;
&lt;p&gt;Primero intenta firmar algo con la llave:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;gpg -s &amp;#x3C;file&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Si esto falla, intenta averiguar por qué; yo no soy ningún experto como para ayudarte con eso.&lt;/p&gt;
&lt;p&gt;Revisa la salida de Git con GIT_TRACE=1 git commit -am “test”&lt;/p&gt;
&lt;p&gt;Esto te mostrará exactamente lo que hace Git mientras intenta hacer el commit y firmarlo. Te dará una pista de por qué podría estar fallando la firma.&lt;/p&gt;
&lt;p&gt;De ahí también puedes ver que Git ejecuta el siguiente comando:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;/usr/local/bin/gpg&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --status-fd=2&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -bsau&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puedes intentar ejecutarlo manualmente para ver si te da un error más útil.&lt;/p&gt;</content:encoded><category>Snippets</category><category>Technology</category><category>git</category><category>github</category><category>gpg</category><category>key</category><category>public-key</category><category>secrets</category><category>sign</category><category>yubikey</category></item><item><title>e1000e  eth0: Detected Hardware Unit Hang</title><link>https://bisko.be/es/posts/dev/e1000e-device-id-eth0-detected-hardware-unit-hang/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/e1000e-device-id-eth0-detected-hardware-unit-hang/</guid><description>Hace poco mi servidor doméstico y host de máquinas virtuales empezó a perder la conectividad de red de forma aleatoria. Desde fuera parecía que seguía funcionando, pero no podía acceder a él de ninguna forma remota. Según el switch, el adaptador ethernet parecía estar encendido, así que el problema debía estar en algún lugar del software. No</description><pubDate>Wed, 18 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hace poco mi servidor doméstico y host de máquinas virtuales empezó a perder la conectividad de red de forma aleatoria. Desde fuera parecía que seguía funcionando, pero no podía acceder a él de ninguna forma remota. Según el switch, el adaptador ethernet parecía estar encendido, así que el problema debía estar en algún lugar del software.&lt;/p&gt;
&lt;p&gt;No sería la primera vez que un driver es el culpable de que se cuelgue una conexión de red. En el pasado me han fastidiado unos drivers de WiFi con errores, tanto en equipos con Linux como con Windows.&lt;/p&gt;
&lt;p&gt;Después de escarbar un poco en los registros del sistema, me topé con lo siguiente:&lt;/p&gt;
&lt;p&gt;vmhost kernel: e1000e 0000:00:1f.6 eth0: Detected Hardware Unit Hang:&lt;br&gt;
vmhost kernel:   TDH                  &amp;#x3C;0&gt;&lt;br&gt;
vmhost kernel:   TDT                  &amp;#x3C;1&gt;&lt;br&gt;
vmhost kernel:   next_to_use          &amp;#x3C;1&gt;&lt;br&gt;
vmhost kernel:   next_to_clean        &amp;#x3C;0&gt;&lt;br&gt;
vmhost kernel: buffer_info[next_to_clean]:&lt;br&gt;
vmhost kernel:   time_stamp           &amp;#x3C;10fbc2f81&gt;&lt;br&gt;
vmhost kernel:   next_to_watch        &amp;#x3C;0&gt;&lt;br&gt;
vmhost kernel:   jiffies              &amp;#x3C;10fbc3871&gt;&lt;br&gt;
vmhost kernel:   next_to_watch.status &amp;#x3C;0&gt;&lt;br&gt;
vmhost kernel: MAC Status             &amp;#x3C;40080083&gt;&lt;br&gt;
vmhost kernel: PHY Status             &amp;#x3C;796d&gt;&lt;br&gt;
vmhost kernel: PHY 1000BASE-T Status  &amp;#x3C;7800&gt;&lt;br&gt;
vmhost kernel: PHY Extended Status    &amp;#x3C;3000&gt;&lt;br&gt;
vmhost kernel: PCI Status             &amp;#x3C;10&gt;&lt;br&gt;
vmhost kernel: e1000e 0000:00:1f.6 eth0: Reset adapter unexpectedly&lt;br&gt;
vmhost kernel: vmbr0: port 1(eth0) entered disabled state&lt;/p&gt;
&lt;p&gt;Luego reiniciaba el adaptador de red y, al rato, volvía a hacerlo, hasta que la máquina se quedaba completamente sin conexión.&lt;/p&gt;
&lt;p&gt;Buscando respuestas en línea, encontré &lt;a href=&quot;https://serverfault.com/questions/616485/e1000e-reset-adapter-unexpectedly-detected-hardware-unit-hang&quot;&gt;este hilo de ServerFault&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Causa&lt;/p&gt;
&lt;p&gt;Leyendo distintas fuentes y listas de reportes de errores, parece que la mejor manera de reproducir el problema es someter al dispositivo a un alto consumo de ancho de banda, por ejemplo transmitiendo grandes cantidades de datos que saturen la interfaz.&lt;/p&gt;
&lt;p&gt;En mi caso solía ocurrir cuando transmito contenido multimedia desde el servidor Plex local a algún dispositivo. Por la forma en que está configurada la red, la máquina virtual con Windows que ejecuta Plex tiene que obtener el archivo multimedia de un recurso compartido NFS en otro dispositivo, transcodificarlo en la VM y luego enviarlo al dispositivo de reproducción.&lt;/p&gt;
&lt;p&gt;Todo esto suma mucho tráfico de red, normalmente entre 40 y 100 Mbit/s, dependiendo del dispositivo que reproduce el archivo y de la calidad del archivo original.&lt;/p&gt;
&lt;p&gt;El mismo problema se manifestó al transmitir juegos con Steam Link a nuestro Apple TV. La conexión es por cable, pero no es raro que la red se caiga. Creo que está relacionado con el mismo problema, pero estaré pendiente para ver si vuelve a ocurrir después de la solución.&lt;/p&gt;
&lt;p&gt;Posible solución&lt;/p&gt;
&lt;p&gt;Parece que una posible solución es desactivar &lt;strong&gt;GSO&lt;/strong&gt; &lt;em&gt;(Generic Segmentation Offload)&lt;/em&gt;, &lt;strong&gt;TSO&lt;/strong&gt; &lt;em&gt;(TCP Segmentation Offload)&lt;/em&gt; y &lt;strong&gt;GRO&lt;/strong&gt; &lt;em&gt;(Generic Receive Offload)&lt;/em&gt; en la interfaz de red*:&lt;/p&gt;
&lt;p&gt;ethtool -K eth0 gso off gro off tso off&lt;/p&gt;
&lt;p&gt;Ya lo apliqué en mi configuración y estoy esperando para ver si de verdad resuelve el problema a largo plazo.&lt;/p&gt;
&lt;p&gt;Notas al pie&lt;/p&gt;
&lt;p&gt;* Estas opciones sirven para delegar la segmentación de paquetes al controlador de la interfaz de red y así reducir el uso de CPU de la máquina. Pueden encontrar más detalles en &lt;a href=&quot;https://en.wikipedia.org/wiki/Large_send_offload&quot;&gt;este artículo de Wikipedia&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>Homelab</category><category>bandwidth</category><category>bug</category><category>network</category><category>nic</category><category>plex</category><category>proxmox</category><category>steam</category><category>streaming</category></item><item><title>Snippets: cómo ejecutar un registro de imágenes Docker local/privado en un swarm</title><link>https://bisko.be/es/posts/dev/snippets-running-a-local-private-swarm-docker-image-registry/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/snippets-running-a-local-private-swarm-docker-image-registry/</guid><description>Hoy intentaba generar una imagen de Docker a partir de un proyecto de Node.js y quería desplegarla en mi swarm local de pruebas. Por desgracia, si se construye una imagen de Docker local, esta se almacena y es accesible solo en el nodo donde fue creada, y los demás nodos del swarm no pueden acceder a ella. La</description><pubDate>Fri, 11 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hoy intentaba generar una imagen de Docker a partir de un proyecto de Node.js y quería desplegarla en mi swarm local de pruebas.&lt;/p&gt;
&lt;p&gt;Por desgracia, si se construye una imagen de Docker local, esta se almacena y es accesible solo en el nodo donde fue creada, y los demás nodos del swarm no pueden acceder a ella.&lt;/p&gt;
&lt;p&gt;La forma de solucionarlo es subir la imagen a un registro de Docker (Docker registry) desde el cual todos los nodos del swarm puedan descargarla.&lt;/p&gt;
&lt;p&gt;Creando el registro&lt;/p&gt;
&lt;p&gt;Para instanciar un servicio de registro en el swarm, ejecuten:&lt;/p&gt;
&lt;p&gt;docker service create —name registry —publish 5000:5000 registry:latest&lt;/p&gt;
&lt;p&gt;Esto creará el servicio de registro y lo ejecutará en el swarm, haciéndolo accesible para todos los nodos del swarm.&lt;/p&gt;
&lt;p&gt;Subiendo una imagen al registro&lt;/p&gt;
&lt;p&gt;Para subir una imagen al registro hay que hacer dos cosas: etiquetar la imagen y luego subirla.&lt;/p&gt;
&lt;p&gt;Aquí tienen un ejemplo de cómo construir y etiquetar la imagen de una sola vez:&lt;/p&gt;
&lt;p&gt;docker build . -t localhost.localdomain:5000/&lt;image-name&gt;:latest&lt;/image-name&gt;&lt;/p&gt;
&lt;p&gt;Y así se sube al registro:&lt;/p&gt;
&lt;p&gt;docker push localhost.localdomain:5000/&lt;image-name&gt;:latestUsando la imagen en un servicio/contenedor/etc.&lt;/image-name&gt;&lt;/p&gt;
&lt;p&gt;Para usar la imagen en un servicio o un contenedor, se especifica la ruta completa de arriba, es decir, &lt;code&gt;localhost.localdomain:5000/&amp;#x3C;image-name&gt;:latest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Los nodos del swarm tendrán acceso a la imagen y podrán descargarla y ejecutarla.&lt;/p&gt;
&lt;p&gt;Nota importante sobre la dirección del registro&lt;/p&gt;
&lt;p&gt;En la mayoría de los tutoriales en línea, la URL del registro aparece como &lt;code&gt;localhost:5000&lt;/code&gt;. Por desgracia, esto no funciona y normalmente se agota el tiempo de espera al intentar usarla. ¡Si la cambiamos por &lt;code&gt;localhost.localdomain&lt;/code&gt;, funcionará!&lt;/p&gt;
&lt;p&gt;No sé por qué ocurre esto, si tanto &lt;code&gt;localhost&lt;/code&gt; como &lt;code&gt;localhost.localdomain&lt;/code&gt; se resuelven a &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;</content:encoded><category>Homelab</category><category>Snippets</category><category>Technology</category><category>docker</category><category>docker-swarm</category><category>registry</category><category>swarm</category></item><item><title>Snippets: Reasignar la tecla del signo de sección del teclado de Apple a la comilla invertida ( § a ` )</title><link>https://bisko.be/es/posts/dev/snippets-map-apple-keyboard-section-sign-key-to-back-quote-%C2%A7-to/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/snippets-map-apple-keyboard-section-sign-key-to-back-quote-%C2%A7-to/</guid><description>Llevo un tiempo usando teclados de Apple, pero cuando vives en Europa no es fácil conseguir los de distribución estadounidense a los que estoy acostumbrado. En macOS hay una forma &quot;fácil&quot; de reasignar el teclado con un simple comando de terminal: watch -n1 -x hidutil property -m &apos;{&quot;ProductID&quot;:592, &quot;VendorID&quot;:1452}&apos; --set &apos;{&quot;UserKeyMapping&quot;:[{&quot;HIDKeyboardModifierMappingSrc&quot;:0x700000035,&quot;HIDKeyboardModifierMappingDst&quot;:0x700000064}]}&apos; Lo que hace este</description><pubDate>Wed, 31 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Llevo un tiempo usando teclados de Apple, pero cuando vives en Europa no es fácil conseguir los de distribución estadounidense a los que estoy acostumbrado.&lt;/p&gt;
&lt;p&gt;En macOS hay una forma “fácil” de reasignar el teclado con un simple comando de terminal:&lt;/p&gt;
&lt;p&gt;watch -n1 -x hidutil property -m ’{“ProductID”:592, “VendorID”:1452}’ —set ’{“UserKeyMapping”:[{“HIDKeyboardModifierMappingSrc”:0x700000035,“HIDKeyboardModifierMappingDst”:0x700000064}]}’&lt;/p&gt;
&lt;p&gt;Lo que hace este fragmento de código es cambiar la asignación de la tecla de sección ( § ) para que escriba la tecla de comilla invertida/tilde ( &lt;code&gt;`/ ~&lt;/code&gt; ).&lt;/p&gt;
&lt;p&gt;La parte &lt;code&gt;watch -n1 -x&lt;/code&gt; lo repite cada segundo, porque a veces macOS pierde la configuración, normalmente cuando el equipo entra en suspensión o cuando el teclado se desconecta y se vuelve a conectar.&lt;/p&gt;
&lt;p&gt;Ten en cuenta que en algunas versiones de macOS la utilidad &lt;code&gt;watch&lt;/code&gt; no viene instalada y hay que obtenerla con algo como &lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; o &lt;a href=&quot;https://www.macports.org/&quot;&gt;MacPorts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;El código anterior funciona con el teclado externo de Apple con USB y distribución europea con letras búlgaras. Puede que haga falta un pequeño ajuste en los valores hexadecimales de las teclas para otros tipos de teclados.&lt;/p&gt;
&lt;p&gt;Una variación del comando funciona bien para el mismo tipo de teclado en la propia MacBook Pro. Solo tienes que intercambiar la posición de los valores &lt;code&gt;0x700000035&lt;/code&gt; y &lt;code&gt;0x700000064&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;He pensado en escribir una herramienta que haga esto automáticamente por mí, pero a estas alturas creo que vale más la pena invertir en un teclado nuevo con la distribución correcta 🙂&lt;/p&gt;</content:encoded><category>Uncategorized</category></item><item><title>Cómo reiniciar el control remoto del Apple TV</title><link>https://bisko.be/es/posts/dev/resetting-restarting-apple-tv-remote/</link><guid isPermaLink="true">https://bisko.be/es/posts/dev/resetting-restarting-apple-tv-remote/</guid><description>Anoche, durante la visita de unos invitados, logré sumergir nuestro control remoto del Apple TV bajo el agua. La falta de sueño y los vasos inestables dan como resultado accidentes. El control funcionaba bien anoche, pero hoy dejó de funcionar. No responde a ninguna acción. Intenté golpearlo contra mi mano y sacudirle el agua. No sirvió de nada.</description><pubDate>Sun, 17 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Anoche, durante la visita de unos invitados, logré sumergir nuestro control remoto del Apple TV bajo el agua. La falta de sueño y los vasos inestables dan como resultado accidentes.&lt;/p&gt;
&lt;p&gt;El control funcionaba bien anoche, pero hoy dejó de funcionar. No responde a ninguna acción. Intenté golpearlo contra mi mano y sacudirle el agua. No sirvió de nada.&lt;/p&gt;
&lt;p&gt;Pensamos que ya estaba perdido y empezamos a buscar en las alacenas la bolsa de arroz.&lt;/p&gt;
&lt;p&gt;Entré a internet, más que nada en broma, para ver si podía intentar “reiniciarlo”.&lt;/p&gt;
&lt;p&gt;Una guía muy útil me indicó mantener presionados los botones de Subir volumen y Menú durante 2-3 segundos para restablecer el control y ponerlo en modo de emparejamiento.&lt;/p&gt;
&lt;p&gt;¡Me sorprendí muchísimo cuando &lt;strong&gt;de verdad&lt;/strong&gt; &lt;strong&gt;funcionó&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;Así que, si algo parece sospechoso, intenten primero restablecer el control. Si no funciona, entonces pueden probar con el arroz y la intervención de hardware.&lt;/p&gt;
&lt;p&gt;TL;DR;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mantengan presionados Menú + Subir volumen durante 2-3 segundos&lt;/li&gt;
&lt;li&gt;Esperen unos segundos&lt;/li&gt;
&lt;li&gt;Intenten interactuar con el Apple TV&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Apple</category><category>Technology</category><category>AppleTV</category><category>Have you tried turning it off and on again?</category><category>remote</category><category>reset</category><category>restart</category></item></channel></rss>