Son las seis de la mañana en el SOC. Llega un informe de exposición del servidor de la Clínica Sonrisa, la clínica dental, que abre a las ocho: desde Internet se puede entrar por SSH con contraseña, root también, hay un servicio escuchando que nadie usa y dos paquetes de seguridad sin poner. Nadie ha entrado todavía, pero los barridos que prueban contraseñas no paran. Tienes dos horas para cerrar la puerta sin dejar a la clínica sin su agenda de citas.
Endurecer (en inglés, hardening) es eso: quitar lo que sobra y cerrar lo que no tiene por qué estar abierto, antes de que haga falta. En este tema lo haces con SSH, la puerta principal de cualquier servidor Linux.
Lo que de verdad usa sshd
La configuración de sshd está en /etc/ssh/sshd_config. Casi todo viene comentado (con su valor de serie), y arriba del todo hay una línea que importa mucho:
tecnico@srv-clinica:~$ sudo grep -nE "^(Include|PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config 12:Include /etc/ssh/sshd_config.d/*.conf 33:PermitRootLogin yes
El Include mete ahí, en la línea 12, lo que haya en /etc/ssh/sshd_config.d/. Y en sshd gana
el primer valor: si una opción sale dos veces, vale la que se lee antes. Lo de los drop-ins se lee
antes que casi todo el archivo principal, así que manda sobre él.
Para no tener que hacer esas cuentas, sshd -T dice la configuración que usaría, ya resuelta:
tecnico@srv-clinica:~$ sudo sshd -T | grep -iE "^(permitrootlogin|passwordauthentication)" permitrootlogin yes passwordauthentication yes
El archivo principal no dice nada de contraseñas (la línea está comentada: vale lo de serie, que es
yes). Pero aunque la pusieras a no ahí, seguiría en yes. El culpable está en los drop-ins:
tecnico@srv-clinica:~$ ls -l /etc/ssh/sshd_config.d total 4 -rw------- 1 root root 27 Sep 2 10:00 50-cloud-init.conf
tecnico@srv-clinica:~$ sudo cat /etc/ssh/sshd_config.d/50-cloud-init.conf PasswordAuthentication yes
Ejercicio
Lo que de verdad usa sshd: sshd -T y los drop-ins
srv-clinica abre a las 8:00 y el informe dice que deja entrar con contraseña. Antes de tocar nada, mira qué configuración usa sshd de verdad y de dónde sale.
Preparando la máquina…
La clave, antes que la puerta
Si quitas las contraseñas sin tener otra forma de entrar, te quedas fuera tú también. Lo primero es
que el SOC pueda entrar con su clave: un par de claves, la privada en el SOC y la pública en el
servidor, en el authorized_keys de la cuenta con la que se entra. El SOC te ha dejado la suya en
~/atalaya.pub:
tecnico@srv-clinica:~$ cat ~/atalaya.pub ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBBtsWJTmPly6Hz/dPJgOz7gZM7vPZNBycHiMkAk1J/h soc@atalaya
tecnico@srv-clinica:~$ mkdir -p ~/.ssh
tecnico@srv-clinica:~$ cat ~/atalaya.pub >> ~/.ssh/authorized_keys
tecnico@srv-clinica:~$ ls -la ~/.ssh total 12 drwxrwxr-x 2 tecnico tecnico 4096 Oct 13 06:00 . drwxr-x--- 3 tecnico tecnico 4096 Oct 13 06:00 .. -rw-rw-r-- 1 tecnico tecnico 93 Oct 13 06:00 authorized_keys
Así no vale. sshd es exigente con los permisos (su opción StrictModes): si la carpeta o el
archivo los puede escribir alguien más que su dueño (esa w del grupo), no se fía de ellos y apunta
en auth.log Authentication refused: bad ownership or modes for directory /home/tecnico/.ssh. La
carpeta, solo para ti; el archivo, sin escritura para nadie más:
tecnico@srv-clinica:~$ chmod 700 ~/.ssh
tecnico@srv-clinica:~$ chmod 600 ~/.ssh/authorized_keys
tecnico@srv-clinica:~$ ls -la ~/.ssh total 12 drwx------ 2 tecnico tecnico 4096 Oct 13 06:00 . drwxr-x--- 3 tecnico tecnico 4096 Oct 13 06:00 .. -rw------- 1 tecnico tecnico 93 Oct 13 06:00 authorized_keys
Para comprobar que es la clave que toca, su huella: la compara el SOC con la que tiene en su ficha.
tecnico@srv-clinica:~$ ssh-keygen -lf ~/.ssh/authorized_keys 256 SHA256:cmgqZsHYzubOGaBt+PKEpR5iE7wtxRiKRl1BDn1VShs soc@atalaya (ED25519)
Cuando el SOC vuelve a entrar, auth.log dice con qué: Accepted publickey for tecnico es con su
clave; Accepted password for tecnico, todavía con contraseña.
Otra clave para la misma cuenta se añade al final, sin pisar las que ya hay
(cat otra.pub | sudo tee -a /home/<cuenta>/.ssh/authorized_keys, nunca con > ni con cp). Y a
quien ya no debe entrar no basta con bloquearle la contraseña (usermod -L), porque su clave sigue
valiendo: se le quita del authorized_keys o se caduca la cuenta (sudo usermod -L -e 1 <cuenta>).
Ejercicio
La clave del SOC, antes que nada: authorized_keys y sus permisos
Antes de quitar las contraseñas, el SOC tiene que poder entrar con su clave. La tienes en ~/atalaya.pub: ponla en tu authorized_keys, con los permisos que pide sshd.
Preparando la máquina…
Sin contraseñas y sin root
Con la clave puesta, ya se pueden cerrar las dos puertas: PasswordAuthentication no (nadie entra
con contraseña) y PermitRootLogin no (root no entra por SSH; quien administra entra con su cuenta
y usa sudo). Se pueden cambiar con nano o con sed:
tecnico@srv-clinica:~$ sudo sed -i -E 's/^#?PasswordAuthentication .*/PasswordAuthentication no/; s/^#?PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config
Antes de que sshd la lea, se comprueba. sshd -t no dice nada si está bien; si hay una errata
(aquí, una línea añadida al final con PasswordAuthentification, con letras de más), dice dónde:
tecnico@srv-clinica:~$ sudo sshd -t /etc/ssh/sshd_config: line 123: Bad configuration option: PasswordAuthentification /etc/ssh/sshd_config: terminating, 1 bad configuration options
Arreglada la línea 123, vuelve a callar. Y sshd -T dice lo que pasaría:
tecnico@srv-clinica:~$ sudo sshd -T | grep -iE "^(permitrootlogin|passwordauthentication)" permitrootlogin no passwordauthentication yes
Las contraseñas, aún en yes: es el drop-in. Se cambia también (o se borra):
tecnico@srv-clinica:~$ sudo sed -i 's/yes/no/' /etc/ssh/sshd_config.d/50-cloud-init.conf
tecnico@srv-clinica:~$ sudo sshd -T | grep -iE "^(permitrootlogin|passwordauthentication)" permitrootlogin no passwordauthentication no
Hasta aquí, sshd sigue con lo de antes: lo que está escrito no vale hasta que lo vuelva a leer. Se le pide con reload:
tecnico@srv-clinica:~$ sudo systemctl reload ssh
tecnico@srv-clinica:~$ sudo journalctl -u ssh -n 5 --no-pager Oct 13 06:00:21 srv-clinica systemd[1]: Reloading OpenBSD Secure Shell server... Oct 13 06:00:21 srv-clinica sshd[141]: Received SIGHUP; restarting. Oct 13 06:00:21 srv-clinica systemd[1]: Reloaded OpenBSD Secure Shell server. Oct 13 06:00:21 srv-clinica sshd[141]: Server listening on 0.0.0.0 port 22. Oct 13 06:00:21 srv-clinica sshd[141]: Server listening on :: port 22.
Desde ahora, quien intente entrar con contraseña recibe Permission denied (publickey).: la única
puerta que queda es la de las claves. La contraseña de root sigue valiendo en la consola; si no
debe valer en ningún sitio, sudo passwd -l root la bloquea (sudo sigue funcionando).
Ejercicio
Sin contraseñas y sin root: sshd_config, sshd -t y reload
Con la clave del SOC ya puesta, que nadie entre por SSH con contraseña y que root no entre. Cuidado con el drop-in, y comprueba antes de recargar.
Preparando la máquina…
Frenar las ráfagas: ufw limit
El SSH sigue abierto a Internet (el proveedor de la agenda entra desde fuera, con su clave). Sin contraseñas ya no hay nada que adivinar, pero los barridos siguen llenando los registros. ufw limit los frena: deja pasar las conexiones, pero corta a la IP que abra 6 en 30 segundos.
tecnico@srv-clinica:~$ sudo ufw status numbered Status: active To Action From -- ------ ---- [ 1] OpenSSH ALLOW IN Anywhere [ 2] 443/tcp ALLOW IN Anywhere [ 3] 8080/tcp ALLOW IN Anywhere [ 4] OpenSSH (v6) ALLOW IN Anywhere (v6) [ 5] 443/tcp (v6) ALLOW IN Anywhere (v6) [ 6] 8080/tcp (v6) ALLOW IN Anywhere (v6)
tecnico@srv-clinica:~$ sudo ufw limit OpenSSH Rule updated Rule updated (v6)
tecnico@srv-clinica:~$ sudo ufw status numbered Status: active To Action From -- ------ ---- [ 1] OpenSSH LIMIT IN Anywhere [ 2] 443/tcp ALLOW IN Anywhere [ 3] 8080/tcp ALLOW IN Anywhere [ 4] OpenSSH (v6) LIMIT IN Anywhere (v6) [ 5] 443/tcp (v6) ALLOW IN Anywhere (v6) [ 6] 8080/tcp (v6) ALLOW IN Anywhere (v6)
La regla no se ha añadido: se ha cambiado (de ALLOW a LIMIT), en su sitio.
Ejercicio
Frenar las ráfagas: ufw limit
El SSH tiene que seguir abierto (el proveedor de la agenda entra desde fuera), pero sin dejar probar contraseñas a ráfagas. ufw lo frena: limit corta a quien abra 6 conexiones en 30 segundos.
Preparando la máquina…
Lo que sobra y lo que falta
Cada servicio escuchando es una puerta más. ss -tlnp dice qué escucha y qué programa es:
tecnico@srv-clinica:~$ sudo ss -tlnp State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 2048 0.0.0.0:8080 0.0.0.0:* users:(("agenda-pruebas",pid=401,fd=5)) LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=102,fd=4)) LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=141,fd=3)) LISTEN 0 2048 0.0.0.0:443 0.0.0.0:* users:(("agenda",pid=400,fd=5)) LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=141,fd=4))
La agenda de citas (agenda, en el 443) es lo que la clínica usa. La del 8080 es la copia de
pruebas que dejó el instalador: sobra. Se para y se deshabilita a la vez, para que no vuelva al
reiniciar:
tecnico@srv-clinica:~$ sudo systemctl disable --now agenda-pruebas Removed /etc/systemd/system/multi-user.target.wants/agenda-pruebas.service.
Y lo que falta, las actualizaciones. apt update dice si hay algo esperando, y apt list --upgradable, qué:
tecnico@srv-clinica:~$ sudo apt update Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease Get:2 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [128 kB] Hit:3 http://archive.ubuntu.com/ubuntu jammy-backports InRelease Get:4 http://security.ubuntu.com/ubuntu jammy-security InRelease [129 kB] Fetched 257 kB in 1s (214 kB/s) Reading package lists... Done Building dependency tree... Done Reading state information... Done 2 packages can be upgraded. Run 'apt list --upgradable' to see them.
tecnico@srv-clinica:~$ apt list --upgradable Listing... Done libssl3/jammy-updates,jammy-security 3.0.2-0ubuntu1.30 amd64 [upgradable from: 3.0.2-0ubuntu1.29] openssl/jammy-updates,jammy-security 3.0.2-0ubuntu1.30 amd64 [upgradable from: 3.0.2-0ubuntu1.29]
Las dos vienen de jammy-security: son arreglos de seguridad (de OpenSSL, la biblioteca que cifra
casi todo). sudo apt upgrade las pone. En la sala, eso son 20 minutos de reloj: piénsalo antes.
Ejercicio
Lo que sobra y lo que falta: ss, systemctl disable --now y apt upgrade
El informe dice que hay algo escuchando que no debería y paquetes de seguridad sin poner. Apaga lo que sobra sin tocar la agenda de citas, y actualiza.
Preparando la máquina…
En la sala: antes de abrir
En la sala vuelves al SOC, a las seis de la mañana, con el informe de exposición de la clínica en la
pared. Mientras el servidor siga expuesto, las alertas suben de tono; a las ocho abre la clínica.
Cada orden gasta minutos, como en el tema 1, y el orden importa: la clave del SOC antes de quitar las
contraseñas, sshd -t antes del reload, y nada que tire la agenda de citas o deje fuera al proveedor.