<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Luks on inetshell</title><link>https://inet.sh/es/tags/luks/</link><description>Recent content in Luks on inetshell</description><generator>Hugo</generator><language>es</language><lastBuildDate>Mon, 05 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/es/tags/luks/index.xml" rel="self" type="application/rss+xml"/><item><title>Servidor con LUKS que se cuelga y no vuelve: watchdog, desbloqueo por SSH con dropbear y netconsole</title><link>https://inet.sh/es/posts/linux/luks-desbloqueo-ssh-dropbear-watchdog-netconsole/</link><pubDate>Mon, 05 Oct 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/es/posts/linux/luks-desbloqueo-ssh-dropbear-watchdog-netconsole/</guid><description>Un host Ubuntu con el root cifrado con LUKS se congela y se queda horas muerto: no hay watchdog, kdump se atora pidiendo la passphrase y cualquier reinicio se para en el prompt. Watchdog de hardware (iTCO) con systemd, panic y lockups que reinician, dropbear-initramfs para escribir la passphrase por SSH y netconsole para que el log del cuelgue sobreviva en otra máquina.</description><content:encoded><![CDATA[<h2 id="en-corto">En corto</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># 1. Watchdog de hardware: si el kernel se congela, el chipset reinicia</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> iTCO_wdt &gt; /etc/modules-load.d/iTCO_wdt.conf
</span></span><span class="line"><span class="cl">mkdir -p /etc/systemd/system.conf.d
</span></span><span class="line"><span class="cl"><span class="nb">printf</span> <span class="s1">&#39;[Manager]\nRuntimeWatchdogSec=60s\nRebootWatchdogSec=5min\n&#39;</span> &gt; /etc/systemd/system.conf.d/90-watchdog.conf
</span></span><span class="line"><span class="cl"><span class="nb">printf</span> <span class="s1">&#39;kernel.panic = 10\nkernel.softlockup_panic = 1\nkernel.hardlockup_panic = 1\nkernel.printk = 5 4 1 7\n&#39;</span> &gt; /etc/sysctl.d/90-crash-recovery.conf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. SSH en el initramfs para escribir la passphrase de LUKS</span>
</span></span><span class="line"><span class="cl">apt install dropbear-initramfs
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;DROPBEAR_OPTIONS=&#34;-p 2222 -s -j -k -I 300 -c cryptroot-unlock&#34;&#39;</span> &gt; /etc/dropbear/initramfs/dropbear.conf
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;IP=192.168.1.20::192.168.1.1:255.255.255.0:server1:eno1:off&#39;</span> &gt; /etc/initramfs-tools/conf.d/90-remote-unlock
</span></span><span class="line"><span class="cl"><span class="c1"># llaves públicas en /etc/dropbear/initramfs/authorized_keys</span>
</span></span><span class="line"><span class="cl">update-initramfs -u
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Después de un reinicio, desde la LAN</span>
</span></span><span class="line"><span class="cl">ssh -p <span class="m">2222</span> root@192.168.1.20      <span class="c1"># pide la passphrase y el arranque sigue</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Y netconsole hacia otra máquina para saber <strong>por qué</strong> se cayó (abajo).</p>
<h2 id="el-problema">El problema</h2>
<p>Un host con el root cifrado con LUKS (solo passphrase, sin TPM) se congela a media mañana. No hay corte de luz, el journal se corta a mitad de una línea, sin errores. Nadie se entera hasta que alguien llega y lo prende a mano: seis horas abajo.</p>
<p>Tres cosas alargan la caída y borran la evidencia:</p>
<table>
  <thead>
      <tr>
          <th>Problema</th>
          <th>Consecuencia</th>
          <th>Arreglo</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Sin watchdog</td>
          <td>Un kernel congelado espera a un humano</td>
          <td>Watchdog de hardware alimentado por systemd</td>
      </tr>
      <tr>
          <td>kdump activo (default de Ubuntu) con <code>kernel.panic=0</code></td>
          <td>El kernel de captura necesita la passphrase para escribir en <code>/var/crash</code>, arranca sin USB ni red y espera para siempre</td>
          <td>Apagar kdump, <code>kernel.panic=10</code></td>
      </tr>
      <tr>
          <td>Root con LUKS solo con passphrase</td>
          <td>Cualquier reinicio sin nadie enfrente se queda en el prompt</td>
          <td>dropbear en el initramfs</td>
      </tr>
      <tr>
          <td>Un congelamiento duro no deja nada en el journal local</td>
          <td>No hay forma de saber la causa</td>
          <td>netconsole a otra máquina</td>
      </tr>
  </tbody>
