Reparar sshd roto por actualización desatendida en Ubuntu 24.04 (marzo 2026)
Cómo reparar openssh-server roto tras unattended-upgrades en Ubuntu 24.04. Cubre el directorio /run/sshd faltante, el estado half-configured de dpkg, ssh.socket fallido y un script de reparación automatizado completo.
El 16 de marzo de 2026, una actualización defectuosa de openssh-server (1:9.6p1-3ubuntu13.14 a .15) rompió SSH en servidores Ubuntu 24.04 que ejecutaban unattended-upgrades. La causa raíz parece ser un directorio /run/sshd faltante que provocó que el script postinst fallara, dejando dpkg a medio configurar y ssh.socket en estado de fallo. La solución es recrear el directorio, matar el proceso sshd huérfano, reiniciar systemd y reconfigurar el paquete.
TL;DR - Solución rápida (ejecutar como root)
mkdir -p /run/sshd && chmod 0755 /run/sshd
systemctl reset-failed ssh.socket ssh.service && systemctl start ssh.socket
dpkg --configure openssh-server
Si el proceso sshd antiguo está bloqueando el puerto, termínalo primero: kill $(pgrep -f '/usr/sbin/sshd' | xargs -I{} sh -c 'ps -o ppid= -p {} | tr -d " "' | grep '^1$'). Sigue leyendo para el script automatizado completo.
Qué salió mal
La actualización del paquete openssh-server de 1:9.6p1-3ubuntu13.14 a 1:9.6p1-3ubuntu13.15 parece haber sido distribuida con un script de mantenimiento postinst defectuoso. Cuando unattended-upgrades se ejecutó durante la noche e intentó configurar el nuevo paquete, el script falló. Esto dejó dpkg en un estado a medio configurar, el ssh.socket de systemd en estado de fallo y el proceso maestro sshd antiguo huérfano -- todavía en ejecución, pero sin ser gestionado por systemd.
El servidor permanece accesible solo mientras ese proceso sshd huérfano siga ejecutándose. Si muere o el servidor se reinicia, el acceso SSH se pierde por completo. En Laravel Forge, los servidores afectados aparecen como "Disconnected" ya que Forge ya no puede comunicarse con ellos por SSH.
Cómo lo diagnosticamos
En uno de nuestros sistemas afectados, esto es lo que encontramos al investigar.
dpkg: error processing package openssh-server
Comprobando el estado del paquete:
$ dpkg -s openssh-server | grep Status
Status: install ok half-configured
Esto confirma que el paquete está atascado a mitad de la actualización. Revisando qué hizo unattended-upgrades:
$ cat /var/log/unattended-upgrades/unattended-upgrades-dpkg.log
...
Setting up openssh-server (1:9.6p1-3ubuntu13.15) ...
dpkg: error processing package openssh-server (--configure):
installed openssh-server package post-installation script subprocess returned error exit status 1
ssh.socket: Failed with result 'service-start-limit-hit'
Comprobando systemd:
$ systemctl status ssh.socket
Active: failed (Result: service-start-limit-hit)
$ journalctl -u ssh.socket
ssh.socket: Failed with result 'service-start-limit-hit'.
Failed to start ssh.socket - OpenBSD Secure Shell server socket.
$ journalctl -u ssh.service
ssh.service: Failed with result 'exit-code'.
La activación por socket está completamente rota. Pero aún podíamos conectarnos por SSH, lo que significaba que algo seguía escuchando. Comprobando el proceso huérfano:
$ ps -eo pid,ppid,cmd | grep sshd
1234 1 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
PPID 1 -- ese es el proceso maestro sshd antiguo, huérfano después de que systemd perdiera el rastro de él durante la actualización fallida. Sigue aceptando conexiones, por eso SSH todavía funciona, pero está funcionando con tiempo prestado.
fatal: Missing privilege separation directory: /run/sshd
El archivo auth.log reveló lo que parece ser la causa raíz:
$ grep sshd /var/log/auth.log
2026-03-18T18:20:19.201354+00:00 sshd[3115043]: fatal: Missing privilege separation directory: /run/sshd
El directorio de tiempo de ejecución efectivamente no existía:
$ ls -la /run/sshd
ls: cannot access '/run/sshd': No such file or directory
Este directorio faltante parece ser lo que causó que el script postinst fallara, ya que sshd lo necesita para la separación de privilegios.
También encontramos un informe de fallo:
$ ls /var/crash/
openssh-server.0.crash
Errores que verás
Dependiendo de dónde mires, puedes encontrar algunos o todos estos:
En la salida de dpkg / apt
dpkg: error processing package openssh-server (--configure):
installed openssh-server package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
openssh-server
E: Sub-process /usr/bin/dpkg returned an error code (1)
Al ejecutar apt upgrade o apt install
You might want to run 'apt --fix-broken install' to correct these.
The following packages have unmet dependencies:
openssh-server : Depends: openssh-sftp-server but it is not going to be installed
Estado del paquete
Status: install ok half-configured
Errores de Systemd
ssh.socket: Failed with result 'service-start-limit-hit'.
ssh.service: Failed with result 'exit-code'.
Auth log
sshd[3115043]: fatal: Missing privilege separation directory: /run/sshd
Cómo solucionarlo
Escribimos un script que automatiza toda la solución. Es seguro ejecutarlo en servidores que NO están afectados -- detecta el estado roto primero y sale limpiamente si todo está bien. Esto facilita ejecutarlo en todos tus servidores sin preocuparte por cuáles están realmente rotos.
El script:
- Lee el puerto SSH de sshd_config (no asume el puerto 22)
- Detecta si el servidor está afectado (estado dpkg roto y/o ssh.socket fallido)
- Crea
/run/sshdsi no existe - Encuentra y termina el proceso maestro sshd huérfano
- Espera a que el puerto quede libre
- Reinicia e inicia
ssh.socket - Ejecuta
dpkg --configurepara corregir el estado del paquete - Verifica que todo funciona
- Elimina el archivo de fallo
Aqui está el script completo:
#!/usr/bin/env bash
set -euo pipefail
# Fix broken openssh-server upgrade on Ubuntu 24.04
# Safe to run on unaffected servers -- exits cleanly.
if [[ $EUID -ne 0 ]]; then
echo "ERROR: Must run as root." >&2
exit 1
fi
echo "=== openssh-server upgrade fix ==="
# -- 0. Determine SSH port from sshd_config --
SSH_PORT=22
if [[ -f /etc/ssh/sshd_config ]]; then
configured_port=$(grep -Ei '^\s*Port\s+' /etc/ssh/sshd_config | awk '{print $2}' | tail -1)
if [[ -n "${configured_port:-}" ]]; then
SSH_PORT="$configured_port"
fi
fi
echo "[*] Using SSH port: $SSH_PORT"
# -- 1. Detect if affected --
broken_dpkg=false
failed_socket=false
if dpkg -s openssh-server 2>/dev/null | grep -qE 'Status:.*(half-configured|half-installed|unpacked|triggers-awaited|triggers-pending)'; then
broken_dpkg=true
echo "[!] openssh-server dpkg state is broken."
fi
if systemctl is-failed --quiet ssh.socket 2>/dev/null; then
failed_socket=true
echo "[!] ssh.socket is in failed state."
fi
if ! $broken_dpkg && ! $failed_socket; then
echo "[OK] Server is not affected. openssh-server package and ssh.socket are healthy."
exit 0
fi
echo ""
echo "Server is affected. Proceeding with fix..."
echo ""
# -- 2. Create /run/sshd if missing --
if [[ ! -d /run/sshd ]]; then
echo "[*] Creating /run/sshd ..."
mkdir -p /run/sshd
chmod 0755 /run/sshd
else
echo "[OK] /run/sshd already exists."
fi
# -- 3. Kill orphaned sshd master process --
orphan_pids=()
while IFS= read -r pid; do
[[ -z "$pid" ]] && continue
ppid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ') || continue
if [[ "$ppid" == "1" ]]; then
orphan_pids+=("$pid")
fi
done < <(pgrep -f '/usr/sbin/sshd' 2>/dev/null || true)
if [[ ${#orphan_pids[@]} -gt 0 ]]; then
echo "[*] Found orphaned sshd master process(es): ${orphan_pids[*]}"
for pid in "${orphan_pids[@]}"; do
echo " Killing PID $pid ..."
kill "$pid" 2>/dev/null || true
done
else
echo "[OK] No orphaned sshd master processes found."
fi
# -- 4. Wait for port to free --
echo "[*] Waiting for port $SSH_PORT to free ..."
for i in $(seq 1 30); do
if ! ss -tlnp | grep -q ":${SSH_PORT} "; then
echo " Port $SSH_PORT is free."
break
fi
if [[ $i -eq 30 ]]; then
echo "WARNING: Port $SSH_PORT still in use after 30s. Continuing anyway."
fi
sleep 1
done
# -- 5. Reset and start ssh.socket --
echo "[*] Resetting ssh.socket ..."
systemctl reset-failed ssh.socket 2>/dev/null || true
systemctl reset-failed ssh.service 2>/dev/null || true
echo "[*] Starting ssh.socket ..."
systemctl start ssh.socket
# -- 6. Fix dpkg state --
if $broken_dpkg; then
echo "[*] Running dpkg --configure openssh-server ..."
dpkg --configure openssh-server
fi
# -- 7. Verify --
echo ""
echo "=== Verification ==="
errors=0
if systemctl is-active --quiet ssh.socket; then
echo "[OK] ssh.socket is active."
else
echo "[FAIL] ssh.socket is NOT active."
errors=$((errors + 1))
fi
if ss -tlnp | grep -q ":${SSH_PORT} "; then
echo "[OK] Port $SSH_PORT is listening."
else
echo "[FAIL] Port $SSH_PORT is NOT listening."
errors=$((errors + 1))
fi
status=$(dpkg -s openssh-server 2>/dev/null | grep '^Status:' || echo "not found")
if echo "$status" | grep -q 'install ok installed'; then
echo "[OK] dpkg state is clean: $status"
else
echo "[FAIL] dpkg state is not clean: $status"
errors=$((errors + 1))
fi
# -- 8. Clean up crash file --
if [[ -f /var/crash/openssh-server.0.crash ]]; then
echo "[*] Removing /var/crash/openssh-server.0.crash ..."
rm -f /var/crash/openssh-server.0.crash
fi
echo ""
if [[ $errors -eq 0 ]]; then
echo "=== All checks passed. Server is fixed. ==="
else
echo "=== $errors check(s) failed. Manual investigation needed. ==="
exit 1
fi
Para ejecutarlo en un solo servidor:
sudo bash fix-openssh-upgrade.sh
Para ejecutarlo en varios servidores a la vez:
for server in server1 server2 server3; do
echo "--- $server ---"
ssh root@$server 'bash -s' < fix-openssh-upgrade.sh
done
Cómo prevenir esto en el futuro
Si quieres evitar que unattended-upgrades toque openssh-server en el futuro, puedes incluirlo en la lista negra. Edita /etc/apt/apt.conf.d/50unattended-upgrades y añade a la sección Package-Blacklist:
Unattended-Upgrade::Package-Blacklist {
"openssh-server";
};
Esto garantiza que los paquetes relacionados con SSH solo se actualicen cuando lo hagas manualmente y puedas supervisar el proceso.
Si estás completamente bloqueado
Si el proceso sshd huérfano ya ha muerto o el servidor se ha reiniciado, no podrás conectarte por SSH. En ese caso, usa el acceso a la consola de tu proveedor de hosting para ejecutar la solución:
- DigitalOcean: Droplet Console (basada en web)
- AWS: EC2 Serial Console o Session Manager
- Hetzner: Consola VNC en la Cloud Console
- Cualquier proveedor: Acceso VNC o KVM desde tu panel de control
Resumen
La actualización de openssh-server del 16 de marzo de 2026 en Ubuntu 24.04 rompió SSH en servidores que ejecutaban unattended-upgrades. Los síntomas son un estado dpkg a medio configurar, un ssh.socket fallido, un error "fatal: Missing privilege separation directory: /run/sshd" en auth.log, un proceso sshd huérfano y servidores que aparecen como "Disconnected" en Laravel Forge. La solución implica recrear /run/sshd, terminar el proceso huérfano, reiniciar las unidades de systemd y reconfigurar el paquete. El script anterior automatiza todo el proceso y es seguro de ejecutar en servidores no afectados.