Saltar al contenido principal
Solución de problemas 10 min de lectura

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.

Reparar sshd roto por actualización desatendida en Ubuntu 24.04 (marzo 2026)

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:

  1. Lee el puerto SSH de sshd_config (no asume el puerto 22)
  2. Detecta si el servidor está afectado (estado dpkg roto y/o ssh.socket fallido)
  3. Crea /run/sshd si no existe
  4. Encuentra y termina el proceso maestro sshd huérfano
  5. Espera a que el puerto quede libre
  6. Reinicia e inicia ssh.socket
  7. Ejecuta dpkg --configure para corregir el estado del paquete
  8. Verifica que todo funciona
  9. 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.

Monitorea tus propios servidores

Rastrea CPU, memoria, disco y procesos en hasta 2 servidores gratis. La configuración lleva unos 5 minutos.

Comenzar gratis

Artículos relacionados