En corto

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 1. Watchdog de hardware: si el kernel se congela, el chipset reinicia
echo iTCO_wdt > /etc/modules-load.d/iTCO_wdt.conf
mkdir -p /etc/systemd/system.conf.d
printf '[Manager]\nRuntimeWatchdogSec=60s\nRebootWatchdogSec=5min\n' > /etc/systemd/system.conf.d/90-watchdog.conf
printf 'kernel.panic = 10\nkernel.softlockup_panic = 1\nkernel.hardlockup_panic = 1\nkernel.printk = 5 4 1 7\n' > /etc/sysctl.d/90-crash-recovery.conf

# 2. SSH en el initramfs para escribir la passphrase de LUKS
apt install dropbear-initramfs
echo 'DROPBEAR_OPTIONS="-p 2222 -s -j -k -I 300 -c cryptroot-unlock"' > /etc/dropbear/initramfs/dropbear.conf
echo 'IP=192.168.1.20::192.168.1.1:255.255.255.0:server1:eno1:off' > /etc/initramfs-tools/conf.d/90-remote-unlock
# llaves públicas en /etc/dropbear/initramfs/authorized_keys
update-initramfs -u

# 3. Después de un reinicio, desde la LAN
ssh -p 2222 root@192.168.1.20      # pide la passphrase y el arranque sigue

Y netconsole hacia otra máquina para saber por qué se cayó (abajo).

El problema

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.

Tres cosas alargan la caída y borran la evidencia:

ProblemaConsecuenciaArreglo
Sin watchdogUn kernel congelado espera a un humanoWatchdog de hardware alimentado por systemd
kdump activo (default de Ubuntu) con kernel.panic=0El kernel de captura necesita la passphrase para escribir en /var/crash, arranca sin USB ni red y espera para siempreApagar kdump, kernel.panic=10
Root con LUKS solo con passphraseCualquier reinicio sin nadie enfrente se queda en el promptdropbear en el initramfs
Un congelamiento duro no deja nada en el journal localNo hay forma de saber la causanetconsole a otra máquina

1. Watchdog de hardware y reinicio en panic

En equipos Intel el watchdog del chipset es iTCO_wdt. Ubuntu pone en lista negra todos los drivers de watchdog, pero eso solo evita la carga automática; cargarlo por nombre funciona:

1
2
3
echo iTCO_wdt > /etc/modules-load.d/iTCO_wdt.conf
modprobe iTCO_wdt
cat /sys/class/watchdog/watchdog0/identity      # iTCO_wdt

systemd lo alimenta. Si PID 1 deja de hacerlo durante 60 s (kernel congelado), el chipset reinicia la máquina. RebootWatchdogSec acota además los apagados que se quedan colgados:

1
2
3
4
# /etc/systemd/system.conf.d/90-watchdog.conf
[Manager]
RuntimeWatchdogSec=60s
RebootWatchdogSec=5min
1
2
3
systemctl daemon-reexec
systemctl show -p WatchdogDevice -p RuntimeWatchdogUSec
# WatchdogDevice=/dev/watchdog0  RuntimeWatchdogUSec=1min

Para que los panics y lockups reinicien en vez de quedarse colgados:

1
2
3
4
5
# /etc/sysctl.d/90-crash-recovery.conf
kernel.panic = 10
kernel.softlockup_panic = 1
kernel.hardlockup_panic = 1
kernel.printk = 5 4 1 7

kernel.printk = 5 … sube el nivel de consola para que los warnings también salgan por netconsole (paso 3). hardlockup_panic necesita el NMI watchdog, que viene encendido por defecto.

kdump: 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:

1
2
3
sed -i 's/^USE_KDUMP=.*/USE_KDUMP=0/' /etc/default/kdump-tools
systemctl disable --now kdump-tools
kdump-config unload

Y recupera la memoria que reserva. kdump-tools.cfg agrega crashkernel= desde /etc/default/grub.d/, y esos archivos se leen en orden alfabético: un archivo que lo quite tiene que ordenarse después de él (zz-, no 99-):

1
2
3
4
5
cat > /etc/default/grub.d/zz-no-crashkernel.cfg <<'EOF'
GRUB_CMDLINE_LINUX_DEFAULT="$(printf '%s' "$GRUB_CMDLINE_LINUX_DEFAULT" | sed -E 's/(^| )crashkernel=[^ ]*//g; s/^ +//')"
EOF
update-grub
grep -c crashkernel /boot/grub/grub.cfg     # 0

Así una actualización del paquete no lo regresa.

2. Desbloquear LUKS por SSH con dropbear

dropbear-initramfs mete un servidor SSH mínimo en el initramfs, que arranca antes de abrir el disco:

1
apt install dropbear-initramfs

Opciones en /etc/dropbear/initramfs/dropbear.conf (en versiones viejas de Debian/Ubuntu el archivo es /etc/dropbear-initramfs/config):