</table>
<h2 id="1-watchdog-de-hardware-y-reinicio-en-panic">1. Watchdog de hardware y reinicio en panic</h2>
<p>En equipos Intel el watchdog del chipset es <code>iTCO_wdt</code>. Ubuntu pone en lista negra todos los drivers de watchdog, pero eso solo evita la carga automática; cargarlo por nombre funciona:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">echo</span> iTCO_wdt &gt; /etc/modules-load.d/iTCO_wdt.conf
</span></span><span class="line"><span class="cl">modprobe iTCO_wdt
</span></span><span class="line"><span class="cl">cat /sys/class/watchdog/watchdog0/identity      <span class="c1"># iTCO_wdt</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>systemd lo alimenta. Si PID 1 deja de hacerlo durante 60 s (kernel congelado), el chipset reinicia la máquina. <code>RebootWatchdogSec</code> acota además los apagados que se quedan colgados:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># /etc/systemd/system.conf.d/90-watchdog.conf</span>
</span></span><span class="line"><span class="cl"><span class="k">[Manager]</span>
</span></span><span class="line"><span class="cl"><span class="na">RuntimeWatchdogSec</span><span class="o">=</span><span class="s">60s</span>
</span></span><span class="line"><span class="cl"><span class="na">RebootWatchdogSec</span><span class="o">=</span><span class="s">5min</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">systemctl daemon-reexec
</span></span><span class="line"><span class="cl">systemctl show -p WatchdogDevice -p RuntimeWatchdogUSec
</span></span><span class="line"><span class="cl"><span class="c1"># WatchdogDevice=/dev/watchdog0  RuntimeWatchdogUSec=1min</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Para que los panics y lockups reinicien en vez de quedarse colgados:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># /etc/sysctl.d/90-crash-recovery.conf</span>
</span></span><span class="line"><span class="cl"><span class="na">kernel.panic</span> <span class="o">=</span> <span class="s">10</span>
</span></span><span class="line"><span class="cl"><span class="na">kernel.softlockup_panic</span> <span class="o">=</span> <span class="s">1</span>
</span></span><span class="line"><span class="cl"><span class="na">kernel.hardlockup_panic</span> <span class="o">=</span> <span class="s">1</span>
</span></span><span class="line"><span class="cl"><span class="na">kernel.printk</span> <span class="o">=</span> <span class="s">5 4 1 7</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>kernel.printk = 5 …</code> sube el nivel de consola para que los warnings también salgan por netconsole (paso 3). <code>hardlockup_panic</code> necesita el NMI watchdog, que viene encendido por defecto.</p>
<p><strong>kdump</strong>: con el root cifrado y sin desbloqueo automático, su kernel de captura no puede escribir el dump y se queda en el prompt de la passphrase. Apágalo:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sed -i <span class="s1">&#39;s/^USE_KDUMP=.*/USE_KDUMP=0/&#39;</span> /etc/default/kdump-tools
</span></span><span class="line"><span class="cl">systemctl disable --now kdump-tools
</span></span><span class="line"><span class="cl">kdump-config unload
</span></span></code></pre></td></tr></table>
</div>
</div><p>Y recupera la memoria que reserva. <code>kdump-tools.cfg</code> agrega <code>crashkernel=</code> desde <code>/etc/default/grub.d/</code>, y esos archivos se leen <strong>en orden alfabético</strong>: un archivo que lo quite tiene que ordenarse después de él (<code>zz-</code>, no <code>99-</code>):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cat &gt; /etc/default/grub.d/zz-no-crashkernel.cfg <span class="s">&lt;&lt;&#39;EOF&#39;
</span></span></span><span class="line"><span class="cl"><span class="s">GRUB_CMDLINE_LINUX_DEFAULT=&#34;$(printf &#39;%s&#39; &#34;$GRUB_CMDLINE_LINUX_DEFAULT&#34; | sed -E &#39;s/(^| )crashkernel=[^ ]*//g; s/^ +//&#39;)&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="cl">update-grub
</span></span><span class="line"><span class="cl">grep -c crashkernel /boot/grub/grub.cfg     <span class="c1"># 0</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Así una actualización del paquete no lo regresa.</p>
<h2 id="2-desbloquear-luks-por-ssh-con-dropbear">2. Desbloquear LUKS por SSH con dropbear</h2>
<p><code>dropbear-initramfs</code> mete un servidor SSH mínimo en el initramfs, que arranca <strong>antes</strong> de abrir el disco:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">apt install dropbear-initramfs
</span></span></code></pre></td></tr></table>
</div>
</div><p>Opciones en <code>/etc/dropbear/initramfs/dropbear.conf</code> (en versiones viejas de Debian/Ubuntu el archivo es <code>/etc/dropbear-initramfs/config</code>):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">DROPBEAR_OPTIONS</span><span class="o">=</span><span class="s2">&#34;-p 2222 -s -j -k -I 300 -c cryptroot-unlock&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">IFDOWN</span><span class="o">=</span><span class="s2">&#34;*&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><table>
  <thead>
      <tr>
          <th>Opción</th>
          <th>Para qué</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>-p 2222</code></td>
          <td>Otro puerto que el sshd real: el host key es distinto y así <code>known_hosts</code> no choca</td>
      </tr>
      <tr>
          <td><code>-s</code></td>
          <td>Sin passwords, solo llaves</td>
      </tr>
      <tr>
          <td><code>-j -k</code></td>
          <td>Sin port forwarding</td>
      </tr>
      <tr>
          <td><code>-I 300</code></td>
          <td>Corta sesiones inactivas a los 5 min</td>
      </tr>
      <tr>
          <td><code>-c cryptroot-unlock</code></td>
          <td>Cualquier login corre <code>cryptroot-unlock</code> y nada más</td>
      </tr>
      <tr>
          <td><code>IFDOWN=&quot;*&quot;</code></td>
          <td>Al terminar, baja y limpia la NIC para que el sistema real la configure desde cero (importante si encima va un bridge)</td>
      </tr>
  </tbody>