1
2
DROPBEAR_OPTIONS="-p 2222 -s -j -k -I 300 -c cryptroot-unlock"
IFDOWN="*"
OpciónPara qué
-p 2222Otro puerto que el sshd real: el host key es distinto y así known_hosts no choca
-sSin passwords, solo llaves
-j -kSin port forwarding
-I 300Corta sesiones inactivas a los 5 min
-c cryptroot-unlockCualquier login corre cryptroot-unlock y nada más
IFDOWN="*"Al terminar, baja y limpia la NIC para que el sistema real la configure desde cero (importante si encima va un bridge)

Llaves públicas con restricciones, una por línea:

1
2
3
# /etc/dropbear/initramfs/authorized_keys  (modo 0600)
no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA... laptop
no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA... phone

Red del initramfs con IP fija, sin tocar GRUB (sintaxis de ip= del kernel: cliente::gateway:máscara:hostname:interfaz:autoconf):

1
2
# /etc/initramfs-tools/conf.d/90-remote-unlock
IP=192.168.1.20::192.168.1.1:255.255.255.0:server1:eno1:off

Usa la NIC física. Si el sistema tiene un bridge (br0), en el initramfs todavía no existe: pon la interfaz que es su uplink.

Reconstruye y comprueba que todo quedó adentro:

1
2
update-initramfs -u
lsinitramfs /boot/initrd.img-$(uname -r) | grep -E 'sbin/dropbear$|cryptroot-unlock$|90-remote-unlock|authorized_keys'

Desbloquear

1
2
ssh -p 2222 root@192.168.1.20
# Please unlock disk dm_crypt-0:

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 /etc/dropbear/initramfs/dropbear_*_host_key:

1
dropbearkey -y -f /etc/dropbear/initramfs/dropbear_ed25519_host_key | grep -o 'SHA256:.*'

3. netconsole: que el log del cuelgue sobreviva

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.

En el host, por configfs y después de que exista la interfaz. La opción del módulo (netconsole=… en la línea de comandos) corre antes de que NetworkManager arme un bridge y no sirve:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
modprobe netconsole
mountpoint -q /sys/kernel/config || mount -t configfs none /sys/kernel/config
T=/sys/kernel/config/netconsole/log-host
mkdir $T
echo br0          > $T/dev_name
echo 192.168.1.20 > $T/local_ip
echo 192.168.1.30 > $T/remote_ip
echo 6666         > $T/remote_port
echo 1            > $T/enabled
echo "<4>netconsole: $(hostname) streaming" > /dev/kmsg

Mételo en un servicio oneshot con After= y BindsTo= sobre sys-subsystem-net-devices-br0.device, 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.

En la otra máquina (física, en el mismo segmento: una VM sobre el mismo host se cae con él), un receptor con socat que solo acepta a ese host y escribe al journal:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# /etc/systemd/system/netconsole-server1.service
[Unit]
Description=netconsole receiver for server1
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/socat -u UDP-RECV:6666,bind=192.168.1.30,range=192.168.1.20/32 STDOUT
StandardOutput=journal
SyslogIdentifier=netconsole-server1
Restart=always
DynamicUser=yes

[Install]
WantedBy=multi-user.target
1
journalctl -t netconsole-server1 --since "-12h"

La línea netconsole: … streaming marca cada arranque; lo que está antes de la última marca es el final del arranque anterior. Busca Kernel panic, soft lockup, hard LOCKUP, NVRM: Xid o errores AER de PCIe/Thunderbolt.

Probarlo

1
2
3
4
cat /sys/class/watchdog/watchdog0/{identity,state,timeout}   # iTCO_wdt active 60
cat /sys/kernel/kexec_crash_loaded                           # 0: kdump apagado
echo "<4>netconsole test $(date +%T)" > /dev/kmsg            # aparece en el receptor
systemctl reboot                                             # y desbloquea por SSH

La prueba completa, solo con alguien listo para desbloquear: echo c > /proc/sysrq-trigger provoca un panic. El texto debe llegar al receptor, el host reinicia a los 10 s y espera en el prompt de LUKS.

Trampas

  • El kernel anterior no trae dropbear. update-initramfs -u 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 lsinitramfs o reconstrúyelo con update-initramfs -u -k <versión>.
  • Solo desde la LAN. En el initramfs no hay VPN ni Tailscale. Para desbloquear desde fuera, anuncia la IP del host (/32) desde un subnet router que viva en otra máquina de la LAN.
  • “Host key changed”. El puerto 2222 tiene llaves distintas del sshd normal; la entrada de known_hosts es [192.168.1.20]:2222.
  • La NIC equivocada. 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 IP-Config: eno1 ….
  • netconsole no aparece en tcpdump del propio host: netpoll se salta los puntos de captura. Mira en el receptor.
  • Solo salen los mensajes con nivel menor a console_loglevel: con 5 salen warnings y peores, nunca info ni debug.
  • Falsos positivos del watchdog: si systemd se queda 60 s sin alimentarlo, reinicia y vuelves a escribir la passphrase. Para depurar el kernel, quita el .conf y systemctl daemon-reexec.
  • TPM2/clevis desbloquearía solo, pero también para cualquiera que se lleve el equipo y lo prenda. Aquí se prefirió la passphrase por SSH.

Ver también