</table>
<p>Llaves públicas con restricciones, una por línea:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># /etc/dropbear/initramfs/authorized_keys  (modo 0600)</span>
</span></span><span class="line"><span class="cl">no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA... laptop
</span></span><span class="line"><span class="cl">no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA... phone
</span></span></code></pre></td></tr></table>
</div>
</div><p>Red del initramfs con IP fija, sin tocar GRUB (sintaxis de <code>ip=</code> del kernel: <code>cliente::gateway:máscara:hostname:interfaz:autoconf</code>):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># /etc/initramfs-tools/conf.d/90-remote-unlock</span>
</span></span><span class="line"><span class="cl"><span class="nv">IP</span><span class="o">=</span>192.168.1.20::192.168.1.1:255.255.255.0:server1:eno1:off
</span></span></code></pre></td></tr></table>
</div>
</div><p>Usa la NIC física. Si el sistema tiene un bridge (<code>br0</code>), en el initramfs todavía no existe: pon la interfaz que es su uplink.</p>
<p>Reconstruye y comprueba que todo quedó adentro:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">update-initramfs -u
</span></span><span class="line"><span class="cl">lsinitramfs /boot/initrd.img-<span class="k">$(</span>uname -r<span class="k">)</span> <span class="p">|</span> grep -E <span class="s1">&#39;sbin/dropbear$|cryptroot-unlock$|90-remote-unlock|authorized_keys&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="desbloquear">Desbloquear</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh -p <span class="m">2222</span> root@192.168.1.20
</span></span><span class="line"><span class="cl"><span class="c1"># Please unlock disk dm_crypt-0:</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Escribes la passphrase, la sesión se cierra sola y el arranque sigue. El teclado local sigue funcionando en paralelo: gana el primero que conteste. La primera vez compara el host key con los de <code>/etc/dropbear/initramfs/dropbear_*_host_key</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">dropbearkey -y -f /etc/dropbear/initramfs/dropbear_ed25519_host_key <span class="p">|</span> grep -o <span class="s1">&#39;SHA256:.*&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="3-netconsole-que-el-log-del-cuelgue-sobreviva">3. netconsole: que el log del cuelgue sobreviva</h2>
<p>Un congelamiento duro no alcanza a escribir nada en disco. netconsole manda los mensajes del kernel por UDP a otra máquina, desde el propio kernel (netpoll), así que llegan aunque el userspace ya esté muerto.</p>
<p>En el host, por configfs y <strong>después</strong> de que exista la interfaz. La opción del módulo (<code>netconsole=…</code> en la línea de comandos) corre antes de que NetworkManager arme un bridge y no sirve:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">modprobe netconsole
</span></span><span class="line"><span class="cl">mountpoint -q /sys/kernel/config <span class="o">||</span> mount -t configfs none /sys/kernel/config
</span></span><span class="line"><span class="cl"><span class="nv">T</span><span class="o">=</span>/sys/kernel/config/netconsole/log-host
</span></span><span class="line"><span class="cl">mkdir <span class="nv">$T</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> br0          &gt; <span class="nv">$T</span>/dev_name
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> 192.168.1.20 &gt; <span class="nv">$T</span>/local_ip
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> 192.168.1.30 &gt; <span class="nv">$T</span>/remote_ip
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="m">6666</span>         &gt; <span class="nv">$T</span>/remote_port
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="m">1</span>            &gt; <span class="nv">$T</span>/enabled
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;&lt;4&gt;netconsole: </span><span class="k">$(</span>hostname<span class="k">)</span><span class="s2"> streaming&#34;</span> &gt; /dev/kmsg
</span></span></code></pre></td></tr></table>
</div>
</div><p>Mételo en un servicio <code>oneshot</code> con <code>After=</code> y <code>BindsTo=</code> sobre <code>sys-subsystem-net-devices-br0.device</code>, para que se arme en cada arranque. Si el bridge se recrea con el sistema arriba, el target no se rearma solo: reinicia el servicio.</p>
<p>En la otra máquina (física, en el mismo segmento: una VM sobre el mismo host se cae con él), un receptor con <code>socat</code> que solo acepta a ese host y escribe al journal:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># /etc/systemd/system/netconsole-server1.service</span>
</span></span><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">netconsole receiver for server1</span>
</span></span><span class="line"><span class="cl"><span class="na">After</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl"><span class="na">Wants</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/socat -u UDP-RECV:6666,bind=192.168.1.30,range=192.168.1.20/32 STDOUT</span>
</span></span><span class="line"><span class="cl"><span class="na">StandardOutput</span><span class="o">=</span><span class="s">journal</span>
</span></span><span class="line"><span class="cl"><span class="na">SyslogIdentifier</span><span class="o">=</span><span class="s">netconsole-server1</span>
</span></span><span class="line"><span class="cl"><span class="na">Restart</span><span class="o">=</span><span class="s">always</span>
</span></span><span class="line"><span class="cl"><span class="na">DynamicUser</span><span class="o">=</span><span class="s">yes</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Install]</span>
</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">multi-user.target</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">journalctl -t netconsole-server1 --since <span class="s2">&#34;-12h&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>La línea <code>netconsole: … streaming</code> marca cada arranque; lo que está antes de la última marca es el final del arranque anterior. Busca <code>Kernel panic</code>, <code>soft lockup</code>, <code>hard LOCKUP</code>, <code>NVRM: Xid</code> o errores AER de PCIe/Thunderbolt.</p>
<h2 id="probarlo">Probarlo</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cat /sys/class/watchdog/watchdog0/<span class="o">{</span>identity,state,timeout<span class="o">}</span>   <span class="c1"># iTCO_wdt active 60</span>
</span></span><span class="line"><span class="cl">cat /sys/kernel/kexec_crash_loaded                           <span class="c1"># 0: kdump apagado</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;&lt;4&gt;netconsole test </span><span class="k">$(</span>date +%T<span class="k">)</span><span class="s2">&#34;</span> &gt; /dev/kmsg            <span class="c1"># aparece en el receptor</span>
</span></span><span class="line"><span class="cl">systemctl reboot                                             <span class="c1"># y desbloquea por SSH</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>La prueba completa, <strong>solo con alguien listo para desbloquear</strong>: <code>echo c &gt; /proc/sysrq-trigger</code> provoca un panic. El texto debe llegar al receptor, el host reinicia a los 10 s y espera en el prompt de LUKS.</p>
<h2 id="trampas">Trampas</h2>
<ul>
<li><strong>El kernel anterior no trae dropbear.</strong> <code>update-initramfs -u</code> solo reconstruye el initramfs del kernel en uso. Si arrancas el kernel anterior desde GRUB (la salida normal cuando uno nuevo falla), su initramfs puede ser de antes de instalar dropbear: ahí solo sirve el teclado. Revisa con <code>lsinitramfs</code> o reconstrúyelo con <code>update-initramfs -u -k &lt;versión&gt;</code>.</li>
<li><strong>Solo desde la LAN.</strong> En el initramfs no hay VPN ni Tailscale. Para desbloquear desde fuera, anuncia la IP del host (<code>/32</code>) desde un subnet router que viva en <strong>otra</strong> máquina de la LAN.</li>
<li><strong>&ldquo;Host key changed&rdquo;.</strong> El puerto 2222 tiene llaves distintas del sshd normal; la entrada de <code>known_hosts</code> es <code>[192.168.1.20]:2222</code>.</li>
<li><strong>La NIC equivocada.</strong> Si el cable está en un adaptador USB o en un dock y la IP está en la integrada, el initramfs no tiene red. En la consola local busca <code>IP-Config: eno1 …</code>.</li>
<li><strong>netconsole no aparece en <code>tcpdump</code> del propio host</strong>: netpoll se salta los puntos de captura. Mira en el receptor.</li>
<li><strong>Solo salen los mensajes con nivel menor a <code>console_loglevel</code></strong>: con <code>5</code> salen warnings y peores, nunca <code>info</code> ni <code>debug</code>.</li>
<li><strong>Falsos positivos del watchdog</strong>: si systemd se queda 60 s sin alimentarlo, reinicia y vuelves a escribir la passphrase. Para depurar el kernel, quita el <code>.conf</code> y <code>systemctl daemon-reexec</code>.</li>
<li><strong>TPM2/clevis</strong> desbloquearía solo, pero también para cualquiera que se lleve el equipo y lo prenda. Aquí se prefirió la passphrase por SSH.</li>
</ul>
<h2 id="ver-también">Ver también</h2>
<ul>
<li><a href="/es/posts/linux/luks-respaldar-header-llaves/">LUKS: respaldar y restaurar el header, y añadir, cambiar o quitar llaves</a></li>
<li><a href="/es/posts/linux/ssh-agent-systemd/">ssh-agent persistente con systemd</a></li>
</ul>
]]></content:encoded></item><item><title>LUKS: respaldar y restaurar el header, y añadir, cambiar o quitar llaves</title><link>https://inet.sh/es/posts/linux/luks-respaldar-header-llaves/</link><pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/es/posts/linux/luks-respaldar-header-llaves/</guid><description>Los comandos de cryptsetup que evitan perder un disco cifrado con LUKS: luksHeaderBackup y luksHeaderRestore, luksAddKey, luksChangeKey, luksRemoveKey y luksKillSlot, luksDump para ver los slots, keyfiles en crypttab. Y el aviso que casi nadie lee: un backup del header resucita las passphrases que ya quitaste.</description><content:encoded><![CDATA[<h2 id="en-corto">En corto</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Respaldo del header (no hace falta abrir el volumen)</span>
</span></span><span class="line"><span class="cl">cryptsetup luksHeaderBackup /dev/sdb --header-backup-file /root/sdb-luks-header.img
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Slots en uso</span>
</span></span><span class="line"><span class="cl">cryptsetup luksDump /dev/sdb
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Llaves</span>
</span></span><span class="line"><span class="cl">cryptsetup luksAddKey /dev/sdb                      <span class="c1"># otra passphrase (pide una existente)</span>
</span></span><span class="line"><span class="cl">cryptsetup luksAddKey /dev/sdb /root/keyfile.bin    <span class="c1"># un keyfile en un slot nuevo</span>
</span></span><span class="line"><span class="cl">cryptsetup luksChangeKey /dev/sdb                   <span class="c1"># sustituir una passphrase por otra, mismo slot</span>
</span></span><span class="line"><span class="cl">cryptsetup luksRemoveKey /dev/sdb                   <span class="c1"># quitar la passphrase que escribas</span>
</span></span><span class="line"><span class="cl">cryptsetup luksKillSlot /dev/sdb <span class="m">2</span>                  <span class="c1"># quitar el slot 2 sin conocer su passphrase (pide otra)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Restaurar el header</span>
</span></span><span class="line"><span class="cl">cryptsetup luksHeaderRestore /dev/sdb --header-backup-file /root/sdb-luks-header.img
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>Después de cualquier cambio de llaves, respalda el header otra vez y destruye el backup anterior.</strong> Restaurar un backup viejo reactiva todas las passphrases que tenía en ese momento, incluidas las que quitaste.</p>
<h2 id="por-qué-el-header-lo-es-todo">Por qué el header lo es todo</h2>
<p>LUKS no cifra los datos con tu passphrase: los cifra con una <strong>master key</strong> aleatoria, y esa master key va cifrada dentro del header, una copia por slot, cada una protegida por una passphrase o keyfile. El header ocupa los primeros <strong>16 MiB</strong> del dispositivo en LUKS2 (2 MiB en LUKS1).</p>
<p>Si esos megabytes se dañan (un <code>dd</code> equivocado, un <code>mkfs</code> sobre el disco crudo, sectores muertos justo ahí), la master key desaparece y los datos se vuelven ruido irrecuperable, aunque sepas la passphrase. Un backup del header, guardado en otro sitio, es la única póliza. El archivo pesa lo mismo que el header y <strong>no contiene datos</strong>, solo la master key cifrada.</p>
<h2 id="respaldar-y-restaurar">Respaldar y restaurar</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cryptsetup luksHeaderBackup /dev/sdb --header-backup-file /root/sdb-luks-header.img
</span></span><span class="line"><span class="cl">cryptsetup luksDump /root/sdb-luks-header.img         <span class="c1"># el backup se puede inspeccionar como un dispositivo</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Guárdalo fuera de la máquina, cifrado (un <code>gpg -c</code> basta) y con nombre que diga de qué disco es. Restaurar:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cryptsetup luksHeaderRestore /dev/sdb --header-backup-file /root/sdb-luks-header.img
</span></span></code></pre></td></tr></table>
</div>
</div><p>Solo repara el header. Si el accidente pasó más allá de los primeros 16 MiB, lo que hubiera ahí sigue perdido.</p>
<h2 id="slots-y-llaves">Slots y llaves</h2>
<p>LUKS2 tiene 32 slots (0–31); LUKS1, 8. Todos abren la misma master key, así que da igual con cuál entres:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cryptsetup luksDump /dev/sdb <span class="p">|</span> sed -n <span class="s1">&#39;/^Keyslots:/,/^Tokens:/p&#39;</span>
</span></span><span class="line"><span class="cl">cryptsetup open --test-passphrase -v /dev/sdb         <span class="c1"># &#34;Key slot 0 unlocked&#34;: qué slot usa tu passphrase</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Keyfile para abrir sin teclear (típico en un disco de datos que arranca con el sistema):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">dd <span class="k">if</span><span class="o">=</span>/dev/urandom <span class="nv">bs</span><span class="o">=</span><span class="m">32</span> <span class="nv">count</span><span class="o">=</span><span class="m">1</span> <span class="nv">of</span><span class="o">=</span>/root/keyfile.bin
</span></span><span class="line"><span class="cl">chmod <span class="m">0400</span> /root/keyfile.bin
</span></span><span class="line"><span class="cl">cryptsetup luksAddKey /dev/sdb /root/keyfile.bin
</span></span></code></pre></td></tr></table>
</div>
</div><p>Y en <code>/etc/crypttab</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">datos  UUID=&lt;uuid-del-luks&gt;  /root/keyfile.bin  luks
</span></span></code></pre></td></tr></table>
</div>
</div><p>Prueba sin reiniciar: <code>systemctl daemon-reload &amp;&amp; systemctl start systemd-cryptsetup@datos.service</code>. El keyfile en <code>/root</code> solo tiene sentido si <code>/</code> también está cifrado o el disco es de una máquina que confías físicamente.</p>
<p>Quitar llaves:</p>
<ul>
<li><code>luksRemoveKey</code> pide la passphrase que quieres eliminar.</li>
<li><code>luksKillSlot &lt;n&gt;</code> elimina un slot concreto y pide <strong>otra</strong> passphrase válida; sirve cuando la del slot ya nadie la recuerda.</li>
<li><code>luksChangeKey</code> cambia una passphrase por otra en el mismo slot.</li>
</ul>
<h2 id="trampas">Trampas</h2>
<ul>
<li><strong>El backup del header conserva las llaves viejas.</strong> Quitaste la passphrase de un ex compañero y el backup de antes sigue en un USB: ese USB abre el disco. Re-respalda tras cada cambio y destruye los anteriores (<code>shred -u</code>).</li>
<li><strong><code>luksErase</code> no es borrado seguro si existe un backup del header.</strong> Destruye todos los slots del disco, pero un <code>luksHeaderRestore</code> los devuelve. Para un borrado definitivo, además, destruye los backups.</li>
<li><strong>No mates el último slot.</strong> <code>cryptsetup</code> avisa de que el dispositivo quedará inutilizable; si aceptas, ya está.</li>
<li><strong>El backup en el mismo disco no es backup.</strong> Ni en el mismo servidor si lo que temes es perder el servidor.</li>
<li><strong>Header separado</strong> (<code>--header /ruta/header.img</code> al formatear y al abrir) mueve el header fuera del disco por completo; entonces el &ldquo;backup&rdquo; es el propio header y perderlo equivale a perder los datos.</li>
<li><strong>LUKS1 vs LUKS2</strong>: los comandos son los mismos; <code>cryptsetup convert --type luks2</code> migra un LUKS1 (con backup antes, claro).</li>
</ul>
<p>Referencia: <code>man 8 cryptsetup</code>, secciones <em>luksHeaderBackup</em>, <em>luksHeaderRestore</em> y <em>luksKillSlot</em>.</p>
]]></content:encoded></item></channel></rss